执行队列
microtask
当前JS执行loop的尾部
macrotask
下个JS执行loop的首部
JS的执行顺序
JS逐行执行 (首先执行同步代码)
1【1行,2行,3行】
pop()出栈
2【microtask】
(eventLoop)进入下个loop
3【macrotask】
引用
MicroTask和macroTask的api范畴
1macrotasks: setTimeout, setInterval, setImmediate, I/O, UI rendering
2microtasks: process.nextTick, Promises, Object.observe, MutationObserver
stackoverflow
Promise 的异步执行
const p = new Promise((res, rej) => {
res(1)
console.log('定义new Promise - 同步')
}).then(val => {
console.log('microtask start')
console.log('执行then,enqueue micarotask 1')
console.log(val) // 1
})
Promise.resolve({
then(res, rej) {
console.log('执行then,enqueue micarotask 2')
res(5)
}
}).then(val => {
console.log('执行then,enqueue micarotask 3')
console.log(val) // 5
})
console.log('逐行执行1 - 同步')
console.log('逐行执行2 - 同步')
console.log(3) // 3
setTimeout(console.log, 0, 'macrotask start') // 4
setTimeout(console.log, 0, 4) // 4
// 执行结果如下
定义new Promise - 同步
逐行执行1 - 同步
逐行执行2 - 同步
3
// 同步队列执行完毕为空 进入下一个栈
microtask start
执行then,enqueue micarotask 1
1
执行then,enqueue micarotask 2
执行then,enqueue micarotask 3
5
// microtask执行完毕为空 进入下一个栈
macrotask start
4
9
// macrotask执行完毕为空 结束
结论:Promise的then API把语句压人micarotask内执行,setTimout则压macrotask
总结:JS先执行同步代码,再执行micarotask,最后执行macrotask
generator 异步执行
遍历逻辑
const generator_souce = function* () {
console.log('start 遇到yield暂停')
const a = yield 1
console.log('a === ' + a + ' 遇到yield暂停')
const b = yield 2
console.log('b === ' + b + ' 没有yield执行所有语句')
console.log('end')
}
const g = generator_souce()
for(let i of g) {
console.log(i)
} // for of 循环遍历iteration
/* 执行结果
start 遇到yield暂停
1 // 确定const a = yield 1的结果为1
a === undefined 遇到yield暂停
2 // 确定const b = yield 2的结果为2
b === undefined 没有yield执行所有语句
end
*/
传值逻辑
当我们换成next() API执行generator函数时,generator函数内yield赋值的变量不能在函数体内进行传递
g.next()
g.next()
g.next()
/*
start 遇到yield暂停
a === undefined 遇到yield暂停
b === undefined 没有yield执行所有语句
end
*/
next()执行后返回的value为generator函数内yield产出的值
generator函数内yield赋值的变量只能靠在next().value进行传递
const c = g.next()
const d = g.next(c.value)
g.next(d.value)
/*
start 遇到yield暂停
a === 1 遇到yield暂停
b === 2 没有yield执行所有语句
end
*/
co模块
co模块其实就是封装了传值逻辑
,内部实现使用了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实现的简单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)
执行结果
/*
start 遇到yield暂停
a === 1 遇到yield暂停
b === 2 没有yield执行所有语句
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函数返回的value === ${await1}`)
}
fn(2)
fn(3)
执行结果
/*
await函数返回的value === 3
await函数返回的value === 4
*/
简单理解就是await后面的所有Promise完成之后变量才被赋值
简单原理:
aync + await 降级=> generator + 自动执行 转化=> Promise + 语法转换
setTimeout (microtask 和 macrotask)
1macrotasks: setTimeout, setInterval, setImmediate, I/O, UI rendering
2microtasks: process.nextTick, Promises, Object.observe, MutationObserver
同步代码执行完毕立即执行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')
/* 执行顺序 [同步代码] pop() [microtasks] eventLoop() [macrotasks]
1
2
tick0
tick1
0s
1s
2s
*/
MutationObserver
vue2.0使用MutationObserver API作为异步更新队列的DOM更新解决方案
MutationObserver属于microtasks,执行效率更高,优先于setTimeout执行
// Firefox和Chrome早期版本中带有前缀
const MutationObserver = window.MutationObserver
|| window.WebKitMutationObserver || window.MozMutationObserver
// 创建观察者对象
const observer = new MutationObserver(mutations=>{
mutations.forEach(function(mutation) {
console.log(mutation.type);
})
})
// 配置观察选项:
const config = {
attributes: true,
childList: true,
characterData: true
}
// 选择目标节点
const target = document.querySelector('#test');
// 传入目标节点和观察选项
observer.observe(target, config);
target.appendChild(document.createElement("div"))
/*
* mutationObserver优先于setTimeout
*/
setTimeout(console.log,0,'setTimeout')
console.log('appending')
target.setAttribute('class','hello') //添加了一个元素子节点,触发回调函数.
// 随后,你还可以停止观察
// observer.disconnect();
/*
* doc https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver
*/
执行结果
/*
appending
childList
attributes
setTimeout
*/