博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
深入理解Javascript之Callstack&EventLoop
阅读量:7091 次
发布时间:2019-06-28

本文共 38322 字,大约阅读时间需要 127 分钟。

1.概述

众所周知,Javascript是一个单线程的语言。这意味着,在Javascript中,同一时间只能做一件事情。

这样的设计有一些优点,例如简单,避免了多线程中复杂的状态同步,写程序时不用考虑并发访问。但同时也带来了一些其他问题,其中比较突出的一个问题是:代码逻辑不直观。由于Javascript是单线程的,其中只有一个执行序列。所以,在执行异步操作(例如定时,网络请求这些不能立即完成的操作)时,Javascript运行时不可能在那里等着操作完成。否则整个运行时都被阻塞在那里了,导致其他所有的操作都无法进行,例如网页渲染,用户点击、滚动页面等操作。这样的用户体验是非常糟糕的。

正因为如此,Javascript使用回调函数处理异步操作结果。进行异步操作时传入一个回调,操作完成之后由Javascript引擎执行这个回调,将结果传入。慢慢地,Javascript中充斥着大量的回调。过多的使用回调让一段完整的逻辑被拆分成了很多片段,非常不利于阅读与维护。回调过多的问题在NodeJS中更为突出,故而出现了Promise(见我的前一篇)和async/await

那么异步操作完成时,Javascript运行时是怎样感知到并调用对应的回调函数的呢?

答案是EventLoop(事件循环)。

要了解EventLoop是怎样运作的,我们首先需要了解Javascript是怎样处理一个个任务,调用一个个函数的。这就是CallStack(调用栈)所做的事。

2.调用栈

相信有过其他语言编程经验的读者都听说过CallStack的概念。Javascript中的CallStack类似。

CallStack是一个栈结构,栈的特点是LIFO(后入先出),出栈入栈只会在一端(也就是栈顶)进行。

CallStack是用来处理函数调用与返回的。每次调用一个函数,Javascript运行时会生成一个新的调用结构压入CallStack。而函数调用结束返回时,JavaScript运行时会将栈顶的调用结构弹出。由于栈的LIFO特性,每次弹出的必然是最新调用的那个函数的结构。

Javascript启动时,从文件或标准输入加载程序。加载完成时,Javascript运行时会生成一个匿名的函数,函数体就是输入的代码。这个函数就有点类似于C/C++中的main()函数,是我们的入口函数。我们姑且称之为<main>函数。Javascript启动时,首先调用就是<main>函数。看下面代码:

function func1() {    console.log('in function1');}function func2() {    func1();    console.log('in function2');}function func3() {    func2();    console.log('in function3');}func3();复制代码

上面代码很好理解,我们来看看Javascript是如何运行这段代码的。

Javascript首先加载代码,创建一个匿名<main>包裹这段代码并调用该函数。<main>函数执行,依次定义函数func1func2func3,然后调用函数func3。为func3创建调用结构并压栈。函数func3中调用func2,为func2创建调用结构并压栈。函数func2中调用func1,为func1创建调用结构并压栈。这个过程中,CallStack的变化如下。

然后,函数func1执行完成,从栈顶弹出调用结构。然后func2继续执行,func2执行完成后从栈顶弹出其调用结构。然后func3继续执行,func3执行完成后从栈顶弹出其调用结构。这个过程中,CallStack的变化如下。

当然,我这里有一个地方不太严谨。不知道读者有没有注意到,console.log也是函数函数哦。所以在func1中调用console.log时,CallStack上也会有对应的调用堆栈。func2func3中的console.log调用同样如此。有兴趣的话,可以自己画一画完整的调用流程,这样可以加深理解?。

这里我推荐大家使用Google Chrome的开发者工具来帮助我们理解CallStack。下图是上面代码在开发者工具中的一步步执行的结果:

  • Chrome中<main>称为<anonymous>

  • 单步执行时,重点观察右侧工具栏中Call Stack一栏的变化。

在CallStack中执行的函数,我们称之为一个task(任务)。

接下来,我们来思考这样一个问题:setTimeoutsetIntervalAJAX请求这些功能是怎么实现的?

一位牛人曾经将V8引擎(Chrome内置的Javascript引擎)源码下载下来,然后用grep查找,发现源码中并没有实现这些函数的代码?。

那么这些函数到底是如何实现的,又是如何与Javascript引擎交互的呢?

答案是:宿主(网页中指的是浏览器,Node中指的是Node引擎)提供实现,并在操作完成时将结果(异步的,会有延迟)放入Javascript引擎的task队列,由Javascript引擎处理。

3.事件循环

EventLoop顾名思义,其实就是Javascript引擎中的一个循环,它就是一个不停地从任务队列(task queue)中取出任务执行的过程。

我们前面详细了解了CallStack以及Javascript启动时是如何处理的。但是<main>退出后,Javascript引擎就没事做了吗?当然不是,有很多任务会不定时的触发需要Javascript引擎去处理。例如,用户点击按钮,定时器,页面渲染等。

其实,Javascript引擎中维护着一个任务队列。当CallStack中没有任务在执行时,引擎会从任务队列中取出任务压入CallStack处理。我们通过代码来具体看看(引用):

setTimeout(() => {    console.log('hi');}, 1000);复制代码

我们的Js代码,call stack,task queue和Web APIs(浏览器中实现)关系如下:

[code]        |   [call stack]    | [task queue] | |    [Web APIs] |  --------------------|-------------------|--------------| |---------------|  setTimeout(() => {  |                   |              | |               |    console.log('hi') |                   |              | |               |  }, 1000)            |                   |              | |               |                      |                   |              | |               |复制代码

开始时,代码未执行,所有都是空的。

[code]        |   [call stack]    | [task queue] | |   [Web APIs]  |  --------------------|-------------------|--------------| |---------------|  setTimeout(() => {  | 
| | | | console.log('hi') | | | | | }, 1000) | | | | | | | | | |复制代码

开始执行代码,压入我们的<main>函数。

[code]        |   [call stack]    | [task queue] | |   [Web APIs]  |  --------------------|-------------------|--------------| |---------------|> setTimeout(() => {  | 
| | | | console.log('hi') | setTimeout | | | | }, 1000) | | | | | | | | | |复制代码

执行第一行代码,调用函数setTimeout。我们前面说过,每个函数调用都会创建一个新的调用记录压到栈上。

[code]        |   [call stack]    | [task queue] | |   [Web APIs]  |  --------------------|-------------------|--------------| |---------------|  setTimeout(() => {  | 
| | | timeout, 1000 | console.log('hi') | | | | | }, 1000) | | | | | | | | | |复制代码

setTimeout执行完成,从栈中移除对应调用记录。Web APIs记录超时和回调,超时机制由浏览器实现。

[code]        |   [call stack]    | [task queue] | |   [Web APIs]  |  --------------------|-------------------|--------------| |---------------|  setTimeout(() => {  |                   |              | | timeout, 1000 |    console.log('hi') |                   |              | |               |  }, 1000)            |                   |              | |               |                      |                   |              | |               |复制代码

代码中没有其他逻辑,<main>函数结束,从栈移除调用信息。

[code]        |   [call stack]    | [task queue] | |   [Web APIs]  |  --------------------|-------------------|--------------| |---------------|  setTimeout(() => {  |                   | function   <-----timeout, 1000 |    console.log('hi') |                   |              | |               |  }, 1000)            |                   |              | |               |                      |                   |              | |               |复制代码

超时时间到了,Web APIs将回调放入task queue中。

[code]        |   [call stack]    | [task queue] | |   [Web APIs]  |  --------------------|-------------------|--------------| |---------------|  setTimeout(() => {  | function        <---function     | |               |    console.log('hi') |                   |              | |               |  }, 1000)            |                   |              | |               |                      |                   |              | |               |复制代码

EventLoop检测到Javascript没有任务处理,从task queue中取出任务执行。

[code]        |   [call stack]    | [task queue] | |   [Web APIs]  |  --------------------|-------------------|--------------| |---------------|  setTimeout(() => {  | function          |              | |               |>   console.log('hi') | console.log       |              | |               |  }, 1000)            |                   |              | |               |                      |                   |              | |               |复制代码

执行该回调函数,回调函数中又调用了console.log函数。

[code]        |   [call stack]    | [task queue] | |   [Web APIs]  |  --------------------|-------------------|--------------| |---------------|  setTimeout(() => {  | function          |              | |               |    console.log('hi') |                   |              | |               |  }, 1000)            |                   |              | |               |                      |                   |              | |               |> hi复制代码

console.log执行完成,输出"hi"。

[code]        |   [call stack]    | [task queue] | |   [Web APIs]  |  --------------------|-------------------|--------------| |---------------|  setTimeout(() => {  |                   |              | |               |    console.log('hi') |                   |              | |               |  }, 1000)            |                   |              | |               |                      |                   |              | |               |> hi复制代码

回调函数执行完成,CallStack再次为空。

上面就是setTimeout的执行过程,从中开始看出EventLoop在幕后做的工作。

下面我们再来看一段代码:

console.log("start");setTimeout(() => {    console.log("timeout");}, 0);Promise.resolve()  .then(() => {      console.log("promise1");  })  .then(() => {      console.log("promise2");  });console.log("end");复制代码

这段程序的输出是什么?建议大家先思考一下,最好能动笔画一画图?。

4.微任务队列

接着上一节的代码,符合标准(很多旧版本的浏览器实现都是不符合标准的,具体参见参考链接)的输出应该是:

startendpromise1promise2timeout复制代码

但是,为?什?么?

实际上,Javascript中有另外一种队列。Promise的回调是被放入这个队列的。这个队列叫做microtask queue(微任务队列),ES6标准中叫。microTask的优先级是比task高的,也就是说microtask队列中的任务要先处理。

  • EventLoop检测到当前没有任务在执行,首先检查microtask队列中有没有需要处理的任务。如果有那么一个个执行,直到microtask队列为空。

  • microtask队列中没有任务了,执行task队列中的任务。这里需要注意,**每执行一个task队列中的任务,就检查一下microtask队列状态。将microtask队列中所有任务都执行完成之后,再从task队列中取出任务执行。

下面我们看看上面那段代码是怎么一步步执行的:

为方便起见我们称setTimeout回调为timeoutcb,称第一个then成功回调为promisecb1,第二个then回调为promisecb2

[code]                    |   [call stack]    | [task queue] | | [microtask queue] | |   [Web APIs]  |  --------------------------------|-------------------|--------------| |-------------------| |---------------|  1. console.log("start");        |                   |              | |                   | |               |  2.                              |                   |              | |                   | |               |  3. setTimeout(() => {           |                   |              | |                   | |               |  4.     console.log("timeout");  |                   |              | |                   | |               |  5. }, 0);                       |                   |              | |                   | |               |  6.                              |                   |              | |                   | |               |  7. Promise.resolve()            |                   |              | |                   | |               |  8. .then(() => {                |                   |              | |                   | |               |  9.     console.log("promise1"); |                   |              | |                   | |               |  10.})                           |                   |              | |                   | |               |  11..then(() => {                |                   |              | |                   | |               |  12.    console.log("promise2"); |                   |              | |                   | |               |  13.});                          |                   |              | |                   | |               |  14.                             |                   |              | |                   | |               |  15.console.log("end");          |                   |              | |                   | |               |复制代码

开始时,call stack、task queue、microtask queue都为空。

[code]                     |   [call stack]    | [task queue] | | [microtask queue] | |   [Web APIs]  |  ---------------------------------|-------------------|--------------| |-------------------| |---------------|> 1. console.log("start");         |     
| | | | | | 2. | console.log | | | | | | 3. setTimeout(() => { | | | | | | | 4. console.log("timeout"); | | | | | | | 5. }, 0); | | | | | | | 6. | | | | | | | 7. Promise.resolve() | | | | | | | 8. .then(() => { | | | | | | | 9. console.log("promise1"); | | | | | | | 10. }) | | | | | | | 11. .then(() => { | | | | | | | 12. console.log("promise2"); | | | | | | | 13. }); | | | | | | | 14. | | | | | | | 15. console.log("end"); | | | | | | |复制代码

程序开始运行,压入<main>函数。首先执行第一行代码,console.log("start"),将console.log压栈。

[code]                     |   [call stack]    | [task queue] | | [microtask queue] | |   [Web APIs]  |  ---------------------------------|-------------------|--------------| |-------------------| |---------------|> 1. console.log("start");         |     
| | | | | | 2. | | | | | | | 3. setTimeout(() => { | | | | | | | 4. console.log("timeout"); | | | | | | | 5. }, 0); | | | | | | | 6. | | | | | | | 7. Promise.resolve() | | | | | | | 8. .then(() => { | | | | | | | 9. console.log("promise1"); | | | | | | | 10. }) | | | | | | | 11. .then(() => { | | | | | | | 12. console.log("promise2"); | | | | | | | 13. }); | | | | | | | 14. | | | | | | | 15. console.log("end"); | | | | | | |> start复制代码

console.log执行完成,输出"start"。

[code]                     |   [call stack]    | [task queue] | | [microtask queue] | |   [Web APIs]  |  ---------------------------------|-------------------|--------------| |-------------------| |---------------|  1. console.log("start");         |    
| | | | | | 2. | setTimeout | | | | | |> 3. setTimeout(() => { | | | | | | | 4. console.log("timeout"); | | | | | | | 5. }, 0); | | | | | | | 6. | | | | | | | 7. Promise.resolve() | | | | | | | 8. .then(() => { | | | | | | | 9. console.log("promise1"); | | | | | | | 10. }) | | | | | | | 11. .then(() => { | | | | | | | 12. console.log("promise2"); | | | | | | | 13. }); | | | | | | | 14. | | | | | | | 15. console.log("end"); | | | | | | |> start复制代码

第二行为空跳过,开始执行第三行代码,setTimeout压栈。

[code]                     |   [call stack]    | [task queue] | | [microtask queue] | |   [Web APIs]  |  ---------------------------------|-------------------|--------------| |-------------------| |---------------|  1. console.log("start");         |    
| timeoutcb <------------------------~~timeoutcb, 0~~| 2. | | | | | | |> 3. setTimeout(() => { | | | | | | | 4. console.log("timeout"); | | | | | | | 5. }, 0); | | | | | | | 6. | | | | | | | 7. Promise.resolve() | | | | | | | 8. .then(() => { | | | | | | | 9. console.log("promise1"); | | | | | | | 10. }) | | | | | | | 11. .then(() => { | | | | | | | 12. console.log("promise2"); | | | | | | | 13. }); | | | | | | | 14. | | | | | | | 15. console.log("end"); | | | | | | |> start复制代码

setTimeout执行完成,由于超时是0,所以立即回调立即进入task queue中。

[code]                     |   [call stack]    | [task queue] | | [microtask queue] | |   [Web APIs]  |  ---------------------------------|-------------------|--------------| |-------------------| |---------------|  1. console.log("start");         |    
| timeoutcb | | promisecb1 | | | 2. | | | | | | | 3. setTimeout(() => { | | | | | | | 4. console.log("timeout"); | | | | | | | 5. }, 0); | | | | | | | 6. | | | | | | |> 7. Promise.resolve() | | | | | | | 8. .then(() => { | | | | | | | 9. console.log("promise1"); | | | | | | | 10. }) | | | | | | | 11. .then(() => { | | | | | | | 12. console.log("promise2"); | | | | | | | 13. }); | | | | | | | 14. | | | | | | | 15. console.log("end"); | | | | | | |> start复制代码

代码执行到第7行:

  • 首先Promise.resolve压栈,执行完成后返回一个Promise对象。

  • 然后调用该对象的then方法,该方法压栈,执行完成后返回一个全新的Promise对象,我们称该对象为promise1。由于Promise.resolve返回对象的状态为resolved,所以promise1回调直接进入microtask队列

  • 接着又执行新对象的then方法,我们称该对象为promise2

[code]                     |   [call stack]    | [task queue] | | [microtask queue] | |   [Web APIs]  |  ---------------------------------|-------------------|--------------| |-------------------| |---------------|  1. console.log("start");         |    
| timeout | | promisecb1 | | | 2. | console.log | | | | | | 3. setTimeout(() => { | | | | | | | 4. console.log("timeout"); | | | | | | | 5. }, 0); | | | | | | | 6. | | | | | | | 7. Promise.resolve() | | | | | | | 8. .then(() => { | | | | | | | 9. console.log("promise1"); | | | | | | | 10. }) | | | | | | | 11. .then(() => { | | | | | | | 12. console.log("promise2"); | | | | | | | 13. }); | | | | | | | 14. | | | | | | |> 15. console.log("end"); | | | | | | |> start复制代码

代码执行到第15行,console.log压栈。

[code]                     |   [call stack]    | [task queue] | | [microtask queue] | |   [Web APIs]  |  ---------------------------------|-------------------|--------------| |-------------------| |---------------|  1. console.log("start");         |    
| timeout | | promisecb1 | | | 2. | | | | | | | 3. setTimeout(() => { | | | | | | | 4. console.log("timeout"); | | | | | | | 5. }, 0); | | | | | | | 6. | | | | | | | 7. Promise.resolve() | | | | | | | 8. .then(() => { | | | | | | | 9. console.log("promise1"); | | | | | | | 10. }) | | | | | | | 11. .then(() => { | | | | | | | 12. console.log("promise2"); | | | | | | | 13. }); | | | | | | | 14. | | | | | | |> 15. console.log("end"); | | | | | | |> start> end复制代码

console.log("end")执行完成,输出"end",出栈。

[code]                     |   [call stack]    | [task queue] | | [microtask queue] | |   [Web APIs]  |  ---------------------------------|-------------------|--------------| |-------------------| |---------------|  1. console.log("start");         |                   |    timeout   | |     promisecb1    | |               |  2.                               |                   |              | |                   | |               |  3. setTimeout(() => {            |                   |              | |                   | |               |  4.     console.log("timeout");   |                   |              | |                   | |               |  5. }, 0);                        |                   |              | |                   | |               |  6.                               |                   |              | |                   | |               |  7. Promise.resolve()             |                   |              | |                   | |               |  8.  .then(() => {                |                   |              | |                   | |               |  9.     console.log("promise1");  |                   |              | |                   | |               |  10. })                           |                   |              | |                   | |               |  11. .then(() => {                |                   |              | |                   | |               |  12.    console.log("promise2");  |                   |              | |                   | |               |  13. });                          |                   |              | |                   | |               |  14.                              |                   |              | |                   | |               |> 15. console.log("end");          |                   |              | |                   | |               |> start> end复制代码

<main>函数没有逻辑需要执行了,出栈。

[code]                     |   [call stack]    | [task queue] | | [microtask queue] | |   [Web APIs]  |  ---------------------------------|-------------------|--------------| |-------------------| |---------------|  1. console.log("start");         |    promisecb1     |    timeout   | |                   | |               |  2.                               |                   |              | |                   | |               |  3. setTimeout(() => {            |                   |              | |                   | |               |  4.     console.log("timeout");   |                   |              | |                   | |               |  5. }, 0);                        |                   |              | |                   | |               |  6.                               |                   |              | |                   | |               |  7. Promise.resolve()             |                   |              | |                   | |               |  8.  .then(() => {                |                   |              | |                   | |               |  9.     console.log("promise1");  |                   |              | |                   | |               |  10. })                           |                   |              | |                   | |               |  11. .then(() => {                |                   |              | |                   | |               |  12.    console.log("promise2");  |                   |              | |                   | |               |  13. });                          |                   |              | |                   | |               |  14.                              |                   |              | |                   | |               |> 15. console.log("end");          |                   |              | |                   | |               |> start> end复制代码

EventLoop检查到microtask队列中有任务需要执行,将promisecb1取出压入call stack。

[code]                     |   [call stack]    | [task queue] | | [microtask queue] | |   [Web APIs]  |  ---------------------------------|-------------------|--------------| |-------------------| |---------------|  1. console.log("start");         |                   |    timeout   | |      promisecb2   | |               |  2.                               |                   |              | |                   | |               |  3. setTimeout(() => {            |                   |              | |                   | |               |  4.     console.log("timeout");   |                   |              | |                   | |               |  5. }, 0);                        |                   |              | |                   | |               |  6.                               |                   |              | |                   | |               |  7. Promise.resolve()             |                   |              | |                   | |               |  8.  .then(() => {                |                   |              | |                   | |               |  9.     console.log("promise1");  |                   |              | |                   | |               |  10. })                           |                   |              | |                   | |               |  11. .then(() => {                |                   |              | |                   | |               |  12.    console.log("promise2");  |                   |              | |                   | |               |  13. });                          |                   |              | |                   | |               |  14.                              |                   |              | |                   | |               |  15. console.log("end");          |                   |              | |                   | |               |> start> end> promise1复制代码

promisecb1执行完成,输出"promise1",并且返回undefined。(其实在这里还有一个console.log压栈出栈的过程,我就不画了,下同)

在中看到,如果返回一个值,那么对象立刻变为resolved所以第二个then的回调需要安排执行,进入microtask队列

[code]                     |   [call stack]    | [task queue] | | [microtask queue] | |   [Web APIs]  |  ---------------------------------|-------------------|--------------| |-------------------| |---------------|  1. console.log("start");         |     promisecb2    |    timeout   | |                   | |               |  2.                               |                   |              | |                   | |               |  3. setTimeout(() => {            |                   |              | |                   | |               |  4.     console.log("timeout");   |                   |              | |                   | |               |  5. }, 0);                        |                   |              | |                   | |               |  6.                               |                   |              | |                   | |               |  7. Promise.resolve()             |                   |              | |                   | |               |  8.  .then(() => {                |                   |              | |                   | |               |  9.     console.log("promise1");  |                   |              | |                   | |               |  10. })                           |                   |              | |                   | |               |  11. .then(() => {                |                   |              | |                   | |               |  12.    console.log("promise2");  |                   |              | |                   | |               |  13. });                          |                   |              | |                   | |               |  14.                              |                   |              | |                   | |               |  15. console.log("end");          |                   |              | |                   | |               |> start> end> promise1复制代码

EventLoop检测到call stack中没有正在执行的任务,同时microtask队列不为空。从microtask队列取出任务压入call stack。

[code]                     |   [call stack]    | [task queue] | | [microtask queue] | |   [Web APIs]  |  ---------------------------------|-------------------|--------------| |-------------------| |---------------|  1. console.log("start");         |                   |    timeout   | |                   | |               |  2.                               |                   |              | |                   | |               |  3. setTimeout(() => {            |                   |              | |                   | |               |  4.     console.log("timeout");   |                   |              | |                   | |               |  5. }, 0);                        |                   |              | |                   | |               |  6.                               |                   |              | |                   | |               |  7. Promise.resolve()             |                   |              | |                   | |               |  8.  .then(() => {                |                   |              | |                   | |               |  9.     console.log("promise1");  |                   |              | |                   | |               |  10. })                           |                   |              | |                   | |               |  11. .then(() => {                |                   |              | |                   | |               |  12.    console.log("promise2");  |                   |              | |                   | |               |  13. });                          |                   |              | |                   | |               |  14.                              |                   |              | |                   | |               |  15. console.log("end");          |                   |              | |                   | |               |> start> end> promise1> promise2复制代码

promisecb2执行完成,输出"promise2"。

[code]                     |   [call stack]    | [task queue] | | [microtask queue] | |   [Web APIs]  |  ---------------------------------|-------------------|--------------| |-------------------| |---------------|  1. console.log("start");         |    timeoutcb      |              | |                   | |               |  2.                               |                   |              | |                   | |               |  3. setTimeout(() => {            |                   |              | |                   | |               |  4.     console.log("timeout");   |                   |              | |                   | |               |  5. }, 0);                        |                   |              | |                   | |               |  6.                               |                   |              | |                   | |               |  7. Promise.resolve()             |                   |              | |                   | |               |  8.  .then(() => {                |                   |              | |                   | |               |  9.     console.log("promise1");  |                   |              | |                   | |               |  10. })                           |                   |              | |                   | |               |  11. .then(() => {                |                   |              | |                   | |               |  12.    console.log("promise2");  |                   |              | |                   | |               |  13. });                          |                   |              | |                   | |               |  14.                              |                   |              | |                   | |               |  15. console.log("end");          |                   |              | |                   | |               |> start> end> promise1> promise2复制代码

接着,EventLoop检测到call stack和microtask队列都为空,从task队列中取出timeoutcb压入栈。

[code]                     |   [call stack]    | [task queue] | | [microtask queue] | |   [Web APIs]  |  ---------------------------------|-------------------|--------------| |-------------------| |---------------|  1. console.log("start");         |                   |              | |                   | |               |  2.                               |                   |              | |                   | |               |  3. setTimeout(() => {            |                   |              | |                   | |               |  4.     console.log("timeout");   |                   |              | |                   | |               |  5. }, 0);                        |                   |              | |                   | |               |  6.                               |                   |              | |                   | |               |  7. Promise.resolve()             |                   |              | |                   | |               |  8.  .then(() => {                |                   |              | |                   | |               |  9.     console.log("promise1");  |                   |              | |                   | |               |  10. })                           |                   |              | |                   | |               |  11. .then(() => {                |                   |              | |                   | |               |  12.    console.log("promise2");  |                   |              | |                   | |               |  13. });                          |                   |              | |                   | |               |  14.                              |                   |              | |                   | |               |  15. console.log("end");          |                   |              | |                   | |               |> start> end> promise1> promise2> timeout复制代码

timeoutcb执行完成,输出"timeout"。

5.总结

通过这篇文章,我们了解到Javascript时如何通过call stack来处理函数的调用与返回的。setTimeout等异步机制其实是宿主提供实现,并在异步操作完成负责将回调放入任务队列,最后由EventLoop在适合的时机取出压入call stack实际执行。

我们还看到了另外一种队列——microtask队列。该队列中存放的一般是优先级较高的任务,例如Promise的回调处理函数。

每当call stack中没有正在执行的任务时,EventLoop会优先从microtask队列中取出任务执行,当该队列为空时才会从task队列取。

在使用一门框架或语言时,对于是否需要了解底层运作机制和原理,往往会有比较大的争论。有人说,我不了解内部原理同样可以写出好程序,那为什么还需要花时间去研究呢? 对此,我觉得了解底层原理还是非常有必要的。有下面几个好处:

  • 可以让我们看到全貌,了解整个系统是如何运作的。

  • 底层原理大多是相通的,例如几乎所有语言的函数调用底层都是利用CallStack来实现的。学会了Javascript的CallStack运作机制,在学习其他语言的相关概念时往往能事半功倍。

  • 了解底层可以让我们心中有数,明白什么事情能做,什么事情不能做。例如Javascript是单线程的,我们写代码时一定不能让线程阻塞了?。

  • 了解底层可以让我们更好的优化代码。当程序性能出现瓶颈时,可以更快地定位问题。

6.参考链接

关于我:

转载地址:http://uqbql.baihongyu.com/

你可能感兴趣的文章
从程序员入门到“第一个项目”的一些事
查看>>
转-Pentaho技术白皮书中文版(三)--构建新组件
查看>>
SpringSrcureCode在grails中实现用户--角色--权限的管理
查看>>
java Servlet 下载 itext 生成的2003 word 文档(java生成word文档3)
查看>>
Delphi 查找标题已知的窗口句柄,遍历窗口控件句柄(转)
查看>>
单例模式
查看>>
最锋利的jQuery源码、电子书及视频教程合集(共46个)
查看>>
JavaScript 内置对象!
查看>>
解决ubuntu下打不开rar文件
查看>>
内核启动过程
查看>>
在使用ibatis实现多条件模糊查询的语句
查看>>
童宁_下一代数据中心的安全挑战
查看>>
android 3g状态及信号监测
查看>>
开源 java CMS - FreeCMS2.8 站点管理
查看>>
JSP中include指令和include行为区别
查看>>
关于zend studio 9.0版本汉化
查看>>
java web编程学习6
查看>>
CSS学习笔记——最基础的定义与使用
查看>>
形象的网络状况工具
查看>>
linux下mysql的root密码忘记解决方法
查看>>