准备
- 使用lie.js替换原生Promise,方便代码调试
- 理解MutationObserver观察器的使用,新建一个节点,每次触发,1,0循环更改text
- nextTick,先进先出,模拟微任务队列规则
Promise代码执行顺序分析
Promise.resolve()
.then(
// cb1
() => {
console.log('promise 1')
Promise.resolve().then(
// cb2
() => {
console.log('promise 2')
Promise.resolve().then(
// cb3
() => {
console.log('promise 3')
})
})
})
.then(
// cb4
()=> {
console.log('promise 4')
}
)
结果:
promise 1
promise 2
promise 4
promise 3
上面代码执行顺序分析
- Promise.resolve()把第一个Promise的state改变为"FULFILLED"
- 此时代码有
Promise.prototype.then = function (onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function' && this.state === FULFILLED ||
typeof onRejected !== 'function' && this.state === REJECTED) {
return this;
}
var promise = new this.constructor(INTERNAL);
if (this.state !== PENDING) {
var resolver = this.state === FULFILLED ? onFulfilled : onRejected;
unwrap(promise, resolver, this.outcome);
} else {
this.queue.push(new QueueItem(promise, onFulfilled, onRejected));
}
return promise;
};
- 当state !== PENDING, 加入到unwrap,即微任务队列:queue = [],即第一个then的cb1加入里面去了
- 此时queue.push(task) === 1满足,draining=undefined !draining 满足,执行了scheduleDrain();
function immediate(task) {
if (queue.push(task) === 1 && !draining) {
scheduleDrain();
}
}
即触发了一次文本节点的修改
- 返回了第一个then,新生成的Promise
- 执行第二个then,此时新生成Promise后,this.state !== PENDING 为false,走下面这个逻辑了
this.queue.push(new QueueItem(promise, onFulfilled, onRejected));
- 即把要执行的回调放入了当前Promise的queue里面
- 返回新的Promise
- 代码执行完毕,加入观察器里面的nextTick开始执行,draining = true了
- 采用先进先出的原则,执行queue的第一个回调函数
- 即打印了promise 1,有开始Promise.resolve().then,循环上面的内容
- 重复步骤3,执行unwrap,唯一不同的是,步骤4,draining = true了。只加入了task,没执行scheduleDrain,此时微任务队列queue有一个回调
- 返回新的Promise
- nextTick包裹了一层的, 开始执行handlers.resolve(promise, returnValue);
function unwrap(promise, func, value) {
immediate(function () {
var returnValue;
try {
returnValue = func(value);
} catch (e) {
return handlers.reject(promise, e);
}
if (returnValue === promise) {
handlers.reject(promise, new TypeError('Cannot resolve promise with itself'));
} else {
// 没有返回值,执行这里
handlers.resolve(promise, returnValue);
}
});
}
- resolve方法最终调用了以下方法,通过unwrap把回调加入到了微任务队列,此时微任务有两个回调
QueueItem.prototype.otherCallFulfilled = function (value) {
unwrap(this.promise, this.onFulfilled, value);
};
- nextTick
function nextTick() {
draining = true;
var i, oldQueue;
var len = queue.length;
while (len) {
oldQueue = queue;
queue = [];
i = -1;
while (++i < len) {
oldQueue[i]();
}
len = queue.length;
}
draining = false;
}
- 第一次的微任务队列执行到第二个while里面,当len = 2,又开始执行外部的while。
- 继续打印promise 2,promise 4,promise 3
总结:
- this.state !== PENDING,加入微任务队列,所以cb1 加入了
var queue = [cb1];
- promise 4加入到了上一个promise的队列里面
promise = {
queue = [{
onFulfilled: cb4
}],
state: ["PENDING"]
}
- 执行微任务队列中cb1,即打印promise 1,同理1,把cb2加入微任务队列
var queue = [cb2];
- 执行cb4之后,把它从promise的队列中加入到queue
var queue = [cb2, cb4];
- cb2出队列,执行cb2,打印promise2,同时cb3进队列
var queue = [cb4, cb3];
- 最后打印promise3