Event Loop 的異步流程

內(nèi)容參考

執(zhí)行隊列

microtask

當(dāng)前JS執(zhí)行l(wèi)oop的尾部

macrotask

下個JS執(zhí)行l(wèi)oop的首部

JS的執(zhí)行順序

JS逐行執(zhí)行 (首先執(zhí)行同步代碼)
1【1行,2行,3行】
pop()出棧
2【microtask】
(eventLoop)進(jìn)入下個loop
3【macrotask】

引用
MicroTask和macroTask的api范疇
1macrotasks: setTimeout, setInterval, setImmediate, I/O, UI rendering
2microtasks: process.nextTick, Promises, Object.observe, MutationObserver
stackoverflow

Promise 的異步執(zhí)行

const p = new Promise((res, rej) => {
  res(1)
  console.log('定義new Promise - 同步')
}).then(val => {
  console.log('microtask start')
  console.log('執(zhí)行then,enqueue micarotask 1')
  console.log(val) // 1
})

Promise.resolve({
  then(res, rej) {
    console.log('執(zhí)行then,enqueue micarotask 2')
    res(5)
  }
}).then(val => {
  console.log('執(zhí)行then,enqueue micarotask 3')
  console.log(val) // 5
})

console.log('逐行執(zhí)行1 - 同步')
console.log('逐行執(zhí)行2 - 同步')
console.log(3) // 3

setTimeout(console.log, 0, 'macrotask start') // 4 
setTimeout(console.log, 0, 4) // 4 
// 執(zhí)行結(jié)果如下
定義new Promise - 同步
逐行執(zhí)行1 - 同步
逐行執(zhí)行2 - 同步
3
// 同步隊列執(zhí)行完畢為空 進(jìn)入下一個棧

microtask start
執(zhí)行then,enqueue micarotask 1
1
執(zhí)行then,enqueue micarotask 2
執(zhí)行then,enqueue micarotask 3
5
// microtask執(zhí)行完畢為空 進(jìn)入下一個棧

macrotask start
4
9
// macrotask執(zhí)行完畢為空 結(jié)束

結(jié)論:Promise的then API把語句壓人micarotask內(nèi)執(zhí)行,setTimout則壓macrotask
總結(jié):JS先執(zhí)行同步代碼,再執(zhí)行micarotask,最后執(zhí)行macrotask

generator 異步執(zhí)行

generator 基礎(chǔ)API

遍歷邏輯

const generator_souce = function* () {
  console.log('start            遇到y(tǒng)ield暫停')
  const a = yield 1
  console.log('a === ' + a + '  遇到y(tǒng)ield暫停')
  const b = yield 2
  console.log('b === ' + b + '  沒有yield執(zhí)行所有語句')
  console.log('end')
}

const g = generator_souce()
for(let i of g) {
  console.log(i)
} // for of 循環(huán)遍歷iteration
/* 執(zhí)行結(jié)果
start            遇到y(tǒng)ield暫停
1 // 確定const a = yield 1的結(jié)果為1
a === undefined  遇到y(tǒng)ield暫停
2 // 確定const b = yield 2的結(jié)果為2
b === undefined  沒有yield執(zhí)行所有語句
end
*/

傳值邏輯

當(dāng)我們換成next() API執(zhí)行g(shù)enerator函數(shù)時,generator函數(shù)內(nèi)yield賦值的變量不能在函數(shù)體內(nèi)進(jìn)行傳遞

g.next()
g.next()
g.next()
/*  
start            遇到y(tǒng)ield暫停
a === undefined  遇到y(tǒng)ield暫停
b === undefined  沒有yield執(zhí)行所有語句
end
*/

next()執(zhí)行后返回的value為generator函數(shù)內(nèi)yield產(chǎn)出的值
generator函數(shù)內(nèi)yield賦值的變量只能靠在next().value進(jìn)行傳遞

const c = g.next()
const d = g.next(c.value)
g.next(d.value)
/*
start    遇到y(tǒng)ield暫停
a === 1  遇到y(tǒng)ield暫停
b === 2  沒有yield執(zhí)行所有語句
end
*/

co模塊

co模塊其實就是封裝了傳值邏輯,內(nèi)部實現(xiàn)使用了Promise

面向過程的co
const g = generator_souce()

const co = generator => {
  const a = generator.next()
  if(!a.done) {
    const b = generator.next(a.value)
    if(!b.done) {
      const c = generator.next(b.value)
      if(!c.done) {
        const d = genterator.next(c.value)
      }
    }
  }
}
co(g)

使用Promise實現(xiàn)的簡單co co-light


const g = generator_souce()

const co = (gen)=>{
    let basePromise=(value)=>{
            let nextGen = gen.next(value);              
            let nextVal = nextGen['value'];
            if(!nextGen.done){
                return Promise.resolve(nextVal).then(basePromise)
            }else{
                return Promise.resolve(nextVal)
            }
        };
    return new Promise((resolve,reject)=>{
        //啟動generator
         let start = gen.next()['value'];
         Promise.resolve(start).then(resolve)
    }).then(basePromise).catch((error)=>{
        gen.throw('generator throw ------' + error.name + error.stack)  
    });
}
co(g)
執(zhí)行結(jié)果
/*
start            遇到y(tǒng)ield暫停
a === 1  遇到y(tǒng)ield暫停
b === 2  沒有yield執(zhí)行所有語句
end
*/

aync - await

await后面必須接一個Promise,返回的值就是這個Promise最后返回的值

const fn = async (val) => {
    const await1 = await 1
}
// 等價于
const fn = async (val) => {
    const await1 = await Promise.resolve(1)
}
const fn = async (val) => {
  const await1 = await new Promise((resolve, reject) => {
    setTimeout(resolve, val*500, val)
  }).then(val => {
    return val + 1
  })
  console.log(`await函數(shù)返回的value === ${await1}`)
}

fn(2)
fn(3)
執(zhí)行結(jié)果
/*
await函數(shù)返回的value === 3
await函數(shù)返回的value === 4
*/

簡單理解就是await后面的所有Promise完成之后變量才被賦值

簡單原理:
aync + await 降級=> generator + 自動執(zhí)行 轉(zhuǎn)化=> Promise + 語法轉(zhuǎn)換

setTimeout (microtask 和 macrotask)

1macrotasks: setTimeout, setInterval, setImmediate, I/O, UI rendering
2microtasks: process.nextTick, Promises, Object.observe, MutationObserver
同步代碼執(zhí)行完畢立即執(zhí)行microtasks,效率更高

process.nextTick(() => console.log('tick0'))
setTimeout(console.log, 0, '0s')
process.nextTick(() => console.log('tick1'))
setTimeout(console.log, 1000, '1s')
setTimeout(console.log, 2000, '2s')
console.log('1')
console.log('2')
/* 執(zhí)行順序 [同步代碼] pop() [microtasks] eventLoop() [macrotasks]
1
2
tick0
tick1
0s
1s
2s
*/

MutationObserver

vue2.0使用MutationObserver API作為異步更新隊列的DOM更新解決方案
MutationObserver屬于microtasks,執(zhí)行效率更高,優(yōu)先于setTimeout執(zhí)行

    // Firefox和Chrome早期版本中帶有前綴
    const MutationObserver = window.MutationObserver
    || window.WebKitMutationObserver || window.MozMutationObserver
    // 創(chuàng)建觀察者對象
    const observer = new MutationObserver(mutations=>{
      mutations.forEach(function(mutation) {
        console.log(mutation.type);
      })   
    }) 
    // 配置觀察選項:
    const config = { 
      attributes: true, 
      childList: true, 
      characterData: true 
    }
    // 選擇目標(biāo)節(jié)點(diǎn)
    const target = document.querySelector('#test');
    // 傳入目標(biāo)節(jié)點(diǎn)和觀察選項
    observer.observe(target, config);

    target.appendChild(document.createElement("div"))   
    /*
    * mutationObserver優(yōu)先于setTimeout
    */
    setTimeout(console.log,0,'setTimeout')
    console.log('appending')  
    target.setAttribute('class','hello')                       //添加了一個元素子節(jié)點(diǎn),觸發(fā)回調(diào)函數(shù).

    // 隨后,你還可以停止觀察
    // observer.disconnect();


    /*
    * doc  https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver
    */
執(zhí)行結(jié)果
/*
appending
childList
attributes
setTimeout
*/

MutationObserver

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 弄懂js異步 講異步之前,我們必須掌握一個基礎(chǔ)知識-event-loop。 我們知道JavaScript的一大特點(diǎn)...
    DCbryant閱讀 2,750評論 0 5
  • 異步編程對JavaScript語言太重要。Javascript語言的執(zhí)行環(huán)境是“單線程”的,如果沒有異步編程,根本...
    呼呼哥閱讀 7,334評論 5 22
  • 由于JavaScript是單線程的語言,因此異步編程對于js的重要程度可想而知,可以說沒有異步的js程序寸步難行。...
    會飛小超人閱讀 1,008評論 0 7
  • 本文首發(fā)在個人博客:http://muyunyun.cn/posts/7b9fdc87/ 提到 Node.js, ...
    牧云云閱讀 1,702評論 0 3
  • 本文旨在通過實例演示JS異步編程幾種常見的解決方案,讓你寫出最優(yōu)雅的異步代碼。 異步是Javascript區(qū)別于其...
    沐童Hankle閱讀 3,395評論 2 3