JavaScript运行机制的理解

0.前言

又是新的一年啊!


timg.jpeg

1.简介

JavaScript 是脚本语言,JS也是单线程的,因此同一时间只能做一件事,也就是说它一次仅能处理一个任务。

思考题
  • JavaScript为什么是单线程的? 为什么需要异步? 单线程又是如何实现异步的呢?
  • JavaScript中的event loop()
  • 看一段代码,想下正确的输出顺序
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)
        })
    })
})

2.概念

执行上下文(Execution Context)

执行上下文简单来说就是一个执行环境。它有全局环境、函数环境和eval函数环境之分。它会在javascript引擎执行你的脚本的时候去创建。

执行栈(Execution Stack)

执行栈也就是常说的调用栈,它是一种拥有LIFO(后进先出)的数据结构。它会存储代码运行时创建的执行上下文

微任务(micro task)与宏任务(macro task)

javasript中的任务分为微任务和宏任务两种,这两种任务的执行时机是不同的,因此区分js中哪些是宏任务,哪些是微任务则十分重要。我们常见的宏任务有:script任务、setTimeoutajax等,常见的微任务比较典型的是:Promise.resolve().then()process.nextTickMutationObserver等。

事件循环(event loop)

javasript是单线程的,一次仅能处理一个任务。但js所在的宿主环境,也就是我们所说的浏览器并不是单线程的(这里宿主环境仅讨论浏览器)。它在遇到一些任务时,比如说setTimeoutevent listener等。它会告诉浏览器:老兄帮个忙,事成后通知我一声,小弟我先干别的事去了。浏览器会回应说:交给我吧,小老弟,事成后我放到任务队列,自己去取啊。于是,javasript开始执行script任务,执行完了就开始检查有没有微任务啊,没有的话就从任务队列开始取宏任务执行,每执行完一次宏任务,就去看看有没有微任务,有的话就执行完成,再执行宏任务,如此往复。如下图:

1.png

而准确的划分方式是:

  • macro-task(宏任务):包括整体代码scriptsetTimeoutsetInterval
  • micro-task(微任务):Promiseprocess.nextTick

3.回答思考题

JavaScript为什么是单线程的?

因为现在如果有两个任务一个是删除DOM节点,一个是增加DOM节点,浏览器该如何执行?所以JavaScript是单线程

为什么需要异步?

如果JavaScript中不存在异步,只能自上而下执行,如果上一行解析时间很长,那么下面的代码就会被阻塞,不向下执行。
页面出来,用户看到觉得是“卡死了”,所以需要异步。

JavaScript单线程又是如何实现异步的呢?

是通过的事件循环(event loop)实现异步的。

JavaScript中的event loop()

JavaScript的执行机制是

  • 首先判断JavaScript是同步还是异步,同步就进入主线程,异步就进入event table
  • 异步任务在event table中注册函数,当满足触发条件后,被推入event queue
  • 同步任务进入主线程后一直执行,直到主线程空闲时,才会去event queue中查看是否有可执行的异步任务,如果有就推入主线程中

了解了这几个概念,再来看看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)
        })
    })
})

第一波先执行宏任务

  • JavaScript引擎在执行这段代码的时候,首先将全局执行上下文压入栈中,然后呢,在执行的时候会碰到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)
})

上面说过setTimeout是宏任务,加入到任务队列中去。
继续向下执行

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。你写对了吗?

4.总结
最后牢记两点

  • JavaScript是单线程语言。
  • JavaScript的Event Loop是JS的执行机制。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容