浏览器的事件循环机制

Javascript是一个单线程、非阻塞、异步、解释性脚本语言。js的并发模型基于事件循环,Event Loop是由js宿主环境,如浏览器实现的。v8是Chrome里的javascript运行环境,在V8的源码中并不存在setTimeout/DOM/HTTP请求等 ,这些异步请求在浏览器中由webAPI处理,它是由C++实现的浏览器创建的线程。

以下是浏览器中事件循环机制的流程图,只要执行栈中没有代码在执行,微任务会在回调后立即执行。


浏览器事件循环机制流程图

关于宏任务、微任务的说法有争议,ecma-262 中称之为JobsJob Queues,这里对宏任务和微任务的理解是参考 Tasks, microtasks, queues and schedules事件循环处理模型,并且不同浏览器对一些异步事件的执行机制有所不同,这里只考虑Chrome情况下的执行顺序。此外这个视频 到底什么是Event Loop 比较基础地介绍了事件循环机制。

以下各个例子有助于加深理解事件循环机制,可自己思考得出结果后再对比运行结果 。

e.g.1

Promise.resolve().then(function promise1 () {
       console.log('promise1');
    })
setTimeout(function setTimeout1 (){
    console.log('setTimeout1')
    Promise.resolve().then(function  promise2 () {
       console.log('promise2');
    })
}, 0)

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

e.g.2

new Promise(resolve => {
    resolve(1)
    Promise.resolve().then(() => console.log(42)) 
    console.log(4)
}).then(t => console.log(t)) 
console.log(3)

e.g.3

async函数是promise的语法糖,await中的语句相当于在promise.resolve中,相当于同步任务,会被立即加入执行栈;await后的语句相当于在promise.then中,如果微任务中嵌套有宏任务,会在执行到该微任务后再将宏任务进入宏任务队列,从而进入下一轮事件循环

console.log('script start')
async function async1() {
    await async2();
    console.log('async1 end');
    setTimeout(function() {
        console.log('async1 setTimeout')
    }, 0);
}
async function async2() {
    console.log('async2 end');
    setTimeout(function() {
        console.log('async2 setTimeout')
    }, 0);
}
async1();

setTimeout(function() {
    Promise.resolve().then(function() {
        console.log('setTimeout promise');
    })
    console.log('setTimeout');
}, 0);

new Promise(resolve => {
    console.log('Promise')
    resolve()
})
.then(function() {
    console.log('promise1')
})
.then(function() {
    console.log('promise2')
})

console.log('script end')

e.g.4

执行下面 两个例子时会发现与预期有出入,是因为:

  • promise.nextTick是放到当前执行栈的尾部,一定比异步的任务队列早,并不是因为优先级高于其他异步任务。
  • 根据 mdn setTimeout()/setInterval() 的每调用一次定时器的最小间隔是4ms,参考 底层源码,第一个例子中,传入1ms和0ms最后执行的都是1ms,第二个例子中的两个setTimeout延时相同,所以被合入了一个宏任务一起执行。
setTimeout(() => {
    console.log(2)
}, 2)

setTimeout(() => {
    console.log(1)
}, 1)

setTimeout(() => {
    console.log(0)
}, 0)
console.log(1)

setTimeout(() => {
   console.log(2)
   new Promise(resolve => {
       console.log(4)
       resolve()
   }).then(() => {
       console.log(5)
   })
   process.nextTick(() => {
       console.log(3)
   })
})

new Promise(resolve => {
   console.log(7)
   resolve()
}).then(() => {
   console.log(8)
})

process.nextTick(() => {
   console.log(6)
})

setTimeout(() => {
   console.log(9)
   process.nextTick(() => {
       console.log(10)
   })
   new Promise(resolve => {
       console.log(11)
       resolve()
   }).then(() => {
       console.log(12)
   })
})

浏览器与node事件循环的根本区别就是:浏览器是执行完一个宏任务后就会清空微任务队列,node是将同源的宏任务执行完毕之后再去清空微任务队列,简单理解就是宏观任务可以进行合并。可以用下面这个例子在浏览器和node上自行运行试验

console.log(1);

setTimeout(() => {
    console.log(2)
    new Promise((resolve) => {
        console.log(6);
        resolve(7);
    }).then((num) => {
        console.log(num);
    })
});

setTimeout(() => {
    console.log(3);
       new Promise((resolve) => {
        console.log(9);
        resolve(10);
    }).then((num) => {
        console.log(num);
    })
    setTimeout(()=>{
        console.log(8);
    })
})

new Promise((resolve) => {
    console.log(4);
    resolve(5)
}).then((num) => {
    console.log(num);
    new Promise((resolve)=>{
        console.log(11);
        resolve(12);
    }).then((num)=>{
        console.log(num);
    })
})

参考
浏览器中的事件循环
Event Loop的规范和实现
浏览器中的事件循环
https://github.com/yangxy6/FE_Learning/issues/2
https://learnku.com/articles/38802
https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/26

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

推荐阅读更多精彩内容