Вы действительно понимаете Обещание?

внешний интерфейс JavaScript

Статья впервые опубликована авторомGithub.

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

Предварительное знание

Перед началом текста некоторые из контента мы первой статьи охватывают аванс, установите тон.

Какие API-интерфейсы Promise включают микрозадачи?

В Promise в микрозадачах участвуют только обратные вызовы, которые необходимо выполнить после изменения состояния, напримерthen,catch,finally, все остальные исполнения кода являются макрозадачами (синхронное выполнение).

На приведенном выше рисунке синим цветом обозначено синхронное выполнение, а желтым — асинхронное выполнение (переброшенное в очередь микрозадач).

Когда эти микрозадачи добавляются в очередь микрозадач?

Давайте посмотрим на эту проблему в соответствии со спецификацией ecma:

  • Если в это время состояние Promise находится в состоянии ожидания, то обратный вызов успеха или неудачи будет добавлен к[[PromiseFulfillReactions]]а также[[PromiseRejectReactions]]середина. Если вы посмотрите на код написанных от руки обещаний, то увидите, что есть два массива, в которых хранятся эти функции обратного вызова.

  • Если в это время состояние Promise не находится в состоянии ожидания, обратный вызов станет Promise Jobs, то есть микрозадачами.

После осмысления вышеуказанных знаний начинается художественный фильм.

То же самое, разные микрозадачи выполняются

начальный

Promise.resolve()
  .then(() => {
    console.log("then1");
    Promise.resolve().then(() => {
      console.log("then1-1");
    });
  })
  .then(() => {
    console.log("then2");
  });

Приведенный выше код должен иметь возможность получить правильный ответ:then1 → then1-1 → then2.

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

Исходя из этого, можно сделать первый вывод:В цепочке вызовов только предыдущийthenПосле выполнения обратного вызоваthenбудут добавлены в очередь микрозадач.

средний

все знаютPromise resolveс последующимthenОбратный вызов немедленно попадет в очередь микрозадач.

Как вы думаете, каким будет вывод следующего кода?

let p = Promise.resolve();

p.then(() => {
  console.log("then1");
  Promise.resolve().then(() => {
    console.log("then1-1");
  });
}).then(() => {
  console.log("then1-2");
});

p.then(() => {
  console.log("then2");
}); 

По первоначальному пониманию нам не сложно нарисоватьthen2Будет вthen1-1После выхода, но на самом деле ситуация обратная.

На основании этого делаем второй вывод:Начало каждого цепного вызова сначала будет по очереди входить в очередь микрозадач.

Дальше пишем иначе:

let p = Promise.resolve().then(() => {
  console.log("then1");
  Promise.resolve().then(() => {
    console.log("then1-1");
  });
}).then(() => {
  console.log("then2");
});

p.then(() => {
  console.log("then3");
});

Приведенный выше код на самом деле имеет ловушку,thenКаждый раз возвращается новое обещание, в это времяpбольше никогдаPromise.resolve()создан, но последнийthenгенерируется, поэтомуthen3должен быть вthen2распечатал позже.

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

передовой

Вы можете предположить следующееthen1-2Когда он будет напечатан?

Promise.resolve()
  .then(() => {
    console.log("then1");
    Promise.resolve()
      .then(() => {
        console.log("then1-1");
        return 1;
      })
      .then(() => {
        console.log("then1-2");
      });
  })
  .then(() => {
    console.log("then2");
  })
  .then(() => {
    console.log("then3");
  })
  .then(() => {
    console.log("then4");
  });

Этот вопрос должен быть простым, запомните первый вывод, чтобы получить ответ, следующий анализ:

  • первый разresolveпосле первогоthenОбратный вызов входит в очередь микрозадач и выполняется, печатаяthen1

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

  • Выполнять микрозадачи, печататьthen1-1а такжеthen2, а затем соответственно послеthenОбратный вызов вставляет очередь микрозадач

  • Выполнять микрозадачи, печататьthen1-2а такжеthen3, содержание после этого не будет объясняться по одному

Далее мы ставимreturn 1Измените его, и результат будет совсем другим:

Promise.resolve()
  .then(() => {
    console.log("then1");
    Promise.resolve()
      .then(() => {
        console.log("then1-1");
        return Promise.resolve();
      })
      .then(() => {
        console.log("then1-2");
      });
  })
  .then(() => {
    console.log("then2");
  })
  .then(() => {
    console.log("then3");
  })
  .then(() => {
    console.log("then4");
  });

когда мыreturn Promise.resolve()когда, угадай чтоthen1-2Когда он будет напечатан?

Ответ заключается в том, что печатается последний.

почему вthenСредняяreturnРазное, порядок выполнения микрозадач имеет такое большое изменение? Далее следует авторский анализ.

PS:then** Возвращает новое обещание и будет использовать это обещание дляresolveВозвращаемое значение, эта концепция должна быть понята в первую очередь. **

Согласно спецификации Promise A+

Согласно спецификации2.3.2,еслиresolveК нему нужно добавить обещаниеthenа такжеresolve.

if (x instanceof MyPromise) {
  if (x.currentState === PENDING) {
  } else {
    x.then(resolve, reject);
  }
  return;
}

Приведенный выше код взят из написанной от руки реализации Promise.

Тогда согласно спецификации A+, если мы находимся вthenвернулся вPromise.resolveЕсли да, то в очередь будет добавлена ​​еще одна микрозадача, но этот вывод пока не соответствует действительности, поэтому нужно искать другие авторитетные документы.

Согласно спецификации ECMA-262

Согласно спецификации25.6.1.3.2,когдаPromise resolveКогда обещание сделано, будет создан NewPromiseResolveThenableJob, который является типом Promise Jobs, то есть микрозадач.

This Job uses the supplied thenable and its then method to resolve the given promise. This process must take place as a Job to ensure that the evaluation of the then method occurs after evaluation of any surrounding code has completed.

и задания также будут вызываться один разthenфункционировать, чтобыresolve Promise, который также генерирует еще одну микрозадачу.

Это источник того, почему микрозадача запускается дважды.

наконец

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