Promise是ES6提出的一个新语法,用来优化异步代码的写法,是前端领域里的必会内容。
初始态pending:
创建Promise对象时,且没有调用resolve或者是reject方法,相当于是初始状态。这个初始状态会随着你调用resolve,或者是reject函数而切换到另一种状态。
let p = new Promise((ok,err)=>{ console.info('发呆.....' )}) console.dir(p)//此时为初始态pending
成功态resolved(
fulfilled ):
表示解决了,就是说这个承诺实现了。 要实现从pending到resolved的转变,需要在创建Promise对象时,在函数体中调用了resolve方法(即第一个参数)。
var p = new Promise((resolve,reject)=>{ resolved();}) console.dir(p)//此时状态为fullfilled
失败态rejected:表示这个承诺没有做到,失败了。 要实现从pending到rejected的转换,需要在创建Promise对象时,调用reject函数(即第二个参数)。
var p = new Promise((resolve,reject)=>{ reject()} ) console.dir(p)//此时状态为rejected
小结:
1.状态是可转化。
最初创建promise对象时,默认状态是pending,如果在函数体内部调用了第一个参数对应的函数,则状态变成了resolved;如果调用了第二个参数对应的函数,则状态变成了rejected。
pending ----- resolve() --> resolved; pending ----- reject() --> rejected;
2.状态转换是不可逆的。一旦从pending ---> resolved(或者是rejected),就不可能再回到pending,也不能由resolved变成rejected。
一个promise对象除了状态之外,还有promiseValue,这个值在调用resolve和reject方法时传入。
var p = new Promise( (resolve,reject) => { resolve(123); } ); // 此时,prommise对象p的状态是 resolved,值是123。 console.dir(p)
var p = new Promise( (resolve,reject) => { reject(123); } ); // 此时,prommise对象p的状态是 rejected,值是123 console.dir(p)
一旦状态发生了变化,就会进行then,或者是catch中去,同时把promiseValue传入对应的函数。
状态从pending变成resolved,进入then中,调用函数,并传入此时的promiseValue(就是调用resolve时传入的实参)
状态从pending变成rejected,进入catch中,调用函数,并传入此时的promiseValue(就是调用reject时传入的实参)
经典格式
// 许下承诺 const p= new Promise((resolve, reject) => { // 做异步操作 // 把状态从pending设为resolved,用实参来设置值 resolve(66) // 把状态从pending设为rejected,用实参来设置值 // reject(55) }) console.dir(p);//打印输出,可以观察状态和值 // 兑现承诺 p.then(res => { // 执行时机:当promise状态从pending ---> resolved时执行 // res是此时promise的值 console.log('成功了'); }).catch(err => { // 执行时机:当promise状态从pending ---> rejected时执行 // err是此时promise的值 console.log('失败了'); }).finally(res => { // 不了状态如何,都一定会执行此函数 console.log('永远会执行'); })
then方法的作用是为Promise对象添加状态改变时的回调函数,有两个参数。
// p 是一个promise对象 p.then(函数1[,函数2])//第二个函数可省略
它的两个参数都是函数。
第一个参数是resolved状态的回调函数。当p的状态从pending变成了resolved之后,函数1会执行。
第二个参数是rejected状态的回调函数。 当p的状态从pending变成了rejected之后,函数2会执行。
其中第二个参数是可选的,如果只写一个参数的话就是如下:
promise对象.then(函数1)
var p = new Promise((resolve,reject)=>{ resolve(val1); // or reject(val2) }) p.then((okVal)=>{ console.info("成功"); console.log(okValue); }, (errVal)=>{ console.info("失败"); console.log(errValue); })
它的两个参数都是函数,其执行逻辑是:
如果promise对象的状态是resolved,则then()会执行第一个函数,并传入当前的PromiseValue(即上面的val1);
如果promise对象的状态是rejected,则then()会执行第二个函数,并传入当前的PromiseValue(即上面的val2);
特别地,如果promise对象的状态是rejected,且此时then方法并没有设置第二个参数,就会向外抛出一个错误,错误的提示大约是Uncaught (in promise)
。
var p = new Promise((resolve,reject)=>{ resolve(1)//主动调用resolve,并传入 }) // 此时,P的状态是resolved,且值promiseValue 是1. p.then((res)=>{ // 因为p的状态是resolved,所以自动执行then的第一个参数,并且把promisevalue传进来。 console.log("then,ok",res) }) //执行输出结果是:`then,ok,1`
var p = new Promise((resolve,reject)=>{ reject(2)//主动调用reject,并传入实参 }) // 此时,P的状态是rejected,且值promiseValue 是2. p.then((res)=>{ // 因为p的状态是resolved,所以这句代码不会执行。 console.log("then,ok",res) },(err)=>{ // 因为p的状态是rejected,所以自动执行then的第二个参数,并且把promisevalue传进来。 console.log("then,err",err) }) //执行输出结果是:`then,err,2`
then()方法的返回值也是一个promise对象,所以它支持链式写法。但是要注意的是它的返回值是一个新的promise对象,与调用then方法的并不是同一个对象。
var p1 = new Promise(()=>{}); var p2 = p1.then(function f_ok(){}, function f_err(){}); // p2也是一个promise对象。 console.log(p1 === p2); // false
p2的状态及promiseValue按如下规则来确定
如果p1的状态是pending,则p2的状态也是pending。
如果p1的状态是resolved,then()会去执行f_ok,则p2的状态由f_ok的返回值决定。
如果f_ok返回值不是promise对象,则p2的状态是resolved,且p2的promiseValue就是f_ok函数的return值。
如果f_ok返回值是一个promise对象,则p2的状态及promiseValue以这个promise对象为准。
如果f_ok这个函数内部发生了错误(或者是用户主动抛出错误),则p2的状态是rejected,且p2的promiseValue就是这个错误对象。
如果p1的状态是rejected,then()会去执行f_err,则p2的状态由f_err的返回值决定。
如果f_err返回值不是promise对象,则p2的状态是resolved,且p2的promiseValue就是f_err函数的return值。
如果f_err返回值是一个promise对象,则p2的状态及promiseValue以这个promise对象为准。
如果f_err这个函数内部发生了错误(或者是用户主动抛出错误),则p2的状态是rejected,且p2的promiseValue就是这个错误对象。
Promise.prototype.catch 是 Promise.prototype.then(null, reject)的别名,用于指定当promise对象的状态从pending变成rejected的回调函数 。
var p1 = new Promise((resolve,reject)=>{ reject('s') }); p1.catch(function(err){ console.log(err); }) // 与下面的代码等价 p1.then(null, function(err){ console.log(err); })
单独使用catch没有什么意义,它一般与then一块使用。如下:
new Promise((resolve,reject)=>{ }).then(function(result){ // 如果promise对象的状态是resolved的,就会到这里来,并且result会接收promiseValue的值 }).catch(function(err){ // 如果promise对象的状态是rejected的,就会到这里来,并且err会接收promiseValue的值 }) // 上面的代码如何拆分来写的话,等价于: var p1 = new Promise((resolve,reject){ }); var p2 = p1.then(function(result){ }); var p3 = p2.catch(function(err){ })
catch的返回值仍是一个promise对象,确定它的值的方式与then(null,(errVal)=>{ })
的方式一致。
function do1() { console.log("任务1"); } function do2() { console.log("任务2"); } function do3() { console.log("任务3"); } function do4() { console.log("任务4"); } var p = new Promise((resolve,reject)=>{ resolve()}) p.then(do1) .then(do2) .then(do3) .then(do4); //结果输出是:`任务1,任务2,任务3,任务4`
第一步:由于p的状态是resolved,所以p.then(do1)中,do1函数会执行。输出任务1
。
第二步:确定p1的状态。按前面关于then的部分的介绍,p1的状态由do1()来决定。因为do1并没有明确指定返回值,则返回值就是undefined. p1的状态就是resolved。
第三步:由于p1的状态是resolved,所以p1.then(do2)会继续执行do2。输出任务2
,且p2的状态由do2来决定。与第二步的分析相同,p2的状态仍是resolved。
第四步:接下来看p3。由于p2的状态是resolved,所以p2.then(do3)会继续执行do3。输出任务2
,且p3的状态由do3来定,仍是resolved。
最后:p3.then(do4)。由于p3的状态是resolved,所以执行do4。输出任务4
。
function do1() { console.log("任务1"); } function do2() { console.log("任务2"); } function do3() { console.log("任务3"); } function do4() { console.log("任务4"); } var p = new Promise((resolve,reject)=>{ resolve()}) p.then(do1) .then(do2) .catch(do3) .then(do4); //的执行结果是:`任务1, 任务2, 任务4`
第一步:由于p的状态是resolved,所以p.then(do1)中,do1函数会执行。输出任务1
。
第二步:确定p1的状态。按前面关于then的部分的介绍,p1的状态由do1()来决定。因为do1并没有明确指定返回值,则返回值就是undefined. p1的状态就是resolved。
第三步:由于p1的状态是resolved,所以p1.then(do2)会继续执行do2。输出任务2
,且p2的状态由do2来决定。与第二步的分析相同,p2的状态仍是resolved。
第四步:接下来看p3。由于p2的状态是resolved,所以它并不会执行do3, p3的状态没有变化,仍保持p2的状态:resolved。
最后:p3.then(do4)。由于p3的状态是resolved,所以执行do4。输出任务4
。
function do1() { console.log("任务1"); console.log(abc); //故意犯错 } function do2() { console.log("任务2"); } function do3() { console.log("任务3"); } function do4() { console.log("任务4"); } var p = new Promise((resolve,reject)=>{ resolve()}) p.then(do1) .then(do2) .then(do3) .catch(do4); //执行结果是:`任务1, 任务4`。
第一步:由于p的状态是resolved,所以p.then(do1)中,do1函数会执行,输出任务1
,同时由于这里有段错误代码,所以p1的状态就是rejected。
第二步:由于p1的状态是rejected,所以p1.then(do2) 就不会执行do2,同时p2的状态也是rejected。
第三步:对p3的分析也是如此。
第四步:p3的状态是rejected,所以会执行do4。
function increment(value) { return value + 1; } function doubleUp(value) { return value * 2; } function output(value) { console.log(value);// => (1 + 1) * 2 } var p = Promise.resolve(1); p.then(increment) .then(doubleUp) .then(output) //输出结果是 4
// 状态:resolved; promiseValue: 1 var p = Promise.resolve(1); // p的状态是resolved; // 在执行then的第一个参数,即increment时 // 把p的 promiseValue 1 传入 // increment执行后的返回值就是2 // p1的状态是resolved,promiseValue是2 var p1 = p.then(increment); // p1的状态是resolved; // 在执行then的第一个参数,即doubleUp时 // 把p1的promiseValue 2传入 // doubleUp执行后的返回值就是4 // p2的状态是resolved,promiseValue是4 var p2 = p1.then(doubleUp); // p2的状态是resolved,promiseValue是4 // 在执行then的第一个参数,即output时 // 把p2的promiseValue传入,就是4 var p3 = p2.then(output) ;