Javascript 事件循环与异步

1 引言

我为什么要选这篇文章呢?

sessionstack 最近接连发了好几篇文章, 深入探讨 JS, 以及 JS 中一些内部原理. 文中也讲到了, 伴随深入了解 JS 中的一些工作原理, 才有可能写出更好的代码和程序.

而 JS 中 Event Loop, 我的感觉就像 JS 中的一门内科, 我们平时只注意外科创伤,却忽视了内科问题往往容易莫名其妙的生病。了解 JS Event Loop 的原理,对 setTimeout Promise 这种基础概念不再浮在表层,可以写出更可靠的代码,如果你是前端新人,不要总是因为这个问题挂在一面 :p。

2 内容概要

从前我对 Event Loop 的理解也并不透彻,通过仔细阅读此文后, Event Loop 、宿主环境、js 线程三者之间关系更加透明了,希望读者读完后也能有所体会。

文中 Promise、async/await 部分就忽略了,本篇重点介绍 Event Loop 。

Event Loop 与 Call Stack、Web APIs 之间的关系

原文通过 16 个图表达了 5 行代码的执行过程,太长就只贴第一张图了。

Call Stack 是调用栈,Event Loop 就是本期的主角 – 事件循环,Web APIs 泛指宿主环境,比如 nodejs 中的 c++,前端中的浏览器。

任何同步的代码都只存在于 Call Stack 中,遵循先进后出,后进先出的规则,也就是只有异步的代码(不一定是回调)才会进入 Event Loop 中,哪些是异步代码呢?比如:

setTimeout()
setInterval()
Promise.resolve().then()
fetch().then()

所有这些异步代码在执行时,都不会进入 Call Stack,而是进入 Event Loop 队列,此时 JS 主线程执行完毕后,且异步时机到了,就会将异步回调中的代码推入 Call Stack 执行。

而控制异步什么时机开始执行,是由宿主环境决定的,因为此时 js 主线程已经调用完毕,除非 Event Loop 队列有内容,推送到 Call Stack 中,否则 js 引擎也不会再执行任何代码。比如通过 fetch 发送请求,当 js 调用浏览器发送请求后,直到浏览器主动告诉 js 请求完成了,期间 js 是无法干预任何的。

最终效果如下 gif 图所示:

Event Loop

Microtask 与 Macrotask

Event Loop 处理异步的方式也分两种,分别是 setTimeout 之流的 Macrotask,与 Promise 之流的 Microtask

异步队列是周而复始循环执行的,可以看作是二维数组:横排是一个队列中的每一个函数,纵排是每一个队列。

Macrotask 的方式是将执行函数添加到新的纵排,而 Microtask 将执行函数添加到当前执行到队列的横排,因此 Microtask 方式的插入是轻量的,最快被执行到的。

3 精读

Event Loop 内容不多,内容概要部分已经讲的比较彻底了,原文最后扯到了 Promise, async/await 的用法和注意点,不然是不会这么长的。

我最近写了一些 dob-react tests 测试文件,发现 componentWillMount 函数在 Microtask 时机 setState 不会触发 rerender:

class Hello extends React.Component {
    async componentWillMount() {
      await immediate(()=>{
        this.setState({a:1})
    })
  }

  render() { /**/ }
}

这种 immediate 函数的写法只会 render 一次:

function immediate(fn) {
    return new Promise(resolve => {
        fn()
        resolve()
    });
}

在线 Demo:http://jsfiddle.net/69z2wepo/90440/

如果再套一层 setTimeout,哪怕是一层 Promise, 就会 render 两次:

function immediate(fn) {
    return new Promise(resolve => Promise.resolve().then(() => {
        fn()
        resolve()
    }));
}

在线 Demo:http://jsfiddle.net/69z2wepo/90441/

ps: 感谢读者们的回复,其实第一个 immediate 函数根本就写错了,执行 fn 的时机是同步的,还是老老实实的使用 Promise().resolve().then() 吧。

4 总结

理解了事件循环之后,才是第一步,比如我就对 React 的生命周期中异步 setState 合并机制时而生效,时而不生效抱有疑问,所以想要写好稳健的业务代码还是挺难的,首先要理解这种 “内科” 知识,其次要读懂 react 源码,最后你还要保证不会忘。

阅读全文
资源下载
下载价格免费
下载说明:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.shuli.cc/?p=17572,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?