Почему дизайнер спецификации Javascript представил многозадачность - PullRequest
0 голосов
/ 06 марта 2019

Я знаю кое-что о микрозадаче и Promise, например, обработчик в Promise.prototype.then будет помещен в микрозадачу и приведен в исполнение, как только стеки вызовов опустеют, и это может быть использовано, чтобы избежать состояния гонки или что-то еще о параллелизме.

но что будет, если мы не введем микрозадачу?

1 Ответ

2 голосов
/ 10 марта 2019

Давайте возьмем этот пример кода:

Promise.resolve("resolved").then(i => console.log("resolved"));
console.log("start");

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

start
resolved

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

resolved
start

С примером кода это действительно будет реальной альтернативой. Но теперь возьмем другой пример, где обещание разрешается не кодом JavaScript, а событием более низкого уровня, глубоко скрытым в API, который инкапсулирует это с интерфейсом обещания. Допустим, это некоторый API для чтения содержимого файла:

readFile(path).then((content) => console.log("file contents: " + content));
console.log("start of a lot of work");
for (let i = 0; i < 1e7; i++) {
    // some work
}
console.Log("end of a lot of work");

Итак, согласно спецификации Promise это выдает:

start of a lot of work
end of a lot of work
file contents: .....

Теперь предположим на мгновение, что цикл работы занимает 3 секунды, и что API чтения файлов имеет реализацию, которая делегирует большую часть работы функциям операционной системы. Эти функции ОС будут работать в соответствии с архитектурой обработки ОС, которая часто является некой формой многозадачности, управляемой событиями. Суть здесь в том, что ОС сможет выполнять файловые операции, в то время как цикл JS также выполняется.

Давайте также предположим, что чтение файла завершается в течение 1 секунды. Событие переходит из ОС в промежуточное программное обеспечение API, не относящееся к JavaScript, готовое всплыть в «мир» JavaScript. Что должно произойти сейчас? Мы можем представить два варианта:

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

  2. Работающий цикл JavaScript прерван , обещание, возвращаемое readFile, разрешается, и в этот момент вызывается обратный вызов then. После завершения обратного вызова контекст выполнения, который был прерван, восстанавливается и продолжается до завершения.

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

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

...