今天练习promise的时候,
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello')
}, 2000)
})
console.log('1', p1)
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('world')
}, 2000)
})
console.log('2', p2)
p1.then((data) => {
console.log(data)
return p2
})
.then((data) => {
console.log(data)
})
输出结果:hello
和world
同时输出
在此之前,我理解的promise
一直是可以保证执行顺序的,即2秒后输出hello
,2秒后再输出world
。然而实际hello
输出后立即输出了world
。
实际上,是我理解的promise
运行机制有一定偏差。遂仔细查看官方文档:
语法:
new Promise( function(resolve, reject) {…} /* executor */ );
参数:
executor:executor是带有 resolve 和 reject 两个参数的函数 。Promise构造函数执行时立即调用 executor 函数, resolve 和 reject 两个函数作为参数传递给executor(executor函数在Promise构造函数返回所建promise实例对象前被调用)。resolve 和 reject 函数被调用时,分别将promise的状态改为fulfilled(完成)或rejected(失败)。executor 内部通常会执行一些异步操作,一旦异步操作执行完毕(可能成功/失败),要么调用resolve函数来将promise状态改成fulfilled,要么调用reject 函数将promise的状态改为rejected。如果在executor函数中抛出一个错误,那么该promise 状态为rejected。executor函数的返回值被忽略。
总结下就是说,new Promise()
时,promise对象内的executor
函数是同步的,因此在返回实例化promise对象前已经执行了。只是他们各自的resolve回调函数(假设都成功)按照执行顺序执行。
用浏览器的执行机制再来理一下吧。 上图:
在new Promise()
时,同步执行内部代码,定时器触发线程进行计数,定时时间到后,定时器触发线程将resolve
回调函数移入任务队列。因此在上面的代码中,实例化两个promise
对象时,其内部的异步代码已经执行,2秒后两个promise
内部定时器都已经定时时间到,两个resolve
函数都已经在任务列表中等待,等待执行。然后浏览器执行微任务,连续输出hello
,world
。
如果想要实现我们原先的目的,即2秒后输出hello
,2秒后再输出world
,就在p1的resolve回调执行后再实例化p2。即:
new Promise(resolve => {
setTimeout(() => {
resolve('hello')
}, 2000)
}).then(val => {
console.log(val) // 参数val = 'hello'
return new Promise(resolve => {
setTimeout(() => {
resolve('world')
}, 2000)
})
}).then(val => {
console.log(val) // 参数val = 'world'
})
又提到浏览器运行机制了,有必要自己又扩展了下:
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('1')
resolve('hello')
}, 2000)
})
console.log('2', p1)
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('3')
resolve('world')
}, 2000)
})
console.log('4', p2)
p1.then((data) => {
console.log('5',data)
return p2
})
.then((data) => {
console.log('6',data)
})
输出:
运行原理:
- 先执行同步代码,输出2,4;
- p1实例化后,定时器触发线程同时定时两个setTimeout,但p1内的setTimeout在前,p2的setTimeout在后。于是2秒后,先输出1,此时遇到promise的reject回调(微任务),必须先执行微任务,因此输出5;
- 此时可以执行p2的setTimeout,因此输出3,再输出6。
再修改下两个定时时间:
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('1')
resolve('hello')
}, 3000)
})
console.log('2', p1)
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('3')
resolve('world')
}, 2000)
})
console.log('4', p2)
p1.then((data) => {
console.log('5',data)
return p2
})
.then((data) => {
console.log('6',data)
})
输出:(先输出2,4
,2秒后输出3
,1秒后输出1,5,6
)
因为我们是链式调用,必须得p1的回调执行完毕后才能执行p2的回调。因此输出3后,需再等一秒先等p1的setTimeout定时结束,执行p1的回调,然后才能顺序执行p2的回调。
如果我们取消链式调用呢?
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('1')
resolve('hello')
}, 3000)
})
console.log('2', p1)
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('3')
resolve('world')
}, 2000)
})
console.log('4', p2)
p1.then((data) => {
console.log('5',data)
})
p2.then((data) => {
console.log('6',data)
})
如果我们取消链式调用,则输出:
按照宏任务执行后,先执行对应微任务,然后继续执行宏任务的机制运行。