Краткое введение: рассказ о порядке выполнения promise.resove, setTimeout, setImmediate, process.nextTick в очереди EvenLoop
1. Введение проблемы
цикл событий: обращается к основному потоку для чтения задач из «очереди задач» в цикле
// 例1:
setTimeout(function(){console.log(1)},0);
console.log(2)
//输出2,1
В приведенном выше примере мы понимаем, что сначала выполняется задача синхронизации в основном потоке, а затем задача считывается из цикла событий после выполнения задачи основного потока, поэтому сначала выведите 2, а затем выведите 1.
Порядок, в котором цикл обработки событий считывает задачи, зависит от ограничений различных правил чтения задач в очереди заданий. Например, следующий пример
// 例2:
setTimeout(function () {
console.log(3);
}, 0);
Promise.resolve().then(function () {
console.log(2);
});
console.log(1);
//输出为 1 2 3
Первый вывод 1, без проблем, потому что задача синхронизации выполняется первой в основном потоке, проблема здесь в том, как определить приоритет выполнения задач setTimeout и Promise.then.
2. Порядок выполнения в очереди заданий
Очереди в Job queue делятся на два типа: macro-task (макрозадача) и microTask (микрозадача).
Предположим, что
очередь макро-задач (макрозадач) содержит задачи: а1, а2, а3
Очередь микрозадач содержит задачи: b1, b2, b3
Порядок выполнения следующий: сначала выполнить задачу в начале очереди марко-задач, то есть задачу а1 , после выполнения выполнить все задачи в очереди микрозадач, то есть выполнить b1, b2, b3 в последовательность, и очистить микрозадачу после выполнения.задача в задаче, затем выполнить вторую задачу в марко-задаче, и цикл по очереди
Поняв порядок выполнения очередей макрозадач и микрозадач, давайте посмотрим на задачи, фактически содержащиеся в этих двух типах очередей в реальных сценариях (в качестве примера возьмем движок узла V8), в узле V8 реальный порядок задач для этих двух типов следующий
Очередь макро-задачи (macro-задачи) на самом деле содержит задачи:
script (основной программный код), setTimeout, setInterval, setImmediate, I/O (передача данных), рендеринг пользовательского интерфейса (рендеринг DOM)
Очередь микрозадач (микрозадач) на самом деле содержит задачи:
process.nextTick, Promises, MutationObserver (мониторинг дерева DOM)
Отсюда получаем порядок выполнения должен быть:
скрипт (основной программный код)—>process.nextTick—>Promises—>MutationObserver—>setTimeout—>setInterval—>setImmediate—>I/O—>рендеринг пользовательского интерфейса
В ES6 очередь макрозадач (макро) также называется ScriptJobs, а микрозадача (микро) также называется PromiseJobs.
3. Пример последовательности выполнения в реальной среде
(1) setTimeout и обещание
例3:
setTimeout(function () {
console.log(3);
}, 0);
Promise.resolve().then(function () {
console.log(2);
});
console.log(1);
Давайте сначала возьмем пример в Разделе 1 в качестве примера, и последовательность, которой нужно следовать здесь, такова:
script (основной программный код) ——>promise——>setTimeout
соответствует выводу по очереди: 1 ——>2————>3
(2) process.nextTick, обещание, setTimeout
例子4:
setTimeout(function(){console.log(1)},0);
new Promise(function(resolve,reject){
console.log(2);
resolve();
}).then(function(){console.log(3)
}).then(function(){console.log(4)});
process.nextTick(function(){console.log(5)});
console.log(6);
//输出2,6,5,3,4,1
Этот пример более сложный.Здесь следует отметить, что при определении промиса конструктивная часть промиса выполняется синхронно , поэтому проблема легко решается.
Сначала проанализируйте порядок выполнения очереди заданий (очереди задач):
скрипт (основной программный код) --> process.nextTick --> обещание --> setTimeout
1.) Основная часть : Строительная часть обещания определения является синхронной,
поэтому сначала выведите 2, а затем выведите 6 в основной части (в случае синхронизации строго в соответствии с порядком определения)
2.) process.nextTick : вывод 5
3.) promise : часть обещания здесь, строго говоря, на самом деле является частью обещания.затем, а вывод равен 3,4.
4.) setTimeout : вывод 1 в конце
Полная последовательность выполнения: 2—>6—>5—>3—>4—>1
(3) Более сложный пример
setTimeout(function(){console.log(1)},0);
new Promise(function(resolve,reject){
console.log(2);
setTimeout(function(){resolve()},0)
}).then(function(){console.log(3)
}).then(function(){console.log(4)});
process.nextTick(function(){console.log(5)});
console.log(6);
//输出的是 2 6 5 1 3 4
Отличие этой ситуации от нашего примера в (2) заключается в том, что при построении промиса нет синхронного разрешения , поэтому promise.then не существует в текущей очереди выполнения, только когда обещание переводится из pending в Resolve. then, и это решение завершается за время setTimout, поэтому в итоге выводятся 3,4.