執(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í)行
遍歷邏輯
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
*/