1
new Promise(resolve => {
console.log('promise1');
resolve();
}).then(() => {
console.log('then11');
// return
new Promise(resolve => {
console.log('promise2');
resolve();
}).then(() => {
console.log('then21');
}).then(() => {
console.log('then22');
});
}).then(() => {
console.log('then12');
});
复制代码
结果:
事件队列执行的基本模型
一步一步解析:
首先
new Promise(resolve => {
console.log('promise1');
resolve();
})
复制代码
里面的执行器函数是同步执行,所以输出 'promise1'
然后执行 resolve()
,同步修改promise的状态和值。然后根据情况是否设置微任务
如果 then
在 resolve
之前执行,说明 resolve
为异步代码。 then
里面已经存了方法,即先运行 then
存储,后触发 resolve
,那么就设置一个微任务,这个微任务是:执行 .then
里面的方法。
如果 then
在 resolve
之后执行,如果没有存方法,就像这个例子一样, resolve
是同步的,执行 resolve
的时候还没有执行 then
, then
是后执行的。那么这个微任务就跳过不设置。
然后执行 .then()
方法,
.then(() => {
console.log('then11');
// return
new Promise(resolve => {
console.log('promise2');
resolve();
}).then(() => {
console.log('then21');
}).then(() => {
console.log('then22');
});
})
复制代码
执行 .then()
,首先看当前实例的状态。
如果是 pendding
状态,那么就把这个方法存起来,什么时候执行 resolve
,再通过 resolve
提供的微任务来执行 then
里面的回调函数。
如果当前实例的状态是成功或者失败(能看出成功还是失败,说明 resolve
已经同步运行了,比如这里的这个例子)。如果确定状态,那就知道到底执行 then
里面的哪个方法。然后将 .then
其中的回调函数设置成一个微任务,等待同步结束后执行
所以这里是把 then
中的回调函数当成看做 微任务1 ,放入微任务队列中,同步结束后执行
接下来执行第二个 then
.then(() => {
console.log('then12');
})
复制代码
第二个 then
执行哪个回调函数,取决于上一个 then
的两个回调函数的返回值和是否报错,因为上一个 then()
微任务1还没有执行,同步结束之后才执行,所以这里先把这个方法先存起来,当做一个微任务,我们叫做微任务2。微任务2需要等待微任务1决定状态之后才执行。
然后同步已经执行完毕,执行完后开始执行微任务1
() => {
console.log('then11');
new Promise(resolve => {
console.log('promise2');
resolve();
}).then(() => {
console.log('then21');
}).then(() => {
console.log('then22');
});
}
复制代码
输出 'then11'
new Promise(resolve => {
console.log('promise2');
resolve();
})
复制代码
同样的原理,输出 'promise2'
,并且 resolve
修改状态,后面 then
创建微任务3,微任务3等到主线程一空闲就可以执行了,因为已经同步resolve了状态,直接知道了状态,所以主线程一空闲优先立即执行,这时微任务1还没有执行完,没有执行到最下面,所以微任务3要优先与微任务2
//微任务3
() => {
console.log('then21');
}
复制代码
接着继续 then
,
.then(() => {
console.log('then22');
})
复制代码
这个then
就创建一个微任务4,直到微任务3执行完才可以知道是否执行。
这时微任务1执行完毕,微任务1执行完,就可以决定最后那个 then
状态,此时要立刻执行微任务2,微任务2的优先级比微任务4要高
如果任务队列中有个任务,谁先知道可执行(谁先到达)谁就先执行
执行到 console.log('promise2'); resolve();
这句代码的时候,继续往下执行,要执行两个 .then()
,这时候第一个 then
中的回调函数任务3还不会执行,会放到微任务队列中,什么时候执行完任务23,才会创建微任务43,放到任务队列中,而什么时候任务1执行完了,才会执行已经放到任务队列中的任务2。
如果改成这样:
new Promise(resolve => {
console.log('promise1');
resolve();
}).then(() => {
console.log('then11');
return new Promise(resolve => {
console.log('promise2');
resolve();
}).then(() => {
console.log('then21');
}).then(() => {
console.log('then22');
});
}).then(() => {
console.log('then12');
});
复制代码
2
setTimeout(() => {
console.log(1);
}, 20);
console.log(2);
setTimeout(() => {
console.log(3);
}, 10);
console.log(4);
console.time('AA');
for (let i = 0; i < 90000000; i++) {
// do soming
}
console.timeEnd('AA'); //=>AA: 70ms 左右
console.log(5);
setTimeout(() => {
console.log(6);
}, 8);
console.log(7);
setTimeout(() => {
console.log(8);
}, 15);
console.log(9);
复制代码
因为执行完同步代码的时候,已经过了70ms,上面两个宏任务时间已经到了,所以执行完同步代码,立刻开始执行可以执行的宏任务的时候,即会把上面两个立刻执行,然后在等8和15毫秒,再执行下面两个新加入的宏任务。
同步结束后:
setTimeout(() => {},0)
, setTimeout(() => {})
:定时器设置为0或者不写,也不是立即执行。 浏览器有一个最快处理反应时间(谷歌5-7ms。ie10-17ms),即使写0也要等待5-7ms左右,所以也是异步的
js处理速度很快,相邻两个同步代码的间隔处理仅有0.01ms左右
3
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
复制代码
执行到 async1()
函数如下
需要注意的是,执行完 await async2();
会将后面的代码创建为微任务2,并且已经知道状态了,所以等同步完成后可立即执行
继续运行如下:
执行then
,因为已经知道状态,所以向微任务队列中添加微任务3,同步结束立即执行
4
let body = document.body;
body.addEventListener('click', function () {
Promise.resolve().then(() => {
console.log(1);
});
console.log(2);
});
body.addEventListener('click', function () {
Promise.resolve().then(() => {
console.log(3);
});
console.log(4);
});
复制代码
代码执行,添加两个回调函数为宏任务,然后点击body
,2,4为同步任务,1,3为两个宏任务中的微任务。要把一个宏任务完成再进行下一个宏任务,一个宏任务完成之前,要先执行完自己内部的微任务,所以是21先,43后
5
console.log('start');
let intervalId;
Promise.resolve().then(() => {
console.log('p1');
}).then(() => {
console.log('p2');
});
setTimeout(() => {
Promise.resolve().then(() => {
console.log('p3');
}).then(() => {
console.log('p4');
});
intervalId = setInterval(() => {
console.log('interval');
}, 3000);
console.log('timeout1');
}, 0);
复制代码
区分宏微任务,同步任务即可
6
setTimeout(() => {
console.log('a');
});
Promise.resolve().then(() => {
console.log('b');
}).then(() => {
return Promise.resolve('c').then(data => {
setTimeout(() => {
console.log('d')
});
console.log('f');
return data;
});
}).then(data => {
console.log(data);
});
复制代码
b微任务,f同步任务,最后要等return
才能执行最后一个 then
,执行完所有的微任务,再执行两个宏任务。
7
function func1() {
console.log('func1 start');
return new Promise(resolve => {
resolve('OK');
});
}
function func2() {
console.log('func2 start');
return new Promise(resolve => {
setTimeout(() => {
resolve('OK');
}, 10);
});
}
console.log(1);
setTimeout(async () => {
console.log(2);
await func1();
console.log(3);
}, 20);
for (let i = 0; i < 90000000; i++) {} //循环大约要进行80MS左右
console.log(4);
func1().then(result => {
console.log(5);
});
func2().then(result => {
console.log(6);
});
setTimeout(() => {
console.log(7);
}, 0);
console.log(8);
复制代码