JavaScript执行机制

几道常见面试题来看 JavaScript 执行机制

前面的话

根据 JavaScript 的运行环境,锁定它为单线程,任务需要排队执行,如果网站资源比较大,这样会导致浏览器加载会很慢,但实际上并没有,大家肯定立刻想到了同步和异步。

所谓的同步和异步也是在排队,只是排队的地方不同。

同步和异步

同步任务进入主线程排队,异步任务进入事件队列中排队

同步任务和异步任务进入到不同的队列中,也就是上面讲的在不同地方排队。

同步任务进入主线程,异步任务进入事件队列,主线程任务执行完毕,事件队列中有等待执行的任务进入主线程执行,直到事件队列中任务全部执行完毕。

开胃菜

console.log('a')

setTimeout(function(){
  console.log('b')
}, 200)

setTimeout(function(){
  console.log('c')
}, 0)

console.log('d')

结果:a d c b

从上到下,该进入主线程的进入主线程,该进入事件队列的进入事件队列。

那么主线程中存在 console.log('a')console.log('d'),定时器 setTimeout 延迟一段时间执行,顾名思义异步任务进入事件队列中,等待主线程任务执行完毕,再进入主线程执行。

定时器的延迟时间为 0 并不是立刻执行,只是代表相比于其他定时器更早的进入主线程中执行。

加一盘

for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i)
    }, 1000)
}

结果:十个10

每次 for 循环遇到 setTimeout 将其放入事件队列中等待执行,直到全部循环结束,i 作为全局变量当循环结束后 i = 10 ,再来执行 setTimeouti 的值已经为 10 , 结果为十个10。

var 改为 let ,变量作用域不同,let 作用在当前循环中,所以进入事件队列的定时器每次的 i 不同,最后打印结果会是 0 1 2...9。

宏任务 微任务

除了经常说的同步任务和异步任务之外,更可分为宏任务,微任务

主要宏任务:整段脚本script setTimeout setTimeout...

主要微任务:promise.then...

执行流程:

1.整段脚本script作为宏任务开始执行

  1. 遇到微任务将其推入微任务队列,宏任务推入宏任务队列

  2. 宏任务执行完毕,检查有没有可执行的微任务

  3. 发现有可执行的微任务,将所有微任务执行完毕

  4. 开始新的宏任务,反复如此直到所有任务执行完毕

来一盘Promise

const p = new Promise(resolve => {
  console.log('a')
  resolve()
  console.log('b')
})

p.then(() => {
  console.log('c')
})

console.log('d')

结果:a b d c

  1. 整段script进入宏任务队列开始执行,

  2. promise 创建立即执行,打印 a b

  3. 遇到 promise.then 进入微任务队列,

  4. 遇到 console.log('d') 打印 d

  5. 整段代码作为宏任务执行完毕,有可执行的微任务,开始执行微任务,打印 c

setTimeout(function(){
  console.log('setTimeout')
}, 0)

const p = new Promise(resolve => {
  console.log('a')
  resolve()
  console.log('b')
})

p.then(() => {
  console.log('c')
})

console.log('d')

结果:a b d c setTimeout

  1. setTimeout 进入宏任务队列,

  2. promise 创建立即执行,打印 a b

  3. 遇到 promise.then 进入微任务队列,

  4. 遇到 console.log('d') 打印 d

  5. 有可执行的微任务,打印 c

  6. 微任务执行完毕,开始执行新的宏任务,setTimeout 开始执行,打印 setTimeout

setTimeout(function(){
  console.log('setTimeout')
}, 0)

const p = new Promise(resolve => {
  console.log('a')
  resolve()
  console.log('b')
})

p.then(() => {
  console.log('c')
  setTimeout(function(){
    console.log('then中的setTimeout')
  }, 0)
})

console.log('d')

结果:a b d c setTimeout then中的setTimeout

  1. 同上

  2. 执行微任务打印 c,遇到 setTimeout 将其推入宏任务队列中

  3. 定时器延迟时间相同,开始按照顺序执行宏任务,分别打印 setTimeout then中的setTimeout

再加点定时器

console.log('a')

new Promise(resolve => {
    console.log('b')
    resolve()
}).then(() => {
    console.log('c')
    setTimeout(() => {
      console.log('d')
    }, 0)
})

setTimeout(() => {
    console.log('e')
    new Promise(resolve => {
        console.log('f')
        resolve()
    }).then(() => {
        console.log('g')
    })
}, 100)

setTimeout(() => {
    console.log('h')
    new Promise(resolve => {
        resolve()
    }).then(() => {
        console.log('i')
    })
    console.log('j')
}, 0)

结果:a b c h j i d e f g

  1. 打印 a

  2. promise 立即执行,打印 b

  3. promise.then 推入微任务队列

  4. setTimeout 推入宏任务队列

  5. 整段代码执行完毕,开始执行微任务,打印 c ,遇到 setTimeout 推入宏任务队列排队等待执行

  6. 没有可执行的微任务开始执行宏任务,定时器按照延迟时间排队执行

  7. 打印 h jpromise.then 推入微任务队列

  8. 有可执行的微任务,打印 i ,继续执行宏任务,打印 d

  9. 执行延迟为100的宏任务,打印 e f,执行微任务打印 g,所有任务执行完毕

简单测试

console.log('start')

a().then(() => {
  console.log('a_then')
})

console.log('end')

function a() {
  console.log('a_function')
  return b().then((res) => {
    console.log('res', res)
    console.log('b_then')
    return Promise.resolve('a方法的返回值')
  })
}

function b() {
  console.log('b_function')
  return Promise.resolve('返回值')
}

结果:start a_function b_function end res 返回值 b_then a_then

根据上面例子的流程讲解来思考这个,加深理解

总结

  • JavaScript 单线程,任务需要排队执行

  • 同步任务进入主线程排队,异步任务进入事件队列排队等待被推入主线程执行

  • 定时器的延迟时间为 0 并不是立刻执行,只是代表相比于其他定时器更早的被执行

  • 以宏任务和微任务进一步理解Js执行机制

  • 整段代码作为宏任务开始执行,执行过程中宏任务和微任务进入相应的队列中

  • 整段代码执行结束,看微任务队列中是否有任务等待执行,如果有则执行所有的微任务,直到微任务队列中的任务执行完毕,如果没有则继续执行新的宏任务

  • 执行新的宏任务,凡是在执行宏任务过程中遇到微任务都将其推入微任务队列中执行

  • 反复如此直到所有任务全部执行完毕

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

推荐阅读更多精彩内容

  • 事件循环Event Loop JavaScript语言的一大特点就是单线程,作为脚本语言,避免复杂性。因为如果是多...
    ERICOOLU阅读 385评论 0 1
  • 答题大纲 先说基本知识点,宏任务、微任务有哪些 说事件循环机制过程,边说边画图出来 说async/await执行顺...
    小虫000阅读 965评论 0 1
  • 弄懂js异步 讲异步之前,我们必须掌握一个基础知识-event-loop。 我们知道JavaScript的一大特点...
    DCbryant阅读 2,710评论 0 5
  • JavaScript执行机制,重点有两点: 1.JavaScript是一门单线程语言。2.Event Loop(事...
    小泡_08f5阅读 11,764评论 2 22
  • 好久没写了,可能最近有要去折腾,说不上创业,但是给别人打工一直没有安全感,我经常在工作或者下班之余惶恐,我...
    船长阅读 181评论 0 0