предисловие
существует«Точки знаний о браузере (13) Различное время выполнения обратного вызова: макрозадачи и микрозадачи»В конце этой статьи есть несколько печатных вопросов для интервью. Я подумал, что это было недостаточно весело, поэтому я нашел несколько соответствующих вопросов для интервью и запустил их на разных версиях Node.js. Надеюсь, вам это тоже понравится.
Давайте сначала рассмотрим процесс цикла событий браузера:
- выполнить первымСинхронизированный код в текущем стеке вызовов(макрозадача);
- После того, как стек вызовов опустеет, проверьте, есть ли асинхронные задачи (микрозадачи), которые нужно выполнить;
- если такЗавершить выполнение текущего асинхронного кода(все микрозадачи в очереди микрозадач в текущей макрозадаче),
- а потомВзять следующую задачу макроса из очереди сообщенийВыполнить (сохранить стек вызовов), а затем запустить новый раунд Event Lopp.
Затем разница в цикле событий между разными версиями Node.js:
- еслиУзел 10 и ранее: в очереди макрозадач есть несколько макрозадач, и микрозадачи в микроочереди не будут выполняться до тех пор, пока не будут выполнены все макрозадачи в очереди макрозадач.
- еслиУзел 11 и выше: после выполнения задачи макроса в соответствующей очереди макросов на этапе (
setTimeout,setIntervalа такжеsetImmediateОдин из трех, исключая ввод-вывод), немедленно выполняет очередь микрозадач, выполняет все микрозадачи в микроочереди, а затем возвращается к предыдущей очереди макросов для выполнения следующей макрозадачи. ЭтоСовместимо с работой на стороне браузера.
Для этой зависимости я намеревался использоватьnvm(nvm install 10.13.0)установлены10.13.0Версия Node.js
Конкурсанты в основном представители макро задачsetTimeoutи микрозадачиPromiseи выскочкаasync/awiat.
базовая версия
Первая версия для одной задачи:
setTimeout:
console.log('script start');
setTimeout(() => {
console.log('setTimeout1')
}, 100)
setTimeout(() => {
console.log('setTimeout2')
}, 50)
console.log('script end');
в хроме естьProcessDelayTaskфункция, функция рассчитает задачи со сроком на основе времени запуска и времени задержки, а затем последовательно выполнит эти задачи со сроком. Порядок выполнения следующий:
Promise:
console.log('script start');
new Promise((resolve) => {
console.log('promise1');
resolve();
console.log('promise1 end');
}).then(() => {
console.log('promise2');
})
console.log('script end');
существуетPromiseвнутриСинхронное выполнение, поэтому будет следующий порядок печати:
async/awiat:
async function async1() {
console.log('async1 start')
await async2();
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
async1();
console.log('script end')
Порядок выполнения следующий, здесь следует отметить, чтоasync2Функции также выполняются синхронно.
Вышеупомянутая базовая версия — это разница между синхронной и асинхронной, потому что это одна задача и нет других сложных сценариев, производительность в Node.js такая же, как и в браузерах.
Комбинированный вариант один (setTimeoutа такжеPromise)
console.log('script start');
setTimeout(() => {
console.log('setimeout');
}, 0)
new Promise((resolve) => {
console.log('promise1');
resolve();
console.log('promise1 end');
}).then(() => {
console.log('promise2');
})
console.log('script end');
Порядок выполнения в Chrome:
Эта версия представляет собой просто задачу макросаsetTimeoutи микрозадачиPromiseКомбинация , поскольку это также макрозадача и микрозадача, она ведет себя одинаково в разных версиях Node.js, как и в браузере.
Комбинированная версия два (setTimeoutа такжеPromise,async/await)
console.log('script start')
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
async1()
new Promise(resolve => {
console.log('promise1 start')
resolve()
console.log('promise1 end')
}).then(() => {
console.log('promise2')
})
console.log('script end')
Порядок выполнения в Chrome:
в Node.js10.13.0Порядок выполнения в:
в Node.js12.18.3Порядок выполнения в:
Не удивляйтесь! Не удивительно! Chrome (версия 91) и Node.js12.18.3Поведение версии соответствует Node.js.10.13.0Есть некоторые отличия в версиях, в основномpromise2а такжеasync1 endПорядок печати другой, то естьasync/awaitjs обрабатывается по-разному в разных версиях Node.js (после 70 и до Chrome 70 также по-разному).
Чтобы понять разницу, перейдите кpromise, async, await, execution orderпроверить это.
Смешанная версия первая (setTimeoutа такжеPromise)
console.log('script start')
setTimeout(() => {
console.log('setTimeout1')
Promise.resolve().then(() => {
console.log('promise1')
})
}, 0)
setTimeout(() => {
console.log('setTimeout2')
Promise.resolve().then(() => {
console.log('promise2')
})
}, 0)
console.log('script end')
Порядок выполнения в Chrome:
в Node.js10.13.0Порядок выполнения в:
в Node.js12.18.3Порядок выполнения в:
Эта смешанная версия в основном рассматривает разницу между циклом событий до и после Node.js версии 10.
Смешанная версия два (setTimeoutа такжеPromise)
setTimeoutа такжеPromise:
console.log('script start')
Promise.resolve().then(() => {
console.log('promise1')
setTimeout(() => {
console.log('setTimeout1')
}, 0)
})
setTimeout(() => {
console.log('setTimeout2')
Promise.resolve().then(() => {
console.log('promise2')
})
}, 0)
console.log('script end')
Порядок выполнения в Chrome:
в Node.js10.13.0Порядок выполнения в:
в Node.js12.18.3Порядок выполнения в:
Эта версия выполняет синхронный код (script end), на этот раз первыйPromise(promise1) в очереди микрозадач второйsetTimeoutОн добавлен в хвост очереди сообщений (delay queue), на этот раз для выполнения микрозадач, то есть печатиpromise1, то поставить первыйsetTimeoutДобавить в хвост очереди сообщений (очередь задержки), чтобы оно было напечатано первымsetTimeout2,promise2распечатать позжеsetTimeout1.
Смешанная версия три (Promise)
Комбинация макрозадач и микрозадач почти одинакова, давайте взглянем на сочетание микрозадач!
async function async1() {
console.log('async1 start');
Promise.resolve(async2()).then(() => {
console.log('async1 end');
})
}
async function async2() {
console.log('async2');
Promise.resolve(async3()).then(() => {
console.log('async2 end');
})
}
async function async3() {
console.log('async3');
Promise.resolve().then(() => {
console.log('async3 end');
})
}
console.log('script start');
async1();
new Promise((resolve) => {
console.log('promise1');
resolve();
}).then(() => {
console.log('promise2');
});
console.log('script end');
Порядок выполнения в Chrome:
в Node.js10.13.0Порядок выполнения в:
Это также легко понять: в соответствии с порядком входа и выхода из стека вызовов код синхронизации выполняется первым.async3только тогда, когдаasync3 endПрисоединяйтесь к очереди микрозадач, затемasync3()функция выскакивает из стека, возвращается вasync2,Пучокasync2 endПрисоединяйтесь к очереди микрозадач, и то же самое верно для следующих, так что есть этот порядок печати.
Смешанная версия четыре (Promiseа такжеasync/await)
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
await async3()
console.log('async2 end')
}
async function async3() {
await console.log('async3');
console.log('async3 end')
}
console.log('script start');
async1();
new Promise((resolve) => {
console.log('promise1');
resolve();
}).then(() => {
console.log('promise2');
});
console.log('script end');
Порядок выполнения в Chrome:
в Node.js10.13.0Порядок выполнения в:
Отличие четвертой версии от третьей состоит в том, чтоasyncвнутри функцииPromiseзаменяетсяawait, а в версии триasyncНе работаетawait, вы можете поставитьfunctionпереднийasyncНе обманывайтесь, если знак будет удален.
Синхронный код выполняется дляscript endЯ думал, что это было хорошо раньше.asyncСуществуютawait, поэтому код можно понимать как следующую форму:
Promise.resolve().then(() => {
console.log('async3 end');
Promise.resolve().then(() => {
console.log('async2 end');
Promise.resolve().then(() => {
console.log('async1 end');
})
})
})
Promise.resolve().then(() => {
console.log('promise2');
})
Смешанная версия пять (Promiseа такжеasync/await)
function async1() {
console.log('async1 start');
Promise.resolve(async2()).then(() => {
console.log('async1 end');
})
}
function async2() {
console.log('async2');
Promise.resolve(async3()).then(() => {
console.log('async2 end');
})
}
async function async3() {
await console.log('async3');
console.log('async3 end');
}
console.log('script start');
async1();
new Promise(function (resolve) {
console.log('promise1');
resolve();
}).then(function () {
console.log('promise2');
});
console.log('script end');
Порядок выполнения в Chrome:
в Node.js10.13.0Порядок выполнения в:
Через тест предыдущей версии 4 вам здесь не должно быть сложно, есть толькоasync3Eстьawait, выполнять доscript endТак же, как и раньше, вот и мыasync3 endКод позади может быть преобразован в следующую форму мышления:
Promise.resolve().then(() => {
console.log('async3 end');
Promise.resolve().then(() => {
console.log('async2 end');
})
})
Promise.resolve().then(() => {
console.log('async1 end');
})
Promise.resolve().then(() => {
console.log('promise2');
})
Суммировать
На бумаге в конце концов я чувствую себя мелким, и я абсолютно точно знаю, что это дело должно быть сделано.
Связанные статьи о циклах событий и макрозадачах, написанных ранее:
«Очки знаний о браузере (12) Механизм цикла событий (цикл событий)»
«Обещание асинхронного программирования: от использования до рукописной реализации (4200 слов)»