2021.09.30

JavaScript 是单线程的,一般发送的网络请求是异步的吧,但是网络请求也不会阻塞页面渲染,这又是怎么回事呢?
对 eventloop 就是为了解决异步编程而出现的模型。JavaScript 的运行时包含3个概念,
一个 stack 由一堆 frames 组成,一个函数的调用会在 stack 上创建一个 frame,一个函数的 return 会销毁调用时创建的 frame。当所有的 frame 都被销毁的时候,stack 就空了。
JS 对象就被分配在这一块内存区。
由一堆待处理的异步消息(task/message)组成,而消息其实分为两种,宏任务和微任务,对应的也有两个 queue,micro task queue 和 macro task queue。setTimeout,event listener 等会作为宏任务被 enqueue 到这个 macro task queue 去, promise 会作为微任务被 enqueue 到 micro task queue。
当 stack 为空的时候,event loop 会先从 macro task queue 里 dequeue 一个消息,并调用,调用会在 stack 上生成一堆 frame,等待 stack 又变空的时候,event loop 会在 dequeue 下一个 message 之前,先会清空 microtask queue(dequeue 一个,到 stack 上执行一个,等 stack 为空再去 check micro task queue,有的话再 dequeue,执行,直到 micro task queue 被清空),清空后 event loop 才会继续 dequeue 下一个 macrotask queue,依次往复)
题外话:宏任务在 ecma 规范里面叫做 ScriptJobs,微任务叫做 PromiseJobs。

参考这个例子写了个 demo,这里不会涉及到过多的 promise 相关的知识点,你只需知道 promise resolve 的时候,.then 注册的回调函数会作为微任务被 enqueue 到 micro task queue 去,
function foo() {
console.log('foo');
new Promise(
function(resolve, reject) {
setTimeout(function() {
resolve('RESOLVING');
}, 5000);
}
)
.then(
function(value) {
console.log(value);
}
)
}
foo();
上面 foo 被调用的时候会发生什么?
foo 这个函数并执行了这个函数,执行 foo 的时候往 stack 上加了一帧。foo 里面,你先调用了 console.log 函数,也就又往 stack 上加了一帧,现在 stack 上一共有两帧,等 console.log 执行完 return 的时候这一帧又从 stack 上被移除了,现在只有调用 foo 函数时创建的一帧了。function(resolve, reject){...} ,它又接受两个入参,都是用来 settle promise 的。在创建实例的时候会立刻调用 executor 函数,这里我们调用完会设置一个 5秒后 expire 的定时器,5秒后将 settle 这个 promise 实例。到目前为止我们还没有创建一个 PromiseJobs。(这里因为不知道 promise 的内部实现具体是怎么样的,所以不讨论 stack 和 frame 了)then 往我们上一步创建好的 promise 身上添加了一个 fulfillment handler function(value){ console.log(value);}。foo 就执行完了,现在 stack 就变空了。这个 ScriptJobs 就执行完了。.then 后往 promise 实例身上添加的 callback 一个个创建 PromiseJobs 往 PromiseJobs 队列中添加。console.log,这个 PromiseJobs 也就执行完毕了。所以最终打印出来的应该是,
foo
RESOLVING // 大约五秒后
总结:其实微任务和宏任务解释起来也比较简单,微任务就是优先级比较高的宏任务,需要在 stack 为空的时候,但是又在下一个宏任务前需要执行的任务。
学习的时候遇到的一个坑,之前看 MDN 上的 Microtask 文档的时候,发现有一个例子好像不对,dispatch 了一个 event 后,不是加到 ScriptJobs 队列里去的吗?然后自己还写了个 demo,不过最初 demo 写的有问题所以让自己愈发觉得 MDN 上文档这部分写的有问题,于是提了个 bug 给 MDN,过了两个小时,觉得可能还是自己的问题,那么多人看怎么就自己觉得有问题,是不是自己的 demo 写的有问题,觉果发现还真的是,那么我理解上还是有问题,于是自己看了看,发现 dispatchEvent 这个方法竟然是同步执行的。。。 然后立马 close 了那个 issue (汗

关于为什么要使用微任务呢,官方的解释是,