Рубрики
Без рубрики

Как JavaScript обещает на самом деле работать изнутри

Одним из важнейших вопросов, которые я столкнулся с интервью, заключался в том, какие обещания будут реализованы. Поскольку async / await становится все более популярным, нужно понимать обещания. Что обещается? Обещание – это объект, представляющий результат асинхронной операции, которая либо решается, либо отклонена

Одним из важнейших вопросов, которые я столкнулся с интервью, заключался в том, какие обещания будут реализованы. Поскольку 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: '))
  1. Обещание Конструктор называется и экземпляр создан с помощью Новое обещание
  2. Исполнитель Функция передается Доререва (Исполнитель, это) и обратный вызов, где мы определили Setimeate будет называться trycalltwo (Исполнитель, resolveCallback, rejectcallback) Так что займет 3 секунды, чтобы закончить
  3. Мы звоним .then () над примером обещания, поэтому перед нашим Тайм-аут завершен или любой async API Возвращает, Обещание. Прототип. Затем будет называться как . что (CB, NULL)
  4. .then Создает новый Обещание и передает его аргументом для Новый обработчик (Onfulfulled, Onrejected, обещание)
  5. ручка Функция называется с оригиналом Обещание экземпляр и обработчик Экземпляр мы создали в пункте 4.
  6. Внутри ручка Функция, текущий self._state. и Self._deferredstate Итак, self_deferredstate станет 1 и обработчик Экземпляр будет назначен на Self.Defreds После этого контроль оттуда вернется
  7. После .then () Мы звоним .ловить () который внутренне позвонит .then (null, errorcallback) – Опять же одинаковые шаги повторяются из Точка 4 до точки 6 и пропустить точку 7 Так как мы позвонили .ловить однажды
  8. Текущий Обещание Государство это в ожидании И это будет ждать, пока он не будет решен или не отклонен. Так в этом примере, через 3 секунды, Setimeate Callback называется, и мы разрешаем это явно, что позвонит разрешать (значение) .
  9. ResolveCallback будет называться со значением Время вышло 🙂 И это позвонит главным решить Функция, которая проверит Если Значение && Значение && Значение
  10. Это не удастся в нашем случае, так как мы прошли Строка и Self._state станет 1 с Self._Value а позже Финал (я) называется.
  11. Финал позвоню Ручка (я, самоуверенность) один раз, потому что Self._deferredState и для цепочки обещаний, это позвонит ручка () для каждого отложить функция.
  12. В ручка Функция, так как Обещание Решено уже, это позвонит HandlerEsed (я, отсроченный)
  13. 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/”