美文网首页
JavaScript的异步机制 Asynchronous

JavaScript的异步机制 Asynchronous

作者: 四月白绵羊 | 来源:发表于2019-10-08 04:13 被阅读0次

JavaScript是一个单线程的编程语言,即便它包含了 awaitasync 的关键字也改变不了这个事实。
那么,作为单线程语言的JS是怎么实现异步操作的呢?

Synchronous


首先,先了解一下JS的同步执行方式。例如下面的🌰:

const second = () => {
    console.log('Hello there!');
}

const first = () => {
    console.log('The Start.');
    second();
    console.log('The End.');
}

可以想象输出会是:

Start.
Hello World!
End.

这是一个同步调用的例子,那我们来说说JS执行机制背后包含的东西:Execution Context 和 Call Stack

Execution Context 执行上下文

每一段代码都运行在自己的上下文之中。例如,回调函数的上下文就是它被定义的地方,包括可获取的变量。

Call Stack 调用栈

Stack就是一个LIFO先进后出的结构。后进入的函数先被执行和释放。还是上面那个例子,每个函数进入栈的顺序如下图:


image.png

所以,发生了什么?

首先,主函数先被运行了,于是主函数包括其上下文Context都被放进了Stack。接着first()及其上下文也被放进了Stack。由于first包含了三个子函数,于是console.log('The Start.');也被放进Stack,且马上被执行,执行完后被释放。同样second()也被放入、执行和释放。然后console.log(‘The End’)也被放入、执行和释放。由于first()的所有内容已经执行完毕,也被释放。最后主函数被释放。

这就是一个完整的同步执行的过程。那么异步执行呢?

Asynchronous


前面也说到了,JS是一个单线程的语言,所以JS并没有办法靠自己完成异步的操作,它需要依赖于浏览器环境或者Node.js环境。先来看一张图:


image.png

这里出现了三个新的东西:Event Loop,Web Api 和Message Queue。

注意这三部分都不是属于JavaScript Engine 的范畴。而是属于浏览器JS运行环境的。

在下面这个🌰中:

const networkRequest = () => {
  setTimeout(() => {
    console.log('Async Code');
  }, 2000);
};
console.log('Hello World');
networkRequest();
console.log('The End');

它的执行过程是:


1_sOz5cj-_Jjv23njWg_-uGA.gif

注意到,这里一个显著的区别就是,当setTimeout被调用的时候,它实际上在Web Api中设置了一个定时器,然后接着执行同步操作。这个计时器可能在同步操作结束之前或者之后结束,但无论如何回调函数都会先被放进Message Queue。

等到Call Stack 中的任务都执行完了之后,Event Loop会来查询Message Queue之中是否有等待被执行的任务。如果有,就取出第一个任务放进Call Stack执行。以此类推,直到Message Queue空了。

Message Queue中同样包含事件响应,如鼠标点击监听等。

Conclusion

所以JS的执行本质是一个调用栈。而它的异步操作实际上是依赖于浏览器当中的JS运行环境来帮助JS把异步调用的返回函数存放在消息队列当中,依赖于Event Loop进行轮训,当Call Stack一空,就把任务从Message Queue中取出。

这样的执行方式也决定了,在一个函数当中,一个异步操作即使是一开头就被执行了,它的回调函数还是必须等到所有同步操作完成了之后才会被调用。

这里也涉及到了另一个问题,就是如果有多个异步操作,那么它们的回调函数执行顺序是怎么样的呢?
简洁的答案是:如果所有的异步回调函数都在同一个队列之中,如Message Queue,那么FIFO先进先出。但是实际上有许多不同的队列,对应了不同的执行优先级,例如 micro-task queue macro-task queuemicro-task queue 当中的任务就会被优先执行。Promise就是基于micro-task queue实现的,而setTimeout是基于macro-task queue实现的。所以Promise的回调函数会比setTimeout的回调函数先执行。具体不同优先级的Queue日后有机会再说说。

Reference

相关文章

  • JavaScript的异步机制 Asynchronous

    JavaScript是一个单线程的编程语言,即便它包含了 await 和 async 的关键字也改变不了这个事实。...

  • AJAX

    AJAX:Asynchronous javascript And XML 异步的javascript和xml 异步...

  • Ajax

    Asynchronous JavaScript and XML (异步的 JavaScript and XML )...

  • ajax:load()加载静态资源

    AJAX = 异步 JavaScript 和 XML (Asynchronous JavaScript ...

  • 对 Ajax 的一次复盘

    Ajax = Asynchronous JavaScript and XML 即为异步的 JavaScript ...

  • Ajax和Json初步了解

    Ajax Asynchronous Javascript and XMLajax = 异步的Javascript+...

  • ajax之换一换的功能实现

    介绍:ajax = Asynchronous JavaScript and XML(异步的 JavaScript ...

  • Ajax

    简介 AJAX = Asynchronous JavaScript and XML(异步的 JavaScript ...

  • Ajax和Json基础

    AJAX: 概念: ASynchronous JavaScript And XML 异步的JavaScript ...

  • jquery与ajax

    AJAX? AJAX = 异步 JavaScript 和 XML(Asynchronous JavaScript ...

网友评论

      本文标题:JavaScript的异步机制 Asynchronous

      本文链接:https://www.haomeiwen.com/subject/ltrppctx.html