Как реализована библиотека обещания / отсрочки?

Как реализована библиотека обещаний / отложений, такая как q? Я пытался прочитать исходный код, но мне было довольно сложно его понять, поэтому я подумал, что было бы здорово, если бы кто-нибудь мог объяснить мне на высоком уровне, какие методы используются для реализации обещаний в однопоточных средах JS. как Node и браузеры.


person Derek Chiang    schedule 18.07.2013    source источник
comment
возможный дубликат концепции - определение принципа работы обещания?   -  person Bergi    schedule 18.07.2013


Ответы (5)


Мне сложнее объяснить, чем показать пример, поэтому вот очень простая реализация того, что может быть defer / prom.

Отказ от ответственности: это не функциональная реализация, и некоторые части спецификации Promise / A отсутствуют. Это просто для объяснения основы обещаний.

tl; dr: Перейдите в раздел Создание классов и примеров, чтобы увидеть полную реализацию.

Обещать:

Сначала нам нужно создать объект обещания с массивом обратных вызовов. Начну работать с объектами, потому что понятнее:

var promise = {
  callbacks: []
}

теперь добавьте обратные вызовы с помощью метода, затем:

var promise = {
  callbacks: [],
  then: function (callback) {
    callbacks.push(callback);
  }
}

И нам тоже нужны обратные вызовы ошибок:

var promise = {
  okCallbacks: [],
  koCallbacks: [],
  then: function (okCallback, koCallback) {
    okCallbacks.push(okCallback);
    if (koCallback) {
      koCallbacks.push(koCallback);
    }
  }
}

Отложить:

Теперь создайте объект defer с обещанием:

var defer = {
  promise: promise
};

Отсрочка должна быть разрешена:

var defer = {
  promise: promise,
  resolve: function (data) {
    this.promise.okCallbacks.forEach(function(callback) {
      window.setTimeout(function () {
        callback(data)
      }, 0);
    });
  },
};

И нужно отклонить:

var defer = {
  promise: promise,
  resolve: function (data) {
    this.promise.okCallbacks.forEach(function(callback) {
      window.setTimeout(function () {
        callback(data)
      }, 0);
    });
  },

  reject: function (error) {
    this.promise.koCallbacks.forEach(function(callback) {
      window.setTimeout(function () {
        callback(error)
      }, 0);
    });
  }
};

Обратите внимание, что обратные вызовы вызываются по таймауту, чтобы код всегда был асинхронным.

И это то, что нужно для базовой реализации defer / обещания.

Создайте классы и пример:

Теперь давайте конвертируем оба объекта в классы, сначала обещание:

var Promise = function () {
  this.okCallbacks = [];
  this.koCallbacks = [];
};

Promise.prototype = {
  okCallbacks: null,
  koCallbacks: null,
  then: function (okCallback, koCallback) {
    okCallbacks.push(okCallback);
    if (koCallback) {
      koCallbacks.push(koCallback);
    }
  }
};

А теперь отсрочка:

var Defer = function () {
  this.promise = new Promise();
};

Defer.prototype = {
  promise: null,
  resolve: function (data) {
    this.promise.okCallbacks.forEach(function(callback) {
      window.setTimeout(function () {
        callback(data)
      }, 0);
    });
  },

  reject: function (error) {
    this.promise.koCallbacks.forEach(function(callback) {
      window.setTimeout(function () {
        callback(error)
      }, 0);
    });
  }
};

А вот пример использования:

function test() {
  var defer = new Defer();
  // an example of an async call
  serverCall(function (request) {
    if (request.status === 200) {
      defer.resolve(request.responseText);
    } else {
      defer.reject(new Error("Status code was " + request.status));
    }
  });
  return defer.promise;
}

test().then(function (text) {
  alert(text);
}, function (error) {
  alert(error.message);
});

Как видите, основные части простые и маленькие. Он будет расти, когда вы добавите другие параметры, например разрешение нескольких обещаний:

Defer.all(promiseA, promiseB, promiseC).then()

или обещание цепочки:

getUserById(id).then(getFilesByUser).then(deleteFile).then(promptResult);

Чтобы узнать больше о спецификациях: CommonJS Promise Specification. Обратите внимание, что основные библиотеки (Q, when.js, rsvp.js, node-prom, ...) следуют Promises / A спецификация.

Надеюсь, я был достаточно ясен.

Редактировать:

Как и просили в комментариях, я добавил в эту версию две вещи:

  • Возможность вызвать обещание, независимо от его статуса.
  • Возможность связывать обещания.

Чтобы иметь возможность вызвать обещание при разрешении, вам нужно добавить статус к обещанию, а когда вызывается then, проверьте этот статус. Если статус разрешен или отклонен, просто выполните обратный вызов с его данными или ошибкой.

Чтобы иметь возможность связывать обещания, вам необходимо сгенерировать новую задержку для каждого вызова then и, когда обещание будет разрешено / отклонено, разрешить / отклонить новое обещание с результатом обратного вызова. Итак, когда обещание выполнено, если обратный вызов возвращает новое обещание, он привязан к обещанию, возвращенному с then(). Если нет, обещание разрешается с результатом обратного вызова.

Вот обещание:

var Promise = function () {
  this.okCallbacks = [];
  this.koCallbacks = [];
};

Promise.prototype = {
  okCallbacks: null,
  koCallbacks: null,
  status: 'pending',
  error: null,

  then: function (okCallback, koCallback) {
    var defer = new Defer();

    // Add callbacks to the arrays with the defer binded to these callbacks
    this.okCallbacks.push({
      func: okCallback,
      defer: defer
    });

    if (koCallback) {
      this.koCallbacks.push({
        func: koCallback,
        defer: defer
      });
    }

    // Check if the promise is not pending. If not call the callback
    if (this.status === 'resolved') {
      this.executeCallback({
        func: okCallback,
        defer: defer
      }, this.data)
    } else if(this.status === 'rejected') {
      this.executeCallback({
        func: koCallback,
        defer: defer
      }, this.error)
    }

    return defer.promise;
  },

  executeCallback: function (callbackData, result) {
    window.setTimeout(function () {
      var res = callbackData.func(result);
      if (res instanceof Promise) {
        callbackData.defer.bind(res);
      } else {
        callbackData.defer.resolve(res);
      }
    }, 0);
  }
};

И отсрочка:

var Defer = function () {
  this.promise = new Promise();
};

Defer.prototype = {
  promise: null,
  resolve: function (data) {
    var promise = this.promise;
    promise.data = data;
    promise.status = 'resolved';
    promise.okCallbacks.forEach(function(callbackData) {
      promise.executeCallback(callbackData, data);
    });
  },

  reject: function (error) {
    var promise = this.promise;
    promise.error = error;
    promise.status = 'rejected';
    promise.koCallbacks.forEach(function(callbackData) {
      promise.executeCallback(callbackData, error);
    });
  },

  // Make this promise behave like another promise:
  // When the other promise is resolved/rejected this is also resolved/rejected
  // with the same data
  bind: function (promise) {
    var that = this;
    promise.then(function (res) {
      that.resolve(res);
    }, function (err) {
      that.reject(err);
    })
  }
};

Как видите, он немного вырос.

person Kaizo    schedule 18.07.2013
comment
наверное, было бы хорошо связать предложение CommonJS Promises / A ... для тех, кто понял и хочет углубиться в этот шаблон :) - person gustavohenke; 18.07.2013
comment
Спасибо. Добавил ссылку на все спецификации. - person Kaizo; 18.07.2013
comment
Ваш пример абсолютно не работает. Ни состояние обещания не устанавливается (ваше может быть выполнено и отклонено одновременно), ни добавление обратных вызовов после работы по разрешению, ни then не возвращает другое обещание (что очень важно). - person Bergi; 18.07.2013
comment
Это чрезвычайно простой пример, а не полностью функциональная реализация. Я знаю, что некоторые части спецификации не реализованы и что это может не работать, но это было только для объяснения основы обещаний. - person Kaizo; 18.07.2013
comment
Да, я это понимаю, и для хорошего ответа необходимо упрощение (мой ответ не является полностью совместимым). Тем не менее, я думаю, что в нем отсутствуют main части реализации обещания. Повторение одного и того же кода снова и снова делает ответ длиннее, а не лучше :-( - person Bergi; 18.07.2013
comment
@Bergi, помните вопрос: How is a promise/defer library like q implemented и it'd be great if someone could explain to me, from a high level, what are the techniques used to implement promises. Чтобы объяснить это, я показывал код постепенно, чтобы сосредоточиться на некоторых частях без потери контекста. Вы объясняете спецификацию и то, что должно быть, а не то, как она реализована, а затем вы показываете очень маленькую реализацию, которая не похожа на Q. И в частности, он просит promise/defer реализацию, а у вас нет отсрочки. - person Kaizo; 18.07.2013
comment
@Kaizo, спасибо за эту подробную статью! Чтобы прояснить: пример цепочки, который вы даете в конце, на самом деле не будет работать, потому что ваша функция then ничего не возвращает, верно? Если да, то можете ли вы также показать нам, что then должен фактически вернуть, чтобы включить цепочку? Мне не сразу стало ясно. - person Derek Chiang; 18.07.2013
comment
@DerekChiang, я добавил новую версию с опцией цепочки. - person Kaizo; 19.07.2013
comment
Кристально чистый ответ! - person Samson; 09.10.2013
comment
Каждый раз, когда вызываемый метод then возвращает новое обещание, а связанный метод then вызывает новое обещание, возвращается. Итак, как это возможно, что okCallbacks в классе Promise действительно используется? - person towry; 26.02.2015
comment
@towry Он используется, когда defer разрешен и вызывает метод executeCallback обещания. Он разрешает обещания, сгенерированные при вызове метода then. Если обратный вызов then возвращает обещание, оно связывается с тем, которое было возвращено при вызове метода then. В противном случае он разрешает обещание со значениями, возвращаемыми обратным вызовом. Надеюсь, это поможет вам понять эту часть. - person Kaizo; 09.04.2015
comment
setImmediate, вероятно, здесь более уместен, чем setTimeout - person Alexander Mills; 21.05.2015
comment
@AlexMills, вы правы, но согласно документации MDN Note: This method is not expected to become standard, and is only implemented by recent builds of Internet Explorer and Node.js 0.10+. It meets resistance both from Gecko (Firefox) and Webkit (Google/Apple). - person Kaizo; 21.05.2015
comment
w3.org/2001/tag/doc/promises-guide - person Shankar Shastri; 30.08.2017

Q - очень сложная библиотека обещаний с точки зрения реализации, поскольку она нацелена на поддержку конвейерной обработки и сценариев типов RPC. У меня есть собственная очень простая реализация спецификации Promises / A + здесь.

В принципе все довольно просто. Прежде чем обещание будет урегулировано / разрешено, вы сохраняете запись любых обратных вызовов или ошибок, помещая их в массив. Когда обещание выполнено, вы вызываете соответствующие обратные вызовы или ошибки и записываете, с каким результатом было согласовано обещание (и было ли оно выполнено или отклонено). После этого вы просто вызываете обратные вызовы или ошибки с сохраненным результатом.

Это дает вам примерно семантику done. Чтобы построить then, вам просто нужно вернуть новое обещание, которое разрешается в результате вызова обратных вызовов / ошибок.

Если вас интересует полное объяснение причин разработки полной реализации обещаний с поддержкой RPC и конвейерной обработки, такой как Q, вы можете прочитать аргументы Крискоула здесь. Это действительно хороший поэтапный подход, который я не могу рекомендовать достаточно, если вы думаете о выполнении обещаний. Вероятно, стоит прочитать, даже если вы просто собираетесь использовать библиотеку обещаний.

person ForbesLindesay    schedule 18.07.2013
comment
Ух ты, эта статья @KrisKowal потрясающая. Он должен опубликовать это как ответ, чтобы получить десятки голосов :-) - person Bergi; 19.07.2013
comment
Действительно, это великолепно, я планирую переформатировать ее как полноценную веб-страницу, чтобы в какой-то момент она была немного лучше отформатирована. - person ForbesLindesay; 19.07.2013
comment
+1 за статью KrisKowal. Отличное чтение. - person Derek Chiang; 20.07.2013
comment
ссылка вроде не работает ... - person T J; 09.12.2015

Как упоминает Forbes в своем ответе, я записал многие дизайнерские решения, связанные с созданием такой библиотеки, как Q, здесь https://github.com/kriskowal/q/tree/v1/design. Достаточно сказать, что есть уровни библиотеки обещаний и множество библиотек, которые останавливаются на разных уровнях.

На первом уровне, охваченном спецификацией Promises / A +, обещание является прокси для конечного результата и подходит для управления «локальной асинхронностью». То есть он подходит для обеспечения того, чтобы работа выполнялась в правильном порядке, а также для обеспечения того, чтобы было просто и понятно отслеживать результат операции, независимо от того, был ли он уже установлен или произойдет в будущем. Это также позволяет одной или нескольким сторонам подписаться на конечный результат.

Q, как я его реализовал, предоставляет обещания, которые являются прокси для возможных, удаленных или возможных + удаленных результатов. С этой целью его дизайн инвертирован, с различными реализациями обещаний - отложенные обещания, выполненные обещания, отклоненные обещания и обещания для удаленных объектов (последнее из них реализовано в Q-Connection). Все они используют один и тот же интерфейс и работают, отправляя и получая сообщения, такие как then (что достаточно для Promises / A +), но также получают и вызывают. Итак, Q относится к распределенной асинхронности и существует на другом уровне.

Однако на самом деле Q был удален с более высокого уровня, где обещания используются для управления распределенной асинхронностью между взаимно подозрительными сторонами, такими как вы, продавец, банк, Facebook, правительство - не врагами, может даже друзья, но иногда с конфликтом интересов. Реализованный мной Q разработан так, чтобы быть совместимым с API с усиленными обещаниями безопасности (что является причиной разделения promise и resolve), в надежде, что он познакомит людей с обещаниями, обучит их использованию этого API и позволит им принимать их код с ними, если им нужно использовать обещания в безопасных гибридных приложениях в будущем.

Конечно, по мере продвижения по слоям есть компромиссы, обычно в скорости. Таким образом, реализации обещаний также могут быть спроектированы для сосуществования. Вот тут-то и появляется понятие «полезного». Библиотеки обещаний на каждом уровне могут быть разработаны для использования обещаний с любого другого уровня, поэтому несколько реализаций могут сосуществовать, и пользователи могут покупать только то, что им нужно.

Все это говорит о том, что нет никаких оправданий тому, что его трудно читать. Мы с Домеником работаем над версией Q, которая будет более модульной и доступной, с некоторыми отвлекающими зависимостями и обходными путями, перенесенными в другие модули и пакеты. К счастью, такие люди, как Forbes, Crockford и другие заполнили образовательный пробел, сделав более простые библиотеки.

person Kris Kowal    schedule 09.09.2013
comment
ссылка вроде не работает ... - person T J; 09.12.2015

Сначала убедитесь, что вы понимаете, как должны работать обещания. Ознакомьтесь с предложениями CommonJs Promises и Promises / спецификация A + для этого.

Есть две основные концепции, каждая из которых может быть реализована в несколько простых строк:

  • Обещание выполняется асинхронно с результатом. Добавление обратных вызовов - это прозрачное действие - независимо от того, выполнено ли обещание или нет, они будут вызваны с результатом, как только он станет доступен.

    function Deferred() {
        var callbacks = [], // list of callbacks
            result; // the resolve arguments or undefined until they're available
        this.resolve = function() {
            if (result) return; // if already settled, abort
            result = arguments; // settle the result
            for (var c;c=callbacks.shift();) // execute stored callbacks
                c.apply(null, result);
        });
        // create Promise interface with a function to add callbacks:
        this.promise = new Promise(function add(c) {
            if (result) // when results are available
                c.apply(null, result); // call it immediately
            else
                callbacks.push(c); // put it on the list to be executed later
        });
    }
    // just an interface for inheritance
    function Promise(add) {
        this.addCallback = add;
    }
    
  • У обещаний есть then метод, который позволяет объединять их в цепочку. Я принимаю обратный вызов и возвращаю новое обещание, которое будет разрешено с результатом этого обратного вызова после того, как оно было вызвано с результатом первого обещания. Если обратный вызов возвращает обещание, оно будет ассимилировано, а не вложено.

    Promise.prototype.then = function(fn) {
        var dfd = new Deferred(); // create a new result Deferred
        this.addCallback(function() { // when `this` resolves…
            // execute the callback with the results
            var result = fn.apply(null, arguments);
            // check whether it returned a promise
            if (result instanceof Promise)
                result.addCallback(dfd.resolve); // then hook the resolution on it
            else
                dfd.resolve(result); // resolve the new promise immediately 
            });
        });
        // and return the new Promise
        return dfd.promise;
    };
    

Дальнейшие концепции будут поддерживать отдельное состояние error (с дополнительным обратным вызовом для него) и улавливать исключения в обработчиках или гарантировать асинхронность для обратных вызовов. Как только вы их добавите, у вас будет полностью функциональная реализация Promise.

Вот выписанная ошибка. К сожалению, это довольно часто повторяется; вы можете добиться большего, используя дополнительные замыкания, но тогда это становится действительно очень трудно понять.

function Deferred() {
    var callbacks = [], // list of callbacks
        errbacks = [], // list of errbacks
        value, // the fulfill arguments or undefined until they're available
        reason; // the error arguments or undefined until they're available
    this.fulfill = function() {
        if (reason || value) return false; // can't change state
        value = arguments; // settle the result
        for (var c;c=callbacks.shift();)
            c.apply(null, value);
        errbacks.length = 0; // clear stored errbacks
    });
    this.reject = function() {
        if (value || reason) return false; // can't change state
        reason = arguments; // settle the errror
        for (var c;c=errbacks.shift();)
            c.apply(null, reason);
        callbacks.length = 0; // clear stored callbacks
    });
    this.promise = new Promise(function add(c) {
        if (reason) return; // nothing to do
        if (value)
            c.apply(null, value);
        else
            callbacks.push(c);
    }, function add(c) {
        if (value) return; // nothing to do
        if (reason)
            c.apply(null, reason);
        else
            errbacks.push(c);
    });
}
function Promise(addC, addE) {
    this.addCallback = addC;
    this.addErrback = addE;
}
Promise.prototype.then = function(fn, err) {
    var dfd = new Deferred();
    this.addCallback(function() { // when `this` is fulfilled…
        try {
            var result = fn.apply(null, arguments);
            if (result instanceof Promise) {
                result.addCallback(dfd.fulfill);
                result.addErrback(dfd.reject);
            } else
                dfd.fulfill(result);
        } catch(e) { // when an exception was thrown
            dfd.reject(e);
        }
    });
    this.addErrback(err ? function() { // when `this` is rejected…
        try {
            var result = err.apply(null, arguments);
            if (result instanceof Promise) {
                result.addCallback(dfd.fulfill);
                result.addErrback(dfd.reject);
            } else
                dfd.fulfill(result);
        } catch(e) { // when an exception was re-thrown
            dfd.reject(e);
        }
    } : dfd.reject); // when no `err` handler is passed then just propagate
    return dfd.promise;
};
person Bergi    schedule 18.07.2013
comment
Не объясняя, как это реализовано, и не показываю реализацию отсрочки / обещания. - person Kaizo; 18.07.2013
comment
@Kaizo: Спасибо, я не понял, что OP конкретно спрашивает об отсрочке. Пояснение добавлено и переключено на отложенный интерфейс. Что-нибудь актуальное все еще отсутствует? - person Bergi; 18.07.2013
comment
Мне потребовалось некоторое время, чтобы понять, как это работает, хотя он довольно маленький и содержит комментарии. Ваша версия намного полнее моей и прелестной умницы, но и намного сложнее. Единственное, что мне не хватает, - это вариант отклонения и обратный вызов ошибки. - person Kaizo; 18.07.2013
comment
@Kaizo: Да, я намеренно пропустил это, потому что, если писать лаконично, код становится еще более сложным и менее понятным :-) Посмотрите мои правки ... - person Bergi; 18.07.2013
comment
В каком случае метод addCallback в классе Promise будет вызываться более одного раза? Метод then просто вернет новый экземпляр Promise, так зачем же хранить массив обратных вызовов в классе Deferred? - person towry; 26.02.2015
comment
@towry: then можно вызывать несколько раз для одного и того же обещания, ничто не мешает этому. Только потому, что многие цепочки обещаний линейны, это не означает, что ветвление не поддерживается. - person Bergi; 26.02.2015

Вы можете прочитать сообщение в блоге на Адехуне.

Adehun - чрезвычайно легкая реализация (около 166 LOC), очень полезная для изучения того, как реализовать спецификацию Promise / A +.

Заявление об ограничении ответственности: я написал сообщение в блоге, но сообщение в блоге объясняет все об Adehun.

Функция перехода - привратник для перехода между состояниями

Функция привратника; гарантирует, что переходы между состояниями происходят при соблюдении всех требуемых условий.

Если условия соблюдены, эта функция обновляет состояние и значение обещания. Затем он запускает функцию процесса для дальнейшей обработки.

Функция процесса выполняет правильное действие в зависимости от перехода (например, от ожидания к выполнению), что будет объяснено позже.

function transition (state, value) {
  if (this.state === state ||
    this.state !== validStates.PENDING ||
    !isValidState(state)) {
      return;
    }

  this.value = value;
  this.state = state;
  this.process();
}

Функция Then

Функция then принимает два необязательных аргумента (обработчики onFulfill и onReject) и должна возвращать новое обещание. Два основных требования:

  1. Базовое обещание (то, которое затем вызывается) должно создать новое обещание, используя переданные обработчики; база также хранит внутреннюю ссылку на это созданное обещание, чтобы его можно было вызвать после выполнения / отклонения базового обещания.

  2. Если базовое обещание установлено (то есть выполнено или отклонено), то соответствующий обработчик должен быть вызван немедленно. Adehun.js обрабатывает этот сценарий, вызывая процесс в функции then.

``

function then(onFulfilled, onRejected) {
    var queuedPromise = new Adehun();
    if (Utils.isFunction(onFulfilled)) {
        queuedPromise.handlers.fulfill = onFulfilled;
    }

    if (Utils.isFunction(onRejected)) {
        queuedPromise.handlers.reject = onRejected;
    }

    this.queue.push(queuedPromise);
    this.process();

    return queuedPromise;
}`

Функция обработки - обработка переходов

Это вызывается после переходов между состояниями или при вызове функции then. Таким образом, ему необходимо проверить ожидающие обещания, поскольку он мог быть вызван из функции then.

Процесс запускает процедуру разрешения обещаний для всех внутренних сохраненных обещаний (то есть тех, которые были прикреплены к базовому обещанию с помощью функции then) и обеспечивает выполнение следующих требований Promise / A +:

  1. Асинхронный вызов обработчиков с помощью помощника Utils.runAsync (тонкая оболочка вокруг setTimeout (setImmediate также будет работать)).

  2. Создание резервных обработчиков для обработчиков onSuccess и onReject, если они отсутствуют.

  3. Выбор правильной функции обработчика на основе состояния обещания, например. выполнено или отклонено.

  4. Применение обработчика к значению базового обещания. Значение этой операции передается в функцию Resolve для завершения цикла обработки обещания.

  5. При возникновении ошибки прикрепленное обещание немедленно отклоняется.

    function process () {var that = this, performFallBack = function (value) {возвращаемое значение; }, rejectFallBack = функция (причина) {бросить причину; };

    if (this.state === validStates.PENDING) {
        return;
    }
    
    Utils.runAsync(function() {
        while (that.queue.length) {
            var queuedP = that.queue.shift(),
                handler = null,
                value;
    
            if (that.state === validStates.FULFILLED) {
                handler = queuedP.handlers.fulfill ||
                    fulfillFallBack;
            }
            if (that.state === validStates.REJECTED) {
                handler = queuedP.handlers.reject ||
                    rejectFallBack;
            }
    
            try {
                value = handler(that.value);
            } catch (e) {
                queuedP.reject(e);
                continue;
            }
    
            Resolve(queuedP, value);
        }
    });
    

    }

Функция выполнения - выполнение обещаний

Это, вероятно, самая важная часть реализации обещания, поскольку она обрабатывает разрешение обещания. Он принимает два параметра - обещание и значение его разрешения.

Хотя существует множество проверок различных возможных значений разрешения; Есть два интересных сценария разрешения: те, которые включают в себя переданное обещание и thenable (объект со значением then).

  1. Передача значения обещания

Если значением разрешения является другое обещание, то обещание должно принять состояние этого значения разрешения. Поскольку это значение разрешения может быть отложенным или установленным, самый простой способ сделать это - присоединить новый обработчик then к значению разрешения и обработать в нем исходное обещание. Как только он уляжется, исходное обещание будет выполнено или отклонено.

  1. Передача полезного значения

Загвоздка здесь в том, что функция then для пригодного для использования значения должна быть вызвана только один раз (хорошее применение для оболочки Once из функционального программирования). Точно так же, если получение функции then вызывает исключение, обещание должно быть немедленно отклонено.

Как и раньше, функция then вызывается с функциями, которые в конечном итоге разрешают или отклоняют обещание, но разница здесь в флаге вызова, который устанавливается при первом вызове и делает последующие вызовы не операциями.

function Resolve(promise, x) {
  if (promise === x) {
    var msg = "Promise can't be value";
    promise.reject(new TypeError(msg));
  }
  else if (Utils.isPromise(x)) {
    if (x.state === validStates.PENDING){
      x.then(function (val) {
        Resolve(promise, val);
      }, function (reason) {
        promise.reject(reason);
      });
    } else {
      promise.transition(x.state, x.value);
    }
  }
  else if (Utils.isObject(x) ||
           Utils.isFunction(x)) {
    var called = false,
        thenHandler;

    try {
      thenHandler = x.then;

      if (Utils.isFunction(thenHandler)){
        thenHandler.call(x,
          function (y) {
            if (!called) {
              Resolve(promise, y);
              called = true;
            }
          }, function (r) {
            if (!called) {
              promise.reject(r);
              called = true;
            }
       });
     } else {
       promise.fulfill(x);
       called = true;
     }
   } catch (e) {
     if (!called) {
       promise.reject(e);
       called = true;
     }
   }
 }
 else {
   promise.fulfill(x);
 }
}

Конструктор обещаний

И это то, что объединяет все воедино. Функции выполнения и отклонения являются синтаксическим сахаром, передающим неработающие функции для разрешения и отклонения.

var Adehun = function (fn) {
 var that = this;

 this.value = null;
 this.state = validStates.PENDING;
 this.queue = [];
 this.handlers = {
   fulfill : null,
   reject : null
 };

 if (fn) {
   fn(function (value) {
     Resolve(that, value);
   }, function (reason) {
     that.reject(reason);
   });
 }
};

Я надеюсь, что это помогло пролить больше света на то, как работают обещания.

person AbdulFattah Popoola    schedule 05.03.2015
comment
Пожалуйста, опубликуйте в своем ответе содержание блога или хотя бы его суть. Ссылки не являются ответом. - person Bergi; 05.03.2015