关于JavaScript运行机制的一些总结
栏目: JavaScript · 发布时间: 5年前
内容简介:说如果你看到这段代码,并知道正确的输出顺序。那说明你对这块掌握的差不多了。(直接翻到最后看结果)好,言归正传。
说 javascript
运行机制之前,先看一段代码:
console.log(1) Promise.resolve().then(function () { console.log(2) }) new Promise(function(resolve, reject){ console.log(3) resolve() }).then(function () { console.log(4) setTimeout(function () { console.log(5) }) }) console.log(6) setTimeout(function () { Promise.resolve().then(function () { console.log(7) setTimeout(function () { console.log(8) }) }) })
如果你看到这段代码,并知道正确的输出顺序。那说明你对这块掌握的差不多了。(直接翻到最后看结果)
好,言归正传。
在说是怎么运行的之前,先看几个概念。
执行上下文(Execution Context)
执行上下文简单来说就是一个执行环境。它有全局环境、函数环境和eval函数环境之分。它会在 javascript
引擎执行你的脚本的时候去创建。
执行栈(Execution Stack)
执行栈也就是常说的调用栈,它是一种拥有LIFO(后进先出)的数据结构。它会存储代码运行时创建的执行上下文
微任务(micro task)与宏任务(macro task)
javasript
中的任务分为微任务和宏任务两种,这两种任务的执行时机是不同的,因此区分js中哪些是宏任务,哪些是微任务则十分重要。我们常见的宏任务有: script任务
、 setTimeout
、 ajax
等,常见的微任务比较典型的是: Promise.resolve().then()
、 process.nextTick
、 MutationObserver
等。
事件循环(event loop)
js是单线程的,也就是说它一次仅能处理一个任务。但js所在的宿主环境,也就是我们所说的浏览器并不是单线程的(这里宿主环境仅讨论浏览器)。它在遇到一些任务时,比如说 setTimeout
、 event listener
等。它会告诉浏览器:老兄帮个忙,事成后通知我一声,小弟我先干别的事去了。浏览器会回应说:交给我吧,小老弟,事成后我放到任务队列,自己去取啊。于是,js开始执行script任务,执行完了就开始检查有没有微任务啊,没有的话就从任务队列开始取宏任务执行,每执行完一次宏任务,就去看看有没有微任务,有的话就执行完成,再执行宏任务,如此往复。如下:
了解了这几个概念,再来看看 javascript
是怎么执行代码的就比较轻松愉快了。开始吧
console.log(1) Promise.resolve().then(function () { console.log(2) }) new Promise(function(resolve, reject){ console.log(3) resolve() }).then(function () { console.log(4) setTimeout(function () { console.log(5) }) }) console.log(6) setTimeout(function () { Promise.resolve().then(function () { console.log(7) setTimeout(function () { console.log(8) }) }) })
js引擎在执行这段代码的时候,首先将全局执行上下文压入栈中:
然后呢,在执行的时候会碰到 console.log
函数,将它压入栈中:
这个时候,直接执行 console
函数,并输出 1
。然后 console
函数出栈:
继续往下执行,碰到了 Promise.resolve().then()
,先将 Promise.resolve().then()
压入栈中(这里,我为了图方便就把它看成整体了,不然得画很多图)。
然后执行 Promise.resolve().then()
,前面说过,这个 then()
函数是个微任务,它会将传入给它的回调函数加入到微任务队列中。
然后 Promise.resolve().then()
就出栈了。
接着执行,遇到 promise
的构造函数,这个构造函数是一个宏任务,会直接将传递给它的函数压入栈中。
执行 console
函数并输出 3
,执行完, console
函数出栈,接着执行 resolve()
函数,并出栈。
然后继续执行 then
函数,将传递给 then
函数的参数函数放到微任务队列中:
继续来,继续往下执行。碰到 console.log(6)
,二话不说,直接压入栈中,执行,输出 6
,出栈,一气呵成。
接着,引擎碰到了 setTimeout
函数,这家伙是个宏任务,但同时它会将传递给它的函数,加入到任务队列中:
好了,到此第一波宏任务就全部执行完毕。接着,引擎就会去看一下微任务队列中有没有任务,如果有的话,执行它们。
现在看到的是,微任务队列中有两个任务。按照队列的先入先出规则,先从 function () {console.log(2)}
开始执行。先是函数入栈,然后执行函数,输出 2
,然后函数出栈。
接着执行下面这段代码:
console.log(4) setTimeout(function () { console.log(5) })
先从 console.log(4)
开始,先将它入栈,然后执行它,输出 4
,然后函数出栈。
接着执行:
setTimeout(function () { console.log(5) })
将
function () { console.log(5) }
加入到任务队列中去
先执行:
function(){ Promise.resolve().then(function () { console.log(7) setTimeout(function () { console.log(8) }) }) }
这里执行这个函数的时候遇到一个微任务,将这个微任务添加到微任务队列,如下:
这批次的宏任务就执行完毕了,接着就回去检查微任务队列中有没有待执行的任务。一看还真有两个小可爱等待执行,于是没什么好说的,直接拧出去就执行
先是执行 console.log(7)
,然后输出 7
。接着执行 setTimeout
,将传递给他的任务添加到任务队列中去:
最后就剩这两个函数了,按照队列的先入后出一次执行吧,输出 5
和 8
。
好了,最后的结果就是 1,3,6,2,4,7,5,8
。你写对了了吗?
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Markdown 在线编辑器
Markdown 在线编辑器
HSV CMYK 转换工具
HSV CMYK互换工具