关于javascript:玩转事件循环

40次阅读

共计 4057 个字符,预计需要花费 11 分钟才能阅读完成。

1. 前言

这篇文章是想跟大家一起讨论一下 javascript 中高大上的Event Loop 事件循环机制

代码得仔细分析,能力绕的过去,否则容易绕晕,审慎再审慎~~~

事件循环次要讲的是异步执行问题,没方法,同步就是程序执行,没什么好说的。

<font color=gray> 至于什么线程、同步异步、调用栈、浏览器线程之类的概念,本文一律没有,有意者可自行查阅。</font>

2. 简介

2.1 工作队列

  • 宏工作队列:script 整体代码、setTimeout/setInterval……
  • 微工作队列:Promise

工作队列 在事件循环机制中施展着核心作用。是咱们理解事件循环机制的外围因素。

2.2 执行程序

1. 无 async/await 的执行程序

留神点前置:

  • Promise 中的函数属于同步函数

    • new Promise(function a(resolve, reject) {console.log('此为同步函数')
      })
  • setTimeout、setInterval、Promise.then 等函数,不为实现状态的状况下,不会推送到工作队列中

图解:

流程图执行阐明:

  1. 首先执行同步代码
  2. 执行完结后查看微工作队列是否有工作,有则执行
  3. 微工作队列清空,查看宏工作队列是否有工作,有则执行
  4. 循环 2 和 3。直至两个队列都清空
  5. 执行完结

栗子:

console.log('1');

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

new Promise(function(resolve) {console.log('3');
    resolve();}).then(function() {console.log('4');
});
console.log('5')

剖析:

  1. 首先执行同步代码,上述代码中 console.log(1)、console.log(3)console.log(5) 都属于同步代码。优先输入。同时执行 延时函数、then 函数 ,then 函数立刻返回后果,推送到 微工作队列
  2. 查看微工作队列,有 console.log(4) 工作,执行并输入。微工作队列完结。
  3. 延时函数倒计时实现推入宏工作队列。
  4. 查看宏工作队列,有 console.log(2) 工作,执行并输入。宏工作队列完结。
  5. 代码执行完结。
  6. 输入程序:1,3,5,4,2

2.async 和 await 后的工夫循环机制

<font color=red> 揭示: 局部文章会介绍 await 让出执行权的问题,这里先不探讨,容易绕晕。有趣味的能够查看以下。</font>

await 代码的执行:

  • async 标记的函数属于同步函数

    • async function log () {console.log('同步函数')
      }
  • await 函数中返回一个 promise,则执行后为 pending 状态,将下方的代码作为微工作解决,并且,只有 await 函数执行之后才会执行下方代码(请看栗子 4)

    • async function async2() {return new Promise(function(resolve) {console.log('3');  // 3
          resolve();}).then(function() {console.log('4'); // 6
        });
      }
      console.log(async2()) // Promise {<pending>}
  • await 函数中执行一个 promise 但不 return 或者 resolve 被 setTimeout/setInterval 包裹,则执行后为 完结 状态,将下方的代码作为同步代码执行

    • // await 函数中执行一个 promise 但不 return
      async function async2() {
        // 区别在于没有 return
        new Promise(function(resolve) {console.log('3');  // 3
          resolve();}).then(function() {console.log('4'); // 6
        });
      }
      console.log(async2()) // Promise {undefined}
      
      
      // resolve 被 setTimeout/setInterval 包裹
      async function async2() {new Promise(function(resolve) {console.log('3');
          setTimeout(() => {resolve();
          }, 0)
        }).then(function() {console.log('4');
        });
      }
      console.log(async2()) // Promise {undefined}

栗子 1:

async function async1() {console.log('1');
  await async2();
  console.log('2');
}
async function async2() {return new Promise(function(resolve) {console.log('3');  
    resolve();}).then(function() {console.log('4'); 
  });
}
console.log('5'); 

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

new Promise(function(resolve) {console.log('7'); 
  resolve();}).then(function() {console.log('8'); 
});

console.log('9');

解析:

  1. 先执行同步,console.log(5)、console.log(1)、console.log(3)、console.log(7)、console.log(9),将 console.log(4)、console.log(8) 放入微工作队列。将 console.log(6) 放入宏工作队列。
  2. 执行 console.log(4) 因为async2 返回的是 promise 所以将前面的代码放入微工作队列中。此时微工作队列有两个工作 console.log(8)、console.log(2)。清空微工作队列,输入 8 和 2
  3. 执行宏工作队列。输入 6
  4. 输入程序 5,1,3,7,9,4,8,2,6

栗子 2(栗子 1 的变种):

// 将 async2 改为上面这种写法,其余代码不变
async function async2() {new Promise(function(resolve) {console.log('3');  
    resolve();}).then(function() {console.log('4'); 
  });
}

解析:

  1. 栗子 1 第一步 放弃不变
  2. 扭转第二步,执行 console.log(4) 因为async2 返回的不是 promise 所以间接执行前面的代码。输入 2此时微工作队列只有 console.log(8) 清空微工作队列,输入 8
  3. 栗子 1 第三步 放弃不变
  4. 输入程序:5,1,3,7,9,4,2,8,6

栗子 3(仍旧是栗子 1 的变种,最初一个小栗子):

async function async1() {console.log('1');
  await async2();
  console.log('2');
}
// 将 async2 中的 resolve 函数应用 setTimeout 包裹 且 return 一个 Promise
async function async2() {return new Promise(function(resolve) {console.log('3');
    setTimeout(() => {resolve();
    }, 0)
  }).then(function() {console.log('4');
  });
}
console.log('5');

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

new Promise(function(resolve) {console.log('7');
  resolve();}).then(function() {console.log('8');
});

解析:

  1. 先执行同步,console.log(5)、console.log(1)、console.log(3)、console.log(7)、console.log(9),将 console.log(8) 放入微工作队列。将 console.log(6) 和 console.log(4)放入宏工作队列。
  2. 执行微工作console.log(8),输入 8
  3. 执行宏工作console.log(6),输入6
  4. 因为 async2 返回一个 Promise,所以只能等到async2 执行之后才会将 console.log(2) 推入到微工作队列。
  5. 执行宏工作 console.log(4),输入4 并将 console.log(2)推入到微工作中。
  6. 执行微工作console.log(2),输入2
  7. 输入程序:5,1,3,7,9,8,6,4,2

栗子 4(真的是最初一个小栗子了):

// 将栗子 3 的 async2 函数改为如下写法
async function async2() {new Promise(function(resolve) {console.log('3');
    setTimeout(() => {resolve();
    }, 0)
  }).then(function() {console.log('4');
  });
}

解析:

  1. 栗子 3 第一步不变
  2. 因为 async2 不返回 Promise,所以会将console.log(2) 作为同步代码执行,第二步执行同步工作console.log(2) 输入2
  3. 执行微工作console.log(8),输入 8
  4. 执行宏工作console.log(6),输入6
  5. 执行宏工作console.log(4),输入4
  6. 输入程序:5,1,3,7,9,2,8,6,4

无论你是否认真的查看了下面的文章,首先祝贺你能够看到这里,事件队列始终是比拟难以了解的 javascript 知识点。还是衷心的心愿这篇文章能带给你不一样的了解。也祝愿正在看文章的你技术越来越好。

正文完
 0