Event loops秒懂

image

簡介

JS是一種單線程腳本語言,為什么要設(shè)計成單線程?

舉例說明,假設(shè)JS是多線程腳本語言,A線程修改了DOM,B線程刪除了DOM,一旦B線程先執(zhí)行完,DOM被刪除了,A線程就會報錯,為了避免類似這種問題,JS被設(shè)計為單線程

單線程的問題是一次只能做一件事,要做第二件事,必須等第一件事先做完。假如有個需求是每5分鐘更新一次數(shù)據(jù),用setInterval去計時,那么這個頁面JS永遠(yuǎn)無法做其他事了,線程一直被setInterval占用著。為了讓JS可以同時執(zhí)行多個任務(wù),引入了Event loops(事件循環(huán))機(jī)制

Event loops分為2種隊列,task隊列、microtask隊列,業(yè)界一般把tasks隊列稱為宏任務(wù),microtask翻譯過來叫微任務(wù)。

task隊列和microtask隊列執(zhí)行順序是怎樣的?

代碼剛開始執(zhí)行時,整體代碼就是一個task,立即執(zhí)行這個task,在執(zhí)行過程中

  • 遇到setTimeout、setInterval、I/O、setImmediate(Nodejs環(huán)境)就往task隊列里push
  • 遇到Promise.then/catch/finally、MutationObserver、process.nextTick(Nodejs環(huán)境)就往microtask隊列里push

每執(zhí)行完一個task,就會查看microtask隊列里有沒有待執(zhí)行的任務(wù),如果有,則按先進(jìn)先出的原則依次執(zhí)行其中的任務(wù),執(zhí)行完了再回到task隊列,取下一個task執(zhí)行;如果沒有,就直接執(zhí)行下一個task,以此類推,這就是Event loops,類似于遞歸執(zhí)行過程

案例

按照以上規(guī)則,思考以下代碼輸出順序

// 先自己思考一下輸出順序
console.log('script start');

setTimeout(function () {
  console.log('timeout');
}, 0);

Promise.resolve().then(function () {
  console.log('promise');
}).then(function () {
  console.log('then');
});

console.log('script end');

分析:

  1. 整體代碼做為第一個task,從上到下開始執(zhí)行
  2. 輸出script start
  3. 遇到setTimeout(),push到task隊列,等待執(zhí)行
  4. 遇到Promise第一個then(),push到microtask隊列,等待執(zhí)行
  5. 輸出script end
  6. 第一個task執(zhí)行完成,查看microtask隊列,有任務(wù),開始執(zhí)行
  7. 輸出promise,遇到第二個then(),push到microtask隊列
  8. 輸出剛剛push的then
  9. microtask隊列執(zhí)行完成,取下一個task執(zhí)行
  10. 輸出timeout

輸出順序為:script start -> script end -> promise -> then -> timeout

升級,return Promise

將上面例子的Promise升級了一下,假設(shè)Promise.then內(nèi)部又有Promise,怎么分析?

Promise.resolve().then(function () {
  console.log('promise');
  return new Promise((resolve, reject) => {
    console.log('inner promise');
    resolve();
  }).then(() => {
    console.log('inner then1');
  }).then(() => {
    console.log('inner then2');
  })
}).then(function () {
  console.log('then');
});

分析:

  1. 整體代碼為第一個task,從上到下開始執(zhí)行
  2. 遇到第一個then,push到microtask隊列
  3. 第一個task執(zhí)行完成,查看microtask隊列,有任務(wù),開始執(zhí)行
  4. 輸出 promise
  5. 進(jìn)入內(nèi)部new Promise,輸出 inner promise
  6. 遇到內(nèi)部new Promise第一個then,push到microtask隊列
  7. 輸出剛剛push的 inner then1
  8. 遇到內(nèi)部new Promise第二個then,push到microtask隊列
  9. 輸出剛剛push的 inner then2
  10. 內(nèi)部new Promise執(zhí)行完,外部promise第一個then拿到返回值,繼續(xù)往下,遇到它的第二個then,push到microtask隊列
  11. 輸出剛剛push的 then

輸出順序為:promise -> inner promise -> inner then1 -> inner then2 -> then

注意:then鏈?zhǔn)秸{(diào)用時,如果前面的then方法return了一個新Promise對象,后面的then會等待這個新Promise對象狀態(tài)發(fā)生變化后,才會執(zhí)行,換句話說,兩個then的執(zhí)行由異步變成同步了,如果把return去掉呢?

變化,無return Promise

Promise.resolve().then(function () {
  console.log('promise');
  new Promise((resolve, reject) => {
    console.log('inner promise');
    resolve();
  }).then(() => {
    console.log('inner then1');
  }).then(() => {
    console.log('inner then2');
  })
}).then(function () {
  console.log('then');
});

分析:

  1. 整體代碼為第一個task,從上到下開始執(zhí)行
  2. 遇到第一個then,push到microtask隊列
  3. 第一個task執(zhí)行完成,查看microtask隊列,有任務(wù),開始執(zhí)行
  4. 輸出 promise
  5. 進(jìn)入內(nèi)部new Promise,輸出 inner promise
  6. 遇到內(nèi)部new Promise第一個then,push到microtask隊列,前6步跟上面一樣
  7. 此時,外部Promise對象的第一個then里的同步代碼已經(jīng)執(zhí)行完了,接著執(zhí)行它的第二個then,push到microtask隊列
  8. 繼續(xù)執(zhí)行microtask,輸出 inner then1
  9. 到了內(nèi)部new Promise的第二個then,push到microtask隊列
  10. 繼續(xù)執(zhí)行microtask,輸出 then
  11. 最后輸出 inner then2

輸出順序為:promise -> inner promise -> inner then1 -> then -> inner then2

總結(jié):return去掉之后,前面的then執(zhí)行完同步代碼就會跳到下一個then

思考

最后思考一個問題,下面這個錯誤能被catch捕獲到嗎?為什么?

new Promise(function (resolve, reject) {
  setTimeout(function () { 
    throw new Error('test') 
  }, 0)
  resolve('ok');
}).catch(err => {
  console.error(err);
});

熟悉了Event loops,回答這個問題就很容易:不能捕獲到。因為錯誤在setTimeout內(nèi)部拋出,setTimeout和.catch并不在同一個task執(zhí)行,拋出錯誤的時候,catch已經(jīng)執(zhí)行完了。

覺得不錯,點個star吧Github

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容