Promise 是异步编程的一种解决方案,与传统的回调函数和事件相比,Promise的优点是代码可以让我们更容易理解,我们可以更加清晰的知道代码的走向,避免回调函数嵌套陷入回调地狱中。
一、三种状态
Pending
(进行中)、Resolved
(已完成,又称 Fulfilled)和Rejected
(已失败),Promise的状态只能从Pending
变为Resolved
或者从Pending
变为 Rejected
。
即使改变已经发生,而且当时并没有为Promise添加回调函数,之后再为Promise添加回调函数依然会执行,这和事件回调函数完全不同。
二、常见用法
1.Promise
创建一个promise实例:
var p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(0);
},5000)
});
Promise构造函数接受一个函数作为参数,函数的第一个参数(resolve)将promise状态从pending
变成resolved
(成功),第二个参数(reject)将状态从pending
变成rejected
(失败)。
2. then
then方法接受两个函数作为参数,分别作为promise状态变为resolved和rejected时的回调函数:
p1.then((data)=>{
console.log(`resolve${data}`);
},e=>{
console.log(`reject-${e}`);
})
结果:reject-0
then方法返回的仍然是一个Promise对象,可以继续调用then方法,新的then方法接受的数据是前一个的返回值
var p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(0);
},5000)
});
p1.then((data)=>{
console.log(`resolve${data}`);
},e=>{
console.log(`reject-${e}`);
})
.then(data=>{
console.log(`resolve2-${data}`)
},e=>{
console.log(`reject2-${e}`)
});
结果:
reject-0
resolve2-undefined
因为我们第一个then方法没有return任何值,所以第二个输出是undefined。
我们发现即使第一个then方法调用的是rejected
状态的回调,但是第二个then方法仍然调用了resolved
状态的回调,其实只要then方法没有throw Error
,后面的then方法都会执行resolved状态,如果抛出异常就会执行rejected
3.catch
当promise操作及then方法的过程中抛出异常,promise状态就变成了rejected。此时走接下来then中的rejected,如果仅仅为了处理异常,接下来的then方法可以这样写:
p1.then(null,reject)
reject接受的是抛出的异常信息,catch
方法就是这种写法的简写:
p1.catch(reject)
如果Promise状态已经变成Resolved,再抛出错误是无效的。因为Promise 的状态一旦改变,就永久保持该状态,不会再变了
Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止,只要被捕获就不会继续传递(注意:catch和then中的reject方法是一样的,两者都可以捕获异常),所以我们可以在then中不写reject,在最后一个then后面写catch
Promise中的异常及时不处理也不会引起程序异常
4.Promise.all([p1,p2,p3…])
Promise.all接受一个Promise实例数组,如果不是,就会先调用Promise.resolve
方法转为Promise实例。
var p = Promise.all([p1, p2, p3]);
此时p的状态由所有的Promise实例参数的状态所决定:
1. 如果所有的状态都是resolved,则p是resolved,p的返回值是所有实例返回值组成的数组,数组的顺序和定义p时的顺序一致 2. 如果有一个参数的状态是rejected,则p的状态是rejected,p的返回值是第一个状态为rejected的实例的返回值,也是数组形式
例1:
var p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('p1');
},2000)
}).then(data=>{return 'p2';});
Promise.all([p1,22,33,44,55])
.then(data=>{
console.log(data);
},(e)=>{
console.log(e);
});
结果:
[ 'p2', 22, 33, 44, 55 ]
我们可以看到返回值是一个数组,而且p1的返回值是最终then的返回值,不是仅仅Promise实例时的值
例2:
var p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('p1');
},2000)
}).then(data=>{
throw Error(`p1 is wrong`);
});
var p2=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('p2');
},1000)
}).then(data=>{
throw Error(`p2 is wrong`);
});
Promise.all([p1,p2,33,44,55])
.then(data=>{
console.log(data);
},(e)=>{
console.log(e);
});
结果:
[Error: p2 is wrong]
因为p1,p2状态为rejected,而且p2 1s延迟先执行完,所以结果为p2的返回值。
5. Promise.race([p1,p2,p3…])
Promise.race方法同样是将多个Promise实例,包装成一个新的Promise实例。
var p = Promise.race([p1, p2, p3]);
Promise.race
与 Promise.all
接受的参数一样,不是Promise实例也会先resolve转换。
Promise.race与名字一样,多个实例就像在赛跑一样,谁先执行完毕最后p的状态就是那个先执行完毕的,那个率先改变的 Promise 实例的返回值,就传递给p的回调函数
例1:
var p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('p1');
},2000)
}).then(data=>{
// throw Error(`p1 is wrong`);
return 'p11111';
});
var p2=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('p2');
},1000)
}).then(data=>{
// throw Error(`p2 is wrong`);
return 'p222222';
});
Promise.race([p1,p2,33,44,55])
.then(data=>{
console.log(data);
},(e)=>{
console.log(e);
});
结果:
33
所有实例状态都是resolved,但是p1、p2都有延迟,所以第三个先执行完。
例2:
var p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('p1');
},2000)
}).then(data=>{
throw Error(`p1 is wrong`);
});
var p2=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('p2');
},1000)
}).then(data=>{
throw Error(`p2 is wrong`);
});
var p3=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('p3');
},1000)
});
Promise.race([p1,p2,p3])
.then(data=>{
console.log(data);
},(e)=>{
console.log(`catch:${e}`);
});
结果:
catch:Error: p2 is wrong
因为p2先执行完切抛出异常,所以p最后状态也是rejected,接受p2返回值