资讯专栏INFORMATION COLUMN

关于JavaScript运行机制的一些总结

TNFE / 1750人阅读

摘要:说运行机制之前,先看一段代码如果你看到这段代码,并知道正确的输出顺序。执行栈执行栈也就是常说的调用栈,它是一种拥有后进先出的数据结构。事件循环是单线程的,也就是说它一次仅能处理一个任务。好了,最后的结果就是。

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任务setTimeoutajax等,常见的微任务比较典型的是:Promise.resolve().then()process.nextTickMutationObserver等。

事件循环(event loop)

js是单线程的,也就是说它一次仅能处理一个任务。但js所在的宿主环境,也就是我们所说的浏览器并不是单线程的(这里宿主环境仅讨论浏览器)。它在遇到一些任务时,比如说setTimeoutevent 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,将传递给他的任务添加到任务队列中去:

最后就剩这两个函数了,按照队列的先入后出一次执行吧,输出58

好了,最后的结果就是1,3,6,2,4,7,5,8。你写对了了吗?

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/101033.html

相关文章

  • 理解JavaScript核心知识点:This

    摘要:关键字计算为当前执行上下文的属性的值。毫无疑问它将指向了这个前置的对象。构造函数也是同理。严格模式无论调用位置,只取显式给定的上下文绑定的,通过方法传入的第一参数,否则是。其实并不属于特殊规则,是由于各种事件监听定义方式本身造成的。 this 是 JavaScript 中非常重要且使用最广的一个关键字,它的值指向了一个对象的引用。这个引用的结果非常容易引起开发者的误判,所以必须对这个关...

    TerryCai 评论0 收藏0
  • 原理解释 - 收藏集 - 掘金

    摘要:巧前端基础进阶全方位解读前端掘金我们在学习的过程中,由于对一些概念理解得不是很清楚,但是又想要通过一些方式把它记下来,于是就很容易草率的给这些概念定下一些方便自己记忆的有偏差的结论。 计算机程序的思维逻辑 (83) - 并发总结 - 掘金从65节到82节,我们用了18篇文章讨论并发,本节进行简要总结。 多线程开发有两个核心问题,一个是竞争,另一个是协作。竞争会出现线程安全问题,所以,本...

    AlphaGooo 评论0 收藏0
  • 原理解释 - 收藏集 - 掘金

    摘要:巧前端基础进阶全方位解读前端掘金我们在学习的过程中,由于对一些概念理解得不是很清楚,但是又想要通过一些方式把它记下来,于是就很容易草率的给这些概念定下一些方便自己记忆的有偏差的结论。 计算机程序的思维逻辑 (83) - 并发总结 - 掘金从65节到82节,我们用了18篇文章讨论并发,本节进行简要总结。 多线程开发有两个核心问题,一个是竞争,另一个是协作。竞争会出现线程安全问题,所以,本...

    forrest23 评论0 收藏0
  • 前端窝 - 收藏集 - 掘金

    摘要:毫无疑问,设计模式于己于他人于系统都是多赢的设计模式使代码编写真正工程化设计模小书前端掘金这是一本关于的小书。 JavaScript 常见设计模式解析 - 掘金设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。毫无疑问,设计模式于己于他人于系统都是多赢的;设计...

    李文鹏 评论0 收藏0
  • JavasScript重难点知识

    摘要:忍者级别的函数操作对于什么是匿名函数,这里就不做过多介绍了。我们需要知道的是,对于而言,匿名函数是一个很重要且具有逻辑性的特性。通常,匿名函数的使用情况是创建一个供以后使用的函数。 JS 中的递归 递归, 递归基础, 斐波那契数列, 使用递归方式深拷贝, 自定义事件添加 这一次,彻底弄懂 JavaScript 执行机制 本文的目的就是要保证你彻底弄懂javascript的执行机制,如果...

    forsigner 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<