javascript 进阶之 - 事件循环

引言
  1. js 引擎是单线程, 所以同一个时间只能做一件事.
  2. js 的执行顺序核心是队列 , 而执行的核心是执行栈. 总是从事件队列取事件执行.
  3. js 代码有同步执行代码和异步代码, 却并不是以一段代码要执行的时间做区分.
  4. 解析到 <script> 标签 , 或者成功加载一个脚本文件, 是在事件队列中添加了一个事件.
粗略流程模拟

浏览器开启 -> js 引擎开启 ->
开启定时扫描宏任务队列( task queue ) -> 任务队列有事件了 -> 暂停定时扫描 -> 取出任务到执行栈执行 -> 预编译 -> 执行 -> [ 同步执行, 异步代码抛出去 ] -> 扫描微任务队列( jobs )-> 将任务踢出执行栈 -> 栈空 ->
开启定时扫描宏任务队列( task queue ) ….

js 代码本身不负责异步代码执行, js 负责找到对应事件, 然后交给浏览器对应的处理线程处理 , 然后继续执行.

异步有哪些?
  • task
    • script 标签解析 , 脚本文件加载
    • 定时类 setTimeout , setInterval
    • http 请求 : ajax 请求 + 文件加载 onload, onerror
  • jobs
    • promise
示例
1. 仅 task

console.log('start');
setTimeout(()=>{ console.log('timeout') },0);
console.log('end');

// 打印结果: start end timeout

一段<script>脚本 , 就是一个任务( task ) , task 里面不执行执行其他的 task , 只执行 本身 taskjobs;

  1. 第一个 task => <script>
  2. <script> task 执行结果: 打印 start end , setTimeout 丢给 “定时处理线程”
  3. jobs 中没有任务 , 跳过
  4. 第一个 task 结束
  5. 扫描 task queue
  6. 下一个 task => setTimeout task
  7. setTimeout task 执行结果 : 打印 timeout
  8. jobs 中没有任务 , 跳过
  9. 第二个 task 结束
  10. 扫描 task queue
2. task 和 jobs 并存
console.log('start');

new Promise((resolve,rejct)=>{
    console.log('promise start');
    resolve()
})
.then(()=>{
    console.log('promise then1')
})
.then(()=>{
    console.log('promise then2')
})

setTimeout(()=>{ console.log('timeout') },0)
console.log('end');

// 打印结果:start , promise start , end, promise then1 ,promise then2, timeout

new Promise 传递的函数 , 是立即执行的 , 而 setTimeout 传递的函数是回调函数.

  1. 第一个 task => <script>
  2. <script> 同步执行 ① 打印 start
  3. new Promise(fn).then() , 其中 fn 和 then 都为同步任务
  4. fn 执行, ② 打印 promise start
  5. resolve 执行, 将 Promise fulfilled 状态要执行的方法 ,推入 jobs queue
  6. then 执行, 将 then 的参数( 函数 )加入 promise 的 fulfilled 状态要执行的方法的列表.
  7. setTimeout 丢给 “定时处理线程”
  8. console.log(‘end’) ③ 打印 end
  9. 执行栈空, 扫描 jobs queue
  10. 取出 jobs queue 中 Promise fulfilled 方法列表 => 两个 then 传入的函数依次执行
  11. 第一个 then , ④ 打印 promise then1 ; 第二个 then ⑤ 打印 promise then2
  12. 执行栈空 , 扫描 jobs queue, jobs queue
  13. 一次循环完成
  14. 继续扫描 task queue , 找到 setTimeout 回调事件
  15. setTimeout 回调执行, ⑥ 打印 timeout
  16. 执行栈空, jobs queue 空 , task queue 空.

注意: 其中 new Promise 同步执行的不只是 Promise 传入的函数, 还有后面的两个 then, then 并不是扫描 jobs queue 后才执行, jobs queue 执行的是 then 里面的函数.
可以改写一下 promise 的结构证明:

    console.log('start')

    new Promise((resolve, rejct) => {
      console.log('promise start');
      resolve()
    })
      .then((function () {
        console.log('then is runing')
        return function () {
          console.log('promise then1')
        }
      })())
      .then(() => {
        console.log('promise then2')
      })

    setTimeout(() => { console.log('timeout') }, 0)

可以看出 then is runingend 之前.

总结
  1. 在部分浏览器中, 并未把 promise 当成一个 jobs , 而是直接列为一个新的 task .
  2. js 执行, 是一系列的 task 的有序执行 , 一个 task 又可以生成新的 task 和 新的 jobs, 一个jobs 又可以生成新的 task 和 jobs , 一个 task 的执行完成包括其所有 jobs 的完成.
相关链接
  1. [javascript 进阶之 - promise]

猜你喜欢

转载自blog.csdn.net/haokur/article/details/80556204