Одним из важнейших вопросов, которые я столкнулся с интервью, заключался в том, какие обещания будут реализованы. Поскольку async/await становится все более популярным, нужно понимать обещания.
Что такое обещание?
Обещание – это объект, представляющий результат асинхронной операции, которая либо решается, либо отклонена (по причине).
Есть 3 государства
- Выполнено:
Onfulfulled ()будет называться (например,разрешение ()назывался) - Отклонено:
Onrejected ()будет называться (например,lets ()был вызван) - В ожидании: еще не выполнено или отклонено
Итак, давайте посмотрим, как это реализовано:
https://github.com/then/promise/blob/master/src/core.js
Согласно определению в Mozilla : Требуется Исполнитель Функция как аргумент.
function noop() {}
function Promise(executor) {
if (typeof this !== 'object') {
throw new TypeError('Promises must be constructed via new');
}
if (typeof executor !== 'function') {
throw new TypeError('Promise constructor\'s argument is not a function');
}
this._deferredState = 0;
this._state = 0;
this._value = null;
this._deferreds = null;
if (executor === noop) return;
doResolve(executor, this);
}Похоже, простой функция с некоторыми свойствами инициализированными к 0 или нулевой . Вот несколько вещей, чтобы заметить:
Это ._state Свойство может иметь три возможных значения, как описано выше:
0 - pending 1 - fulfilled with _value 2 - rejected with _value 3 - adopted the state of another promise, _value
Его значение – 0 ( ожидается) Когда вы создаете новый обещать.
Позже Доререва (Исполнитель, это) вызывается с Исполнитель и обещание объект.
Давайте перейдем к определению Дререва и посмотрите, как это реализовано.
/**
* Take a potentially misbehaving resolver function and make sure
* onFulfilled and onRejected are only called once.
*
* Makes no guarantees about asynchrony.
*/
function doResolve(fn, promise) {
var done = false;
var resolveCallback = function(value) {
if (done) return;
done = true;
resolve(promise, value);
};
var rejectCallback = function(reason) {
if (done) return;
done = true;
reject(promise, reason);
};
var res = tryCallTwo(fn, resolveCallback, rejectCallback);
if (!done && res === IS_ERROR) {
done = true;
reject(promise, LAST_ERROR);
}
}Здесь снова звонит trycalltwo Функция с исполнителем и 2 обратными вызовами. Обратные вызовы снова звонят решить и отклонять
сделано Переменная используется здесь, чтобы убедиться, что обещание разрешено или отклонено только один раз, поэтому, если вы попытаетесь отклонить или разрешить обещание более одного раза, то он вернется, потому что сделано Отказ
function tryCallTwo(fn, a, b) {
try {
fn(a, b);
} catch (ex) {
LAST_ERROR = ex;
return IS_ERROR;
}
}Эта функция косвенно называет главную Исполнитель Обратный вызов с 2 аргументами. Эти аргументы содержат логику о том, как решить или Отклонить должны быть вызваны. Вы можете проверить ResolveCallback и rejectcallback в Дререва функция выше Отказ
Если во время выполнения есть ошибка, она будет хранить ошибку в Last_Error и вернуть ошибку.
Прежде чем мы прыгаем в решить Определение функции, давайте проверим .then Функция первой:
Promise.prototype.then = function(onFulfilled, onRejected) {
if (this.constructor !== Promise) {
return safeThen(this, onFulfilled, onRejected);
}
var res = new Promise(noop);
handle(this, new Handler(onFulfilled, onRejected, res));
return res;
};
function Handler(onFulfilled, onRejected, promise) {
this.onFulfilled = typeof onFulfilled === "function" ? onFulfilled : null;
this.onRejected = typeof onRejected === "function" ? onRejected : null;
this.promise = promise;
}Так что в вышеуказанной функции, то создает новый Обещание и назначить его как свойство новой функции, называемой Обработчик Отказ Обработчик Функция имеет аргументы Onfulfulled а также Onrejected. Позже это будет использовать это обещание разрешить или отклонить со значением/причиной.
Как видите, .then Функция снова вызывает другую функцию:
handle(this, new Handler(onFulfilled, onRejected, res));
Реализация:
function handle(self, deferred) {
while (self._state === 3) {
self = self._value;
}
if (Promise._onHandle) {
Promise._onHandle(self);
}
if (self._state === 0) {
if (self._deferredState === 0) {
self._deferredState = 1;
self._deferreds = deferred;
return;
}
if (self._deferredState === 1) {
self._deferredState = 2;
self._deferreds = [self._deferreds, deferred];
return;
}
self._deferreds.push(deferred);
return;
}
handleResolved(self, deferred);
}- Есть a пока цикл, который будет продолжать присвоение объекта разрешенного обещания к текущему обещанию, которое также является обещанием
_штат - Если
_state (ожидается)и Государство обещало было отложено до того, как другое вложенное обещание не решено, его обратный вызов хранится вSelf._defreds.
function handleResolved(self, deferred) {
asap(function() { // asap is external lib used to execute cb immediately
var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
if (cb === null) {
if (self._state === 1) {
resolve(deferred.promise, self._value);
} else {
reject(deferred.promise, self._value);
}
return;
}
var ret = tryCallOne(cb, self._value);
if (ret === IS_ERROR) {
reject(deferred.promise, LAST_ERROR);
} else {
resolve(deferred.promise, ret);
}
});
}Что творится:
- Если государство составляет 1
(выполнено)Затем позвоните в решить еще отклонять - Если
OnfulfulledилиOnrejectedэтонулевойили если мы использовали пустую.then ()разрешено или Отклонить будет называться соответственно - Если
CBне пусто, то это называется другой функциейTrycallone (CB, Self._Value)
function tryCallOne(fn, a) {
try {
return fn(a);
} catch (ex) {
LAST_ERROR = ex;
return IS_ERROR;
}
} a) {Trycallone : Эта функция вызывает только обратный вызов, который передается в аргумент Self._Value. . Если нет ошибки, это будет разрешено обещание, в противном случае он откроет его.
Каждое обещание должно снабжать .тогда () Способ со следующей подписью:
promise.then( onFulfilled?: Function, onRejected?: Function ) => Promise
- Оба
Onfulfulled ()иOnrejected ()необязательны. - Если прилагаемые аргументы не являются функциями, они должны быть проигнорированы.
Onfulfulled ()Будут вызвать после выполнения обещания, с ценностью обещания как первый аргумент.Onrejected ()Будут вызвать после того, как обещание отклонено, по причине подавления в качестве первого аргумента.- Ни один
Onfulfulled ()ниOnrejected ()можно назвать более чем один раз. .then ()Может быть вызван много раз на том же обещании. Другими словами, обещание может использоваться для совокупных обратных вызовов..тогда ()должен вернуть новое обещание.
Обещание цепочки
.then должен вернуть обещание. Вот почему мы можем создать цепочку обещаний, как это:
Promise .then(() => Promise.then(() => Promise.then(result => result) )).catch(err)
Разрешение обещания
Давайте посмотрим решить Определение функции, которое мы уехали ранее, прежде чем перейти к .тогда () :
function resolve(self, newValue) {
// Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
if (newValue === self) {
return reject(
self,
new TypeError("A promise cannot be resolved with itself.")
);
}
if (
newValue &&
(typeof newValue === "object" || typeof newValue === "function")
) {
var then = getThen(newValue);
if (then === IS_ERROR) {
return reject(self, LAST_ERROR);
}
if (then === self.then && newValue instanceof Promise) {
self._state = 3;
self._value = newValue;
finale(self);
return;
} else if (typeof then === "function") {
doResolve(then.bind(newValue), self);
return;
}
}
self._state = 1;
self._value = newValue;
finale(self);
}- Мы проверяем, является ли результатом обещанием или нет. Если это функция, то вызовите эту функцию с помощью значения, используя
Doresolve ()Отказ - Если результат – это обещание, то он будет толкаться на
отложенныемножество. Вы можете найти эту логику вФиналфункция.
Отклонение обещания:
Promise.prototype['catch'] = function (onRejected) {
return this.then(null, onRejected);
};Вышеуказанная функция можно найти в ./es6-extensions.js Отказ
Всякий раз, когда мы отвергаем обещание, .catch Callback называется сахарным пальто для тогда (нуль, нанесенный) Отказ
Вот основная необработанная диаграмма, которую я создал, которая является птицей-глазным видом на то, что происходит внутри:
Давайте еще раз посмотрим, как все работает:
Например, у нас есть это обещание:
new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Time is out");
}, 3000)
})
.then(console.log.bind(null, 'Promise is fulfilled'))
.catch(console.error.bind(null, 'Something bad happened: '))- Обещание
Конструкторназывается и экземпляр создан с помощьюНовое обещание ИсполнительФункция передаетсяДоререва (Исполнитель, это)и обратный вызов, где мы определилиSetimeateбудет называтьсяtrycalltwo (Исполнитель, resolveCallback, rejectcallback)Так что займет 3 секунды, чтобы закончить- Мы звоним
.then ()над примером обещания, поэтому перед нашимТайм-аутзавершен или любой asyncAPIВозвращает,Обещание. Прототип. Затембудет называться как. что (CB, NULL) .thenСоздает новыйОбещаниеи передает его аргументом дляНовый обработчик (Onfulfulled, Onrejected, обещание)ручкаФункция называется с оригиналомОбещаниеэкземпляр иобработчикЭкземпляр мы создали в пункте 4.- Внутри
ручкаФункция, текущийself._state.иSelf._deferredstateИтак,self_deferredstateстанет1иобработчикЭкземпляр будет назначен наSelf.DefredsПосле этого контроль оттуда вернется - После
.then ()Мы звоним.ловить ()который внутренне позвонит.then (null, errorcallback)– Опять же одинаковые шаги повторяются из Точка 4 до точки 6 и пропустить точку 7 Так как мы позвонили.ловитьоднажды - Текущий
ОбещаниеГосударство это в ожидании И это будет ждать, пока он не будет решен или не отклонен. Так в этом примере, через 3 секунды,SetimeateCallback называется, и мы разрешаем это явно, что позвонитразрешать (значение). ResolveCallbackбудет называться со значениемВремя вышло🙂 И это позвонит главнымрешитьФункция, которая проверит ЕслиЗначение && Значение && Значение- Это не удастся в нашем случае, так как мы прошли
СтрокаиSelf._stateстанет1сSelf._Valueа позжеФинал (я)называется. ФиналпозвонюРучка (я, самоуверенность)один раз, потому чтоSelf._deferredStateи для цепочки обещаний, это позвонитручка ()для каждогоотложитьфункция.- В
ручкаФункция, так какОбещаниеРешено уже, это позвонитHandlerEsed (я, отсроченный) HandlerSeededФункция проверит, если_stateи назначитьCB.onfulfulledкоторый нашатогдаПерезвоните. ПозжеTrycallone (CB, Self._Value)позвонит этот обратный вызов, и мы получаем окончательный результат. Выполняя это, если какая-либо ошибка произошла, тоОбещаниебудет отклонен.
Когда обещание отклонено
В этом случае все шаги останутся прежними – но в Точка 8 Мы называем Отклонить (причина) . Это косвенно позвонит rejectcallback Определяется в Doresolve () и Self._state станет 2 Отказ В Финал функция CB будет равен отложенный .onejected который будет называться позже Trycallone Отказ Вот как .catch Callback будет вызван.
Это все сейчас! Я надеюсь, что вам понравилось статью, и она помогает в вашем следующем интервью JavaScript.
Если вы столкнулись с любой проблемой, не стесняйтесь связаться или комментарий ниже. Я был бы счастлив помочь?
Не стесняйтесь хлопать, если вы считаете это стоящим чтением!
Первоначально опубликовано 101node.io 05 февраля 2019 года.
Оригинал: “https://www.freecodecamp.org/news/how-javascript-promises-actually-work-from-the-inside-out-76698bb7210b/”