前端进阶:从JS异步/同步到Event Loop(事件循环机制)

JS 是单线程执行的。当你打开一个 Tab 页时,其实就是创建了一个进程,一个进程中可以有多个线程,比如渲染线程、JS 引擎线程、HTTP 请求线程等等。当你发起一个请求时,其实就是创建了一个线程,当请求结束后,该线程可能就会被销毁。

那么JS是单线程,为什么会有异步呢?
众所周知,单线程执行就要排队,自上而下执行,可某一行解析时间过长,那么下面的代码就会被阻塞。
所以JS需要有异步,通过Event Loop来实现异步处理,来看一个经典的问题

console.log('1')
setTimeout(() => {
 console.log('2')
},0)
console.log('3')
// 打印 1, 3, 2 

这里说明 setTimeout 并不是立即执行,而是延迟了一段时间后再执行,在JS里这类代码,我们称之为异步代码(其实还是一个同步的过程,后面会解释)

1、执行栈概念

执行JS代码的时候,就是把一个一个的函数放进执行栈中,先进后出的概念,执行完毕就从栈中弹出。

function a() {
  const name = 'Moxuanya';
  return b(name);
};
function b(str) {
  return str;
};
console.log(a());

放入执行栈的顺序:console.log(a()) → a() → b()
弹出执行栈的顺序:b() → a() → console.log(a())
注意:栈可存放的函数是有限制的,一旦存放了过多的函数且没有得到释放的话,就会出现爆栈的问题。

2、Event Loop(事件循环机制)

上面已经说到执行JS其实就是往执行栈中放入函数,遇到异步的时候怎么办呢?
当遇到异步的代码时,会被挂起等待,并加入到Task队列中(microtask 和 macrotask,后面会深入讲到),当执行栈为空时,Event Loop就会把Task队列中的代码加入到执行栈中执行。所以JS的异步本质上,还是同步。

digui(n) {
  if (n < 2) {
    return n;
  }
  return this.digui(n - 2) + this.digui(n - 1);
},
text() {
  console.log('s1', Date.now() / 1000);
  setTimeout(() => {
    console.log('s2', Date.now() / 1000);
  }, 1000);
  this.digui(39); // 一个需要大约3秒执行完毕
  console.log('s3', Date.now() / 1000);
  this.digui(39); // 一个需要大约3秒执行完毕
  console.log('s4', Date.now() / 1000);
},
// s1 1563873581.294
// s3 1563873584.106
// s4 1563873586.687
// s2 1563873586.69

函数执行,遇到setTimeout 放入Task 队列中,执行栈的任务全部执行完毕大约6S 后打印出了s2。
重点来了!!!不管挂载到Task 队列等待的代码具体方法是什么,一定会等待执行栈中的主任务执行完毕

3、任务队列的深层原理:microtask(微任务)和 macrotask(宏任务)

这是我更改过的一道经典题,能答出正确打印顺序吗

console.log('script start')

async function async1() {
  await async2()
  console.log('async1 end')
}
async function async2() {
  console.log('async2 end')
}
async1()

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

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

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

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

console.log('script end')

// script start -> async2 end -> Promise -> script end -> async1 end -> promise1 -> promise2 -> setTimeout1 -> setTimeout2

常见的微任务和宏任务:
微任务包括 process.nextTick ,promise ,MutationObserver,其中 process.nextTick 为 Node 独有。
宏任务包括 script , setTimeout ,setInterval ,setImmediate ,I/O ,UI rendering。

用我的理解解答一下,script start → async2 end → Promise → script end,这些任务是主任务,直接放到执行栈中执行,其他的任务则被挂载到Task 队列中,Task 队列区分是宏任务还是微任务,分别放入microtask队列和 macrotask队列,→ async1 end(这一步需要注意,在Chrome7.0以上await 提前了,当作一个new Promise处理),→ promise1→ promise2 执行微任务队列,→ setTimeout1→ setTimeout2,执行宏任务队列。那么往后延申,再执行宏任务的时候,又会有一些任务被挂起,
写到最后,我的结论先执行一遍宏任务队列(主任务),遇到异步被挂起,宏任务执行完毕,立马执行之前挂起的微任务,等到微任务执行完毕,又往执行栈中放入之前被挂起的宏任务,此时又遇到异步,则又会被挂起,如此循环。博主学艺不精,还望海涵。

千里之行始于足下,冰冻三尺非一日之寒!

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