浏览器的事件循环和异步编程
浏览器的事件循环
'珠峰高级工程师'参考这里的项目:https://gitee.com/wangluoshihuang/webgaoji (opens new window)
- 珠峰高级工程师里面也有讲解浏览器的事件循环,只是不知道是不是在上面的库里
奈学教育这个整个课程以及笔记:
别人github上的笔记:https://github.com/node-deno/LearnPromise
自己对此作了添加,放在了自己的码云上:https://gitee.com/wangluoshihuang/naixue-education/tree/master/event-loop
补充:
【工作线程】:这个线程是幕后工作的,用来处理异步任务的执行。这个线程是用来决定异步任务进入任务队列的时机。例如定时器时间到了就把定时器相应的代码放入到任务队列里;ajax有响应了,就把回到函数放入到任务队列里;绑定的事件被触发了,就把回调函数放入任务队列里,等...。
异步代码先进入工作线程等待执行,执行时机一到才会被按顺序添加到任务队列里去,按顺序排列起来,依次进入执行栈执行。
- 执行时机:
- 定时器:时间一到
- ajax:ajax有响应时
- 绑定的事件:事件被触发
- 执行时机:
定时器什么时候开始计时?setTimeout、setInterval一执行的时候,就已经在工作线程里开始计时了。当时间到了的时候,定时器里的代码就会被放入任务队列里去等待执行。
即使我们给setTimeout设置的时间为0,但是还是有最低5ms的延迟。为什么?因为异步任务从工作线程到任务队列里这其中需要耗费时间。
参考第一天代码示例 (opens new window)的第6/9题,可以有新的收获:
- 第9题:宏任务里又有新的微任务,那么需要等到当前宏异步任务执行完后,立马执行微任务之后;然后再执行同级的宏任务。
- 第6题:requestAnimationFrame的触发时机由浏览器的刷新率决定。
Promise:
- promise里的resolve、reject会
触发 then()里的回调函数以及修改promise的状态(rejected/fulfilled)以及修改Promise的结果(使用console.dir打印出这个promise就可以看见它的结果)。 - 如果不在promise里调用 resolve/reject 那么Promise的状态一直为 pending。
- promise的状态一旦被确定就不会再被修改:
- 参考 第2天笔记第5条 (opens new window) 的14/19行。
- Promise 之所以能链式调用,是基于:参考 二天笔记第6条 (opens new window) 的19行。
- promise的基本调用形式:
- 中断promise的链式调用:
- 参考PPT第11页 中断链式调⽤ (opens new window);会触发catch的调用。
- 为什么中断会成功呢?之前不是说过promise的状态一旦确定就不可修改吗?因为每一次then调用就会返回一个新的promise对象。
- 参考代码 (opens new window)的第31行。
- promise.all:
- 参数是一个每项由Promise对象组成的数组。
- 返回的结果是数组,顺序和Promise.all的参数里的每个promise调用resolve返回的值一一对应。
- 其中只要有一个参数的promise呈现reject状态或者是被中断,参数剩余还没有执行的其他promise就不会执行。
- promise.race:
- promise.all的状态只由第一个参数的promise的状态决定。
- promise里的resolve、reject会
Generator函数:
- promise写起来也非常麻烦。所以这个函数就诞生了。
- yiled不能拿到定时器的值:参考代码 (opens new window)第43行。
- 现⽤Generator将Promise的异步流程同步化:参考代码 (opens new window)
- 我们自己实现了一个函数来达成目的
关于Async和Await
解决了需要自己实现一个函数去达成“现⽤Generator将Promise的异步流程同步化”的目的。
提案中规定了我们可以使⽤async修饰⼀ 个函数,这样就能在该函数的直接⼦作⽤域中(在其子函数里使用是不行的),使⽤await来⾃动的控制函数的流程,await 右侧可以编写任何变量 或对象,当右侧是普通对象的时候函数会⾃动返回右侧的结果并向下执⾏,⽽当await右侧为Promise对象时,如 果Promise对象状态没有变成完成,函数就会挂起等待,直到Promise对象变成fulfilled,程序再向下执⾏,并且 Promise的值会⾃动返回给await左侧的变量中。async和await需要成对出现,async可以单独修饰函数,但是 await只能在被async修饰的函数中使⽤。
有了await和async就相当于使⽤了⾃带执⾏函数的Generator函数,这样我们就不再需要单独针对Generator函数 进⾏开发了,所以async和await逐渐成为主流异步流程控制的终极解决⽅案。⽽Generator慢慢淡出了业务开发者 的舞台,不过Generator函数成为了向下兼容过渡期版本浏览器的候补实现⽅式,虽然在现今的⼤部分项⽬业务中 使⽤Generator函数的场景⾮常的少,但是如果查看脚⼿架项⽬中通过babel构建的JavaScript⽣产代码,我们还是 能⼤量的发现Generator的应⽤的,他的作⽤就是为了兼容不⽀持async和await的浏览器。
async function test(){ console.log(3) var a = await 4 console.log(a) return 1 } console.log(1) test() console.log(2) 实际上上面的代码可以被翻译为: console.log(1) new Promise(function(resolve){ console.log(3) resolve(4) }).then(function(a){ console.log(a) }) console.log(2)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
从 0开始实现完整的 Promise 对象:
最近看了很多异步编程的博文,该文讲的最为透彻
浏览器异步参考题目:

从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理