var date = new Date()
console.log(1, new Date() - date)
setTimeout(() => {
console.log(2, new Date() - date)
}, 500)
Promise.resolve().then(console.log(3, new Date() - date))
while(new Date() - date < 1000) {}
console.log(4, new Date() - date)
求上面的输出顺序和输出值,为什么?
答案:
1 0
3 1
4 1000
2 1000
其中,关于时间差结果可能因为计算机性能造成的微小差异,可忽略不计
你答对了吗?下面我们由浅入深探索本题
首先,看一下 event loop
的基础必备内容
event loop
执行顺序:
script
宏任务Web Worker
任务,有则执行微任务包括:MutationObserver
、Promise.then()或catch()
、Promise为基础开发的其它技术,比如fetch API
、V8
的垃圾回收过程、Node独有的process.nextTick
、 Object.observe
(已废弃;Proxy
对象替代)
宏任务包括:script
、setTimeout
、setInterval
、setImmediate
、I/O
、UI rendering
、 postMessage
、 MessageChannel
注意: 下面的题目都是执行在浏览器环境下
遇到不好理解的,可结合 【promise
源码实现:https://github.com/sisterAn/blog/issues/13】 进行理解,就很简单了
var promise = new Promise((resolve, reject) => {
console.log(1)
resolve()
console.log(2)
})
promise.then(()=>{
console.log(3)
})
console.log(4)
// 1
// 2
// 4
// 3
解析:
Promise
构造函数是同步执行的, then
方法是异步执行的new Promise
,执行构造函数同步代码,输出 1
resolve()
, 将 promise
的状态改为了 resolved
,并且将 resolve
值保存下来,此处没有传值2
promise
,往下执行,碰到 promise.then
这个微任务,将其加入微任务队列4
promise.then
微任务,输出 3
var promise = new Promise((resolve, reject) => {
console.log(1)
})
promise.then(()=>{
console.log(2)
})
console.log(3)
// 1
// 3
解析:
new Promise
,执行构造函数同步代码,输出 1
promise.then
,因为 promise
中并没有resolve
,所以 then
方法不会执行3
var promise = new Promise((resolve, reject) => {
console.log(1)
})
promise.then(console.log(2))
console.log(3)
// 1
// 2
// 3
解析:
.then
或者 .catch
的参数期望是函数,传入非函数则会发生( value => value
)new Promise
,执行构造函数同步代码,输出 1
then()
的参数是一个 console.log(2)
(注意:并不是一个函数),是立即执行的,输出 2
3
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
// 1
解析:
then(2)
、 then(Promise.resolve(3))
发生了值穿透,直接执行最后一个 then
,输出 1
var promise = new Promise((resolve, reject) => {
console.log(1)
resolve()
reject()
})
promise.then(()=>{
console.log(2)
}).catch(()=>{
console.log(3)
})
console.log(4)
// 1
// 4
// 2
解析:
new Promise
,执行构造函数同步代码,输出 1
resolve()
, 将 promise
的状态改为了 resolved
,并且将 resolve
值保存下来,此处没有传值reject()
,此时 promise
的状态已经改为了 resolved
,不能再重新翻转(状态转变只能是pending —> resolved 或者 pending —> rejected,状态转变不可逆)promise
,往下执行,碰到 promise.then
这个微任务,将其加入微任务队列promise.catch
这个微任务,此时 promise
的状态为 resolved
(非 rejected
),忽略 catch
方法4
promise.then
微任务,输出 2
Promise.resolve(1)
.then(res => {
console.log(res);
return 2;
})
.catch(err => {
return 3;
})
.then(res => {
console.log(res);
});
// 1
// 2
解析:
resolve(1)
, 状态改为了 resolved
,并且将 resolve
值保存下来console.log(res)
输出 1
return 2
实际上是包装成了 resolve(2)
resolved
, catch
方法被忽略then
,输出 2
setTimeout(() => {
console.log(1)
})
Promise.resolve().then(() => {
console.log(2)
})
console.log(3)
// 3
// 2
// 1
解析:
setTimout
被放入宏任务队列Promise.resolve().then
, then
方法被放入微任务队列3
then
微任务,输出 2
setTimeout
宏任务,执行输出 1
var promise = new Promise((resolve, reject) => {
console.log(1)
setTimeout(() => {
console.log(2)
resolve()
}, 1000)
})
promise.then(() => {
console.log(3)
})
promise.then(() => {
console.log(4)
})
console.log(5)
// 1
// 5
// 2
// 3
// 4
解析:
promise.then
时,如果当前的 Promise
还处于 pending
状态,我们并不能确定调用 resolved
还是 rejected
,只有等待 promise
的状态确定后,再做处理,所以我们需要把我们的两种情况的处理逻辑做成 callback
放入 promise
的回调数组内,当 promise
状态翻转为 resolved
时,才将之前的 promise.then
推入微任务队列Promise
构造函数同步执行,输出 1
,执行 setTimeout
setTimeout
加入到宏任务队列中promise.then
放入 promise
的回调数组内promise.then
放入 promise
的回调数组内5
setTimeout
宏任务,输入 2
,执行 resolve
, promise
状态翻转为 resolved
,将之前的 promise.then
推入微任务队列setTimeout
宏任务出队,检查微任务队列3
4
现在看,本题就很简单了
var date = new Date()
console.log(1, new Date() - date)
setTimeout(() => {
console.log(2, new Date() - date)
}, 500)
Promise.resolve().then(console.log(3, new Date() - date))
while(new Date() - date < 1000) {}
console.log(4, new Date() - date)
解析:
1 0
setTimeout
,定时 500ms
后执行,此时,将 setTimeout
交给异步线程,主线程继续执行下一步,异步线程执行 setTimeout
Promise.resolve().then
, .then
的参数不是函数,发生( value => value
) ,输出 3 1
whlie
,等待 1000ms
,在此期间,setTimeout
定时 500ms
完成,异步线程将 setTimeout
回调事件放入宏任务队列中4 1000
setTimeout
宏任务,输入 2 1000
Promise
构造函数是同步执行的, then
方法是异步执行的.then
或者 .catch
的参数期望是函数,传入非函数则会发生值透传Promise
的状态一经改变就不能再改变,构造函数中的 resolve
或 reject
只有第一次执行有效,多次调用没有任何作用.then
方法是能接收两个参数的,第一个是处理成功的函数,第二个是处理失败的函数,再某些时候你可以认为catch
是.then
第二个参数的简便写法promise.then
时, 如果当前的 Promise
还处于 pending
状态,我们并不能确定调用 resolved
还是 rejected
,只有等待 promise
的状态确定后,再做处理,所以我们需要把我们的两种情况的处理逻辑做成 callback
放入 promise
的回调数组内,当 promise
状态翻转为 resolved
时,才将之前的 promise.then
推入微任务队列Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8