小白视角理解ES6中的promise
内容参考:https://www.bilibili.com/video/BV1MJ41197Eu?p=19
Promise的概念
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。具体定义不再赘述,请参考阮一峰老师的 ECMAScript6 入门
问题1:如何改变promise的状态?
改变promise的状态有三种常用方式: 调用resolved()、调用reject(),和 throw 一个异常
new Promise((resolve,reject)=>{
// resolve(1) //会将promise状态变为resolved
// reject(2) //会将promsie状态变为rejected
throw 3 //会将promise 状态变为rejected
}).then(
value=>{
console.log('resolved '+value)
},
reason=>{
console.log('rejected '+reason)
}
)
问题2:一个promise指定多个成功/失败回调函数, 都会调用吗?
答案是肯定的。一个promise对象的状态在确定之后就不会再改变,并且可以在它的生命周期里一直被.then()\.catch()
使用,当然也可以被多个.then()\.catch()
使用
const p = new Promise((resolve,reject)=>{
resolve(1) //会将promise状态变为resolved
})
//第一组回调函数
p.then(
value=>{
console.log('resolved A '+value)
},
reason=>{
console.log('rejected A'+reason)
}
)
//第二组回调函数
p.then(
value=>{
console.log('resolved B '+value)
},
reason=>{
console.log('rejected B'+reason)
}
)
// resolved A 1
// resolved B 1
问题3:改变promise状态 和 指定回调函数谁先谁后?
- 都有可能
从正常的逻辑上来说,我们应该在一个事情的结果还没有发生之前,就确定怎么去应对成功/失败的结果(林夕写的好:要拥有必先懂失去怎接受)。反映在这里,就是当promise的状态被确定为resolved
还是rejected
之前,我们就应该绑定好不同的回调函数来处理结果。在promise之前采用回调函数机制来实现异步操作,回调函数就要在执行异步操作之前就要绑定给异步操作。
但是,promise恰恰打破了这种思路,无论promise是什么状态都可以绑定回调函数。例如:
- 在执行器中直接调用resolve()/reject()
const promise = Promise.resolve('哈哈直接成功')
promise.then((value)=>{
console.log(value)
})
说明一下,Promise.resolve(xxx)
是一个API语法糖,用来快捷创建一个成功的promise对象,相当于new Promise((resolved,reject)=>{resolve(xxx)})
。
这里由于执行器函数是同步回调,所以在promise被new出来的时候就立马执行,将promise的状态确定为resolved
。然后再通过.then()
给它绑定回调函数。
- 延迟更长时间才调用then()
const promise = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(123)
},1000)
})
promise.then(
(value)=>{
setTimeout(()=>{
console.log(value)
},1500)
}
)
延迟1s 调用resolve函数,但是1.5s后才调用回调函数。
【什么时候才能得到数据?】
①如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据
②如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据
问题4:promise.then()返回的新promise的结果状态由什么决定?
我们知道,.then() 对象会返回一个新的匿名promise对象,那么这个新对象的状态是如何确定的呢?
- 简单来说,由then()指定的回调函数执行的结果决定。
- 具体来说
- 如果抛出异常, 新promise变为rejected, reason为抛出的异常
- 如果返回的是非promise的任意值, 新promise变为resolved, value为返回的值(可以是undefined)
- 如果返回的是另一个新promise, 此promise的结果就会成为新promise的结果
举个例子:
new Promise((resolve,reject)=>{
resolve(1)
}).then(
(value)=>{
console.log(value) //1 因为上一层promise是resolved
return Promise.resolve(2)
}).then(
(value)=>{
console.log(value) //2 因为上一层then返回的是Promise.resolve(2)
return null
}
).then(
(value)=>{
console.log(value) //null 因为上一层then返回的是null,并没有异常,所以匿名promise对象也是resolved
throw 3
},
(reason)=>{
console.log("出错!")
throw reason
}
).then(
(value)=>{
console.log(value)
},
(reason)=>{
console.log("出错!") // 出错,因为上一层then throw了一个异常,所以匿名promise对象就是rejected
throw reason
}
)
问题5:promise如何串连多个操作任务?
(1)promise的then()返回一个新的promise, 可以开成then()的链式调用
(2)通过then的链式调用串连多个同步/异步任务
例子:
new Promise((resolve,reject)=>{
resolve(1)
}).then(
(value)=>{
console.log("我是同步任务,value = "+value)
return Promise.resolve(2)
}).then(
(value)=>{
return new Promise((resolve,reject)=>{ //由于是异步任务,所以要封装在promise里,才能利用.then()让下面的函数等它
setTimeout(()=>{
console.log("我是异步任务,value = "+value)
resolve(3) //别忘了resolve来确定匿名promise的状态,以出发下一个.then()里的回调函数
},2000)
})
}
).then( //z这个then会等待上一个promise的结果确定才会被调用
(value)=>{
console.log("我是同步任务,value = "+ value)
throw 404
}
).catch(
(reason)=>{
console.log("出错啦!错误代码: "+reason)
}
)
问题6:如何理解promise异常穿透?
当使用promise的then链式调用时,可以在最后指定失败的回调,这样一来,前面任何操作出了异常, 都会传到最后失败的回调中处理。还是上面问题5那个案例:可以看到,每一层.then()
我都只制定了onResolved
函数,而没有写onRejected
函数。实际上,当链上的一环发生错误后,它会去下一层.then()
中寻找onRejected
函数,如果没找到,那么会自动脑补一个onRejected
函数:reason=>{throw reason}
,就像下面这样:
.then(
(value) => {
console.log("我是同步任务,value = " + value)
return Promise.resolve(2)
}),
(reason) => { throw reason}
.then(
(value) => {
return new Promise((resolve, reject) => { //由于是异步任务,所以要封装在promise里,才能利用.then()让下面的函数等它
setTimeout(() => {
console.log("我是异步任务,value = " + value)
resolve(3) //别忘了resolve来确定匿名promise的状态,以出发下一个.then()里的回调函数
}, 2000)
})
},
(reason) => { throw reason } // 脑补了这一行
)
这样,异常就会顺着链条,直到最后的.catch()
来处理它。
问题6:如何中断promise链?
如果在promise链中间就处理了异常,那么此时.catch()
还是会返回一个新的promise对象,且无论它是resolved还是rejected,都会继续触发下面的.then()
。此时怎么办?应该返回一个永远都是pending的promise对象,就相当于是中断了promise链。什么是永远都是pending的promise对象呢?一个空的promise对象。
.catch(
(reason) => {
console.log("出错啦!错误代码: " + reason)
return new Promise(() => { }) //返回一个永远都是pending的promise对象,就相当于是中断了promise链
}
)
.then(
(data) => {
console.log("喂!上面的catch你不要再调用我啦~")
}
)