背景
- 博主在网络上查了各种关于promise的资料,结果还是弄不懂到底怎么转化成实际当中的运用。网上的资料都是Promise写个settimout然后做例子。尼玛这谁都知道promise完后能.then运行,但一到具体场景就不会用了,顶多就是用人家本来就是Promise包装好的进行.then玩,真正自己包装不会。后来博主白嫖到一个讲Promise的视频,终于把这玩意理清楚了。真的是理解不了最好的方法就是找视频学,一个视频讲的不行找另一个。
Promise运行顺序
基本形式
- 我个人认为以前不了解promise的真正原因是不了解运行顺序。虽然看别人文章大家都知道是成功运行resolve,失败运行reject,但还是蒙逼状态。所以,最先理解的应该是promise的运行顺序。
let p = new Promise(function(resolve,reject){
$.ajax({
url:'xxxxx',
success(res){resolve(res)},
err(res){reject(res)}
})
});
- 首先看上面这种 基本形式,在p进行new Promise的时候,Promise函数体内的内容已经开始执行了,上面这个例子就是已经发送了ajax请求去了,然后回来的这个resolve和reject是需要在后面的.then当中定义,如果new 一个Promise后不.then,它就发一个请求就没了。
p.then(result=>{console.log(result)},msg=>{console.log(msg)})
- 上面对p进行.then操作,then里面2个参数是2个function,实际是分别赋给了前面new Promise时里面那个function中的参数。也就是
result=>{console.log(result)}
给了p=new Promise(function(resolve,reject){})
中的resolve。同理,msg那个函数当作参数给了reject。我觉得这句是理解关键。所以没有then只定义promise其实是不完整的。
链式调用
- 我们都知道promise完后.then后还能.then,但是每个.then实际上它管的状态不是一个东西。拿上面那个例子举例,如果p发送ajax我们当作成功,那么他就会执行.then中的第一个参数function,如果这个function中有错,那么第二个.then会执行它的第二个参数function。所以也就是说.then返回的其实也是个Promise,并且下一个.then中它会判断前一个Promise也就是.then的执行是成功还是失败,如果成功,那么就走它第一个参数,失败走第二个参数。
- 根据上面的性质,.then中的函数可以手动封装一个promise并return,这样就会让下一个.then等待这个Promise完成后执行。具体例子看后面的参数传递。
参数传递
- 在第一个p.then中,如果.then里return了一个值,那么这个值就会作为参数传递给下一个.then,如果里面没有返回值,那么参数就是undefined
- 有返回值的promise和没返回值的promise区别很大,看下面这个例子:
let a = function (){
return new Promise(function (resolve,reject) {
setTimeout(() => {
console.log(1);
resolve()
}, 1000);
})
}
let b = function (){
return new Promise(function(resolve,reject){
setTimeout(() => {
console.log(2);
resolve()
}, 1000);
})
}
a().then(()=>b()).then(()=>{console.log('完成')});
a().then(()=>{b()}).then(()=>{console.log('二次完成')});
- 我们封装了2个Promise,第一条语句是在a运行完后返回b,第二条是不返回b,最后输出结果是:
1
1
二次完成
2
2
完成
- 可以发现,返回b的Promise后会等待Promise完成后执行,但是如果不返回Promise就会不等待b的执行直接进行下一个.then操作。
catch语句
- 一般是不建议写reject的,因为Promise有catch语句,总写reject冗余,而且reject中的语句如果没有错误,那么下一个,then就会执行resolve而不是reject。
- catch语句也是比较特殊的,我们看下面这个例子:
let a = function (){
return new Promise(function (resolve,reject) {
setTimeout(() => {
console.log(1);
reject()
}, 1000);
})
}
let b = function (){
return new Promise(function(resolve,reject){
setTimeout(() => {
console.log(2);
resolve()
}, 1000);
})
}
a().then(()=>b(),()=>2).then(()=> 5).then(()=>{console.log('sss1')}).catch(()=>{console.log("抓到错误1")})
a().then(()=>b()).then(()=> 5).then(()=>{console.log('sss2')}).catch(()=>{console.log("抓到错误2")})
1
1
sss1
抓到错误2
- 我们把上面的例子修改了下,a变为失败走reject,那么实际上,如果没有写.then中的第二个function的话,后面的.then统统不会运行,导致catch能捕捉到错误。但是如果写了then中的第二个function,那么后面运行全都正确,catch是抓不到错误的。
- 再看下一个例子
let a = function (){
return new Promise(function (resolve,reject) {
setTimeout(() => {
console.log(1);
resolve()
}, 1000);
})
}
let b = function (){
return new Promise(function(resolve,reject){
setTimeout(() => {
console.log(2);
reject()
}, 1000);
})
}
a().then(()=>b()).catch(()=>{console.log("抓到错误1")})
- 前面我们把a中reject了,b中正常,这次调换一下,a中正常,b中reject。最后运行结果是能抓到错误。
- 所以,这个catch,它可以抓到上一级错误和同级的.then的错误,相当于管了2层,但是更多层不行。
基本封装
- 其实就是上面的例子,把要完成的异步操作做成一个function,这样异步操作不会立即执行,function会返回promise,这样等function调用时候,就相当于立即生成了个Promise,就可以立马进行.then操作了。
实际应用
- 这个就是类似于上面操作,先封装各个异步操作,然后.then的时候return需要执行的下一个异步操作。参考上面参数传递的例子。