Демистификация процесса регистрации и выполнения микрозадач Promise

Promise
  • Пять фрагментов кода для углубленного анализа микрозадачи регистрации Promise и процесса выполнения кода
  • Анализ различий в реализации между Promise/A+ и webkit (ядро Chrome и Safari) Promise
  • Консолидируйте это, выходите из вопроса

Промисы всем слишком знакомы, но здесь мы не говорим о поверхностных простых знаниях, которые всем известны, а давайте вместе разберем весь процесс микрозадач регистрации и выполнения промисов. Уметь правильно использовать промисы и знать почему~

Обычно мы изучаем промисы на основе Promises/A+ реализация. Но я должен сказать вам, что в этой статье также будет проанализирована разница между реализацией js и реализацией Promise в webkit. Конкретно для разницы в работе кода.

Общая идея этой статьи интерпретируется как пример кода + анализ и объяснение, чтобы каждый мог понять и понять цель.

Без преувеличения, если вы прочитаете всю эту статью, то процесс регистрации и исполнения Promise будет непобедимым, до глубины души,Ты Бог Обещаний! ~~~~~~~

предисловие

В этой статье есть способ интерпретации кода, чтобы изучить весь процесс. Вот пять фрагментов кода. Если вы можете полностью и правильно понять процесс вывода, то вы так же сильны, как и вы. Я дам вам большой палец вверх и поздравляю с пониманием процесса выполнения Promise.

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

Прежде чем смотреть на ответ, молча рассчитайте результат самостоятельно.

первый кусок кода

new Promise((resolve, reject) => {
  console.log("外部promise");
  resolve();
})
  .then(() => {
    console.log("外部第一个then");
    return new Promise((resolve, reject) => {
      console.log("内部promise");
      resolve();
    })
    .then(() => {
    console.log("内部第一个then");
    })
    .then(() => {
    console.log("内部第二个then");
    });
  })
  .then(() => {
    console.log("外部第二个then");
  });

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

output:
внешние обещания
Сначала внешний, потом
Внутренние обещания
сначала потом внутри
внутренняя секунда тогда
внешняя секунда тогда

Это второй кусок кода

new Promise((resolve, reject) => {
  console.log("外部promise");
  resolve();
})
  .then(() => {
    console.log("外部第一个then");
    new Promise((resolve, reject) => {
      console.log("内部promise");
      resolve();
    })
      .then(() => {
        console.log("内部第一个then");
      })
      .then(() => {
        console.log("内部第二个then");
      });
  })
  .then(() => {
    console.log("外部第二个then");
  });

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

Как вы это понимаете?

Наше ядро ​​состоит в том, чтобы увидеть, когда будет зарегистрирована функция обратного вызова then.Мы знаем, что механизм событий — «сначала зарегистрируйтесь и сначала выполните», то есть режим «очереди» в структуре данных, первый вошел, первый вышел. Итак, давайте посмотрим, кто зарегистрировался первым.

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

new Promise((resolve, reject) => {
    resolve();
    console.log(111);
})
.then(() => {
    consle.log(222);
})

Этот код, очевидно, сначала выводит выполнение 1111, а затем выполняет 222. Потому что вывод 222 — это выполнение микрозадач, а 111 — синхронное выполнение.

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

Однако внутренний второй then определяется завершением выполнения первого then, а callback первого then не выполняется, выполняется только регистрация синхронного метода .then, поэтому он войдет в состояние ожидания.

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

output:
внешние обещания
сначала внешний, потом
Внутренние обещания
сначала потом внутри
внешняя секунда тогда
внутренняя секунда тогда

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

Посмотрите на третий кусок кода

new Promise((resolve, reject) => {
  console.log("外部promise");
  resolve();
})
  .then(() => {
    console.log("外部第一个then");
    let p = new Promise((resolve, reject) => {
      console.log("内部promise");
      resolve();
    })
    p.then(() => {
        console.log("内部第一个then");
      })
    p.then(() => {
        console.log("内部第二个then");
      });
  })
  .then(() => {
    console.log("外部第二个then");
  });

Отличие этого кода в том, что внутренний код Promise написан по-другому, и это уже не цепочка вызовов.

Как тут понять?

Здесь, после завершения выполнения нового промиса внутри выполнения (обратное состояние промиса), две синхронизации p.then после нового промиса представляют собой два оператора кода выполнения, оба из которых выполняются синхронно, и, естественно, быть зарегистрированы синхронно.

Основные различия между двумя методами заключаются в следующем:

  • Регистрация цепного вызова зависит от передней и задней части Например, регистрация второго внешнего затем выше требует завершения выполнения первого внешнего затем.
  • Способ определения переменных, регистрация синхронная Например, p.then и var p = new Promise здесь выполняются синхронно.

Таким образом, выполнение кода здесь относительно ясно.После завершения внутреннего выполнения (поскольку оно имеет приоритет над регистрацией внешней секунды then ), затем выполняется внешняя секунда:

output:
внешние обещания
сначала внешний, потом
Внутренние обещания
сначала потом внутри
внутренняя секунда тогда
внешняя секунда тогда

Четвертый кусок кода

let p = new Promise((resolve, reject) => {
  console.log("外部promise");
  resolve();
})
p.then(() => {
    console.log("外部第一个then");
    new Promise((resolve, reject) => {
      console.log("内部promise");
      resolve();
    })
      .then(() => {
        console.log("内部第一个then");
      })
      .then(() => {
        console.log("内部第二个then");
      });
  })
p.then(() => {
    console.log("外部第二个then");
  });

В этом коде внешняя регистрация использует метод записи вызова без цепочки.Согласно приведенному выше объяснению, мы знаем, что p.then внешнего кода регистрируется параллельно и синхронно. Таким образом, код выполняется во внутреннем новом промисе, а p.then регистрируется синхронно.

После того, как первый внутренний then зарегистрирован, выполняется второй external then (тогда external second then и external first then регистрируются синхронно). Затем выполнить первый внутренний потом по очереди, а второй внутренний потом.

output:
внешние обещания
сначала внешний, потом
Внутренние обещания
внешняя секунда тогда
сначала потом внутри
внутренняя секунда тогда

Я считаю, что если вы сможете понять приведенные выше четыре фрагмента кода, вы очень хорошо поймете выполнение и регистрацию Promise.

Если вы все еще не понимаете, пожалуйста, прочитайте еще несколько раз, я думаю, вы поймете~~~~~~~~~~

Главная мысль:

Очередь регистрации микрозадач Promise then и выполнение разделены.
Регистрация: это процесс выполнения кода, который полностью соответствует JS и Promise.
Выполнение: сначала синхронизируйте, пожинайте в задачах, а затем в макрозадачах.

Только поняв вышеизложенное отдельно, мы можем по-настоящему понять порядок их выполнения~~~~~~~~~~~~~~~~~~

пятый код

После тщательного и глубокого анализа текста, приведенного выше, я верю, что вы внезапно просветлеете.
Еще одна серьезная тема:

new Promise((resolve, reject) => {
  console.log("外部promise");
  resolve();
})
  .then(() => {
    console.log("外部第一个then");
    new Promise((resolve, reject) => {
      console.log("内部promise");
      resolve();
    })
      .then(() => {
        console.log("内部第一个then");
      })
      .then(() => {
        console.log("内部第二个then");
      });
    return new Promise((resolve, reject) => {
      console.log("内部promise2");
      resolve();
    })
      .then(() => {
        console.log("内部第一个then2");
      })
      .then(() => {
        console.log("内部第二个then2");
      });
  })
  .then(() => {
    console.log("外部第二个then");
  });

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

output:
внешние обещания
сначала внешний, потом
Внутренние обещания
внутреннее обещание2
сначала потом внутри
Первый внутренний тогда2
внутренняя секунда тогда
внутренняя секунда потом2
внешняя секунда тогда

Разница в реализации PROMISE/A+ и Webkit’s Promise

Мы знаем, что обещание ES6 должно считаться обратно совместимым.В разработке обещание системного ядра часто не используется, но вводится обещанием установки npm. Так является ли реализация обещания js точно такой же, как реализация браузера?

По анализу приведенных выше четырех кусков кода мы понимаем, что выполнение промиса потом зависит от завершения выполнения предыдущего тогда, то есть после состояния разрешения, до того, как он начнет регистрироваться в очереди микрозадачи.

Давайте рассмотрим тему вместе, разница здесь в том, что затем возвращается Promise.resolve();

new Promise((resolve, reject) => {
  console.log('外部promise');
  resolve();
})
  .then(() => {
    console.log('外部第一个then');
    new Promise((resolve, reject) => {
      console.log('内部promise');
      resolve();
    })
      .then(() => {
        console.log('内部第一个then');
        return Promise.resolve();
      })
      .then(() => {
        console.log('内部第二个then');
      })
  })
  .then(() => {
    console.log('外部第二个then');
  })
  .then(() => {
    console.log('外部第三个then');
  })

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

output:
внешние обещания
сначала внешний, потом
Внутренние обещания
сначала потом внутри
внешняя секунда тогда
внутренняя секунда тогда
Затем внешний третий

Эта схема последовательности выполнения задачи:

promise

Выше мы являемся результатом вывода кода с использованием js-реализации Promise.

Однако вы запускаете этот код в chrome/safari и обнаруживаете, что результаты отличаются.Ниже приведен результат ядра webkit.

promise

Что является причиной этого?
Почему еще один return Promise.resolve() завершает выполнение внешнего?
Чтобы понять это, нам все же придется отличать понимание от оформления и исполнения.

После выполнения вывода "сначала внутреннее, затем ", встретите return Promise.resolve(), Давайте проанализируем этот Promise.resolve();

Обещание/а + реализация:

Выполните return Promise.resolve() , создайте экземпляр Promise, установите экземпляр Promise в состояние разрешения, этот Promise.resolve() является синхронным, и Promise завершен, поэтому он не повлияет на регистрацию других, кроме s. Так что наш вышеприведенный анализ полностью верен.
Ниже приведена реализация Promise.resolve, мы обнаружили, что она полностью синхронна, поэтому не влияет на конечный результат.

Promise.resolve = function (value) {
  if (value instanceof Promise) return value;
  if (value === null) return NULL;
  if (value === undefined) return UNDEFINED;
  if (value === true) return TRUE;
  if (value === false) return FALSE;
  if (value === 0) return ZERO;
  if (value === '') return EMPTYSTRING;
  if (typeof value === 'object' || typeof value === 'function') {
    try {
      var then = value.then;
      if (typeof then === 'function') {
        return new Promise(then.bind(value));
      }
    } catch (ex) {
      return new Promise(function (resolve, reject) {
        reject(ex);
      });
    }
  }
  return valuePromise(value);
};

Браузерная (вебкит) реализация Promise:

Выполните return Promise.resolve() , создайте экземпляр Promise и выполните resolve , В это время значение разрешения обещания (здесь не определено) вводится в очередь микрозадач, а состояние обещания меняется на противоположное для разрешения. Затем он выполняет ранее зарегистрированное «внешнее второе затем», затем регистрирует «внешнее третье затем», а затем выполняет обещание неопределенного значения разрешения возврата «внутреннее сначала затем». После завершения выполнения затем зарегистрируйтесь следующий затем, но следующего затем нет, выполнение завершено, вся задача возврата завершена, а также выполняется задача синхронизации, а затем выполняется зарегистрированное «внешнее третье затем». "внешний четвертый затем" регистрируется. В это время выполнение "внутреннего сначала затем" завершается, "внутренний второй затем" регистрируется, и, наконец, выполняется "внешний четвертый затем", а затем только что зарегистрированный " внутренняя секунда затем".

Исходный код выглядит следующим образом:

void Promise::Resolver::Resolve(Handle<Value> value) {
  i::Handle<i::JSObject> promise = Utils::OpenHandle(this);
  i::Isolate* isolate = promise->GetIsolate();
  LOG_API(isolate, "Promise::Resolver::Resolve");
  ENTER_V8(isolate);
  EXCEPTION_PREAMBLE(isolate);
  i::Handle<i::Object> argv[] = { promise, Utils::OpenHandle(*value) };
  has_pending_exception = i::Execution::Call(
      isolate,
      isolate->promise_resolve(),
      isolate->factory()->undefined_value(),
      arraysize(argv), argv,
      false).is_null();
  EXCEPTION_BAILOUT_CHECK(isolate, /* void */ ;);
}
PromiseResolve = function PromiseResolve(promise, x) {
    PromiseDone(promise, +1, x, promiseOnResolve)
}
function PromiseDone(promise, status, value, promiseQueue) {
    if (GET_PRIVATE(promise, promiseStatus) === 0) {
        PromiseEnqueue(value, GET_PRIVATE(promise, promiseQueue), status);
        PromiseSet(promise, status, value);
    }
}

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

promise

консолидировать

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

new Promise((resolve, reject) => {
  console.log("外部promise");
  resolve();
})
.then(() => {
    console.log("外部第一个then");
    new Promise((resolve, reject) => {
        console.log("内部promise");
        resolve();
    })
    .then(() => {
        console.log("内部第一个then");
    })
    .then(() => {
        console.log("内部第二个then");
    });
    return new Promise((resolve, reject) => {
        console.log("内部promise2");
        resolve();
    })
    .then(() => {
        console.log("内部第一个then2");
    })
    .then(() => {
        console.log("内部第二个then2");
    });
})
.then(() => {
    console.log("外部第二个then");
});

Добро пожаловать в мой публичный аккаунт WeChat:

follow-me