跳至主要內容

18. Promise

Harry Xiong大约 9 分钟Web 前端JavaScriptPromise 对象JS 异步async 与 await

Promise

18.1 Promise含义

promise是异步编程的一种解决方法。

所谓promise,简单说是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,从语法上说,promise是一个对象,从它可以获取异步操作的消息,promise提供了统一的API,各种异步操作都可以用同样的方法进行处理。

Promise对象代表一个异步操作,有着三种状态:

  • pending,正在进行状态

  • fulfilled,已成功状态

  • rejected,已失败状态

**注意:**只有异步操作的结果可以决定当前是哪一种状态,任何其它的操作都无法改变该状态

18.2 Promise对象的特点

(1)对象的状态不受外界影响,promise对象代表一个异步操作,有三种状态,pending(进行中)、fulfilled(已成功)、rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态,这也是promise这个名字的由来“承若”;

(2)一旦状态改变就不会再变,任何时候都可以得到这个结果,promise对象的状态改变,只有两种可能:从pending变为fulfilled,从pending变为rejected。这时就称为resolved(已定型)。如果改变已经发生了,你再对promise对象添加回调函数,也会立即得到这个结果,这与事件(event)完全不同,事件的特点是:如果你错过了它,再去监听是得不到结果的。

有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

Promise也有一些缺点。首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

18.3 Promise的用法

Promise是一个构造函数,这个构造函数里有两个参数,分别是:resolve(成功之后的回调函数)、reject(失败之后的回调函数)。

因为promise表示的是一个异步操作,每当我们new一个promise实例,就表示一个具体的异步操作,那么这个异步操作的结果就只能有两种状态:成功/失败,两者都需要回调函数resolve/reject返回。所以内部拿到操作的结果后,无法使用return把操作结果返回给调用者,这时候只能用回调函数的形式来把成功或失败的结果返回给调用者。

var promise = new Promise( function (resolve, reject) {
	if (true) {
		resolve(value)
	} else {
		reject(error)
	}
})

promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数

promise.then( (value) => {
	// success
}, (error) => {
	// failure
})

then方法可以接受连个回调函数作为参数,第一个回调函数是promise对象的状态变为resolved时调用,第二个回调函数是promise对象的状态变为rejected时调用,其中,第二个函数是可选的,不一

定要提供,这两个函数都接受promise对象传出的值作为参数;

通过then指定回调函数的时候,成功的回调函数必须传,失败的回调函数可以胜利。

Promise可以作为一个构造函数对象,传入的两个参数都是函数,第一个函数是代表成功,第二个代表失败,每个函数调用后都会在then()中找到自己对于的函数来执行

var p=new Promise(function(success,rejected){
    setTimeout(function(){
        try{
            console.log(123);
            success(456);
        }catch{
            rejected("error");//在一次操作中之后进行success()函数和rejected()函数中的一个,执行
        }                   //完毕后直接到下一个步骤
    },1000)
}).then(function(data){//执行success()后进入该函数
    console.log(data);//456
},function(err){//执行rejected()后进入该函数
    console.log(err);//error
})
/*
then()函数中的第一个回调函数中的参数就是在前一个异步操作中通过success()传的值,而第二个回调函数中的参
数是在前一个异步操作中rejected()传的值
*/
//Promise正确的用法是实现链式操作来实现异步
var p=new Promise(function(success,rejected){
    console.log(123);
    success(456);
}).then(function(data){//假设现在只有成功才执行函数
    console.log(data);//456
    return 789;
    /*
    如果这里不写返回值,默认会返回undefined给下面then()中成功时的函数,注意的是这样写无论写什么普通的    数据类型都是成功的,都是为下面的then()函数传递了成功时的Promise
    */
}).then(function(data){
    console.log(data);//789
})
//链式操作中失败的时候可以执行的异步操作
var p=new Promise(function(success,rejected){
    try{
        console.log(123);
        success(456);    
    }catch{
        rejected("error");
    }
}).then(
    function(data){
    
    console.log(data);//456
    return new Promise(function(success,rejected){
            try{
                success(789)
            }catch{
                rejected("error");
            }
        });
},
    function(err){
    console.log(err);//error
    //失败就不会执行后面的then()了
}).then(
    function(data){
    console.log(data);//789
    return new Promise(function(success,rejected){
             rejected("error");//直接显示失败
        });
    },
    function(err){
    console.log(err);//error
}).catch(
    function(err){
        console.log(err);//error
    })
/*
可以在最后加上catch()代表着只有在失败时才会进行的方法,该方法可以看作是then()失败时的简写,因为then()
要写失败时的情况必须要传入两个数,第二个才是失败时的参数函数,用catch()可以值捕获失败时的结果
*/

18.4 异常捕获

then() 和 .catch()

Promise构造器接受一个函数作为参数,这个函数有两个参数:resolve,reject,分别代表这个Promise实例成功之后的回调函数和失败之后的回调函数。

举个例子:

var promise = new Promise(function (resolve, reject) {
   var a = 1
   if (a == 1) {
     resolve(a)
   } else {
     reject(error)
   }
 })
 promise.then(function (value) {
   console.log(value);
 }).catch(function (error) {
   console.log(error);
 })

// 输出:
 // 1

不管是then方法还是catch方法返回的都是一个新的Promise实例,这意味着Promise可以链式调用then和catch,每一个方法的返回值作为下一个方法的参数:

var promise = new Promise(function (resolve, reject) {
    var a = 1
    if (a === 1) {
        resolve(a)
    } else {
        reject(error)
    }
})
promise.then(function (value) {
    console.log(value++)
    return value
}).catch(function (error) {
    console.log(error)
}).then(function (value) {
    console.log(value++)
})
// 输出:
// 1
// 2

如果其中一个then失败了,它后面第一个catch方法就会接受这个错误并执行,然后继续执行后面的方法,比如:

var promise = new Promise(function (resolve, reject) {
    resolve()
})
promise.then(function (value) { // 抛出错误
    console.log(1 / x)
}).then(function () { // 此方法不会执行
    console.log('This "then" method will not happend!')
}).catch(function (error) { // 接受错误,并打印出错误
    console.log('Have an error: ',error)
}).then(function () { // 此方法会执行
    console.log('This "then" method  will happend!')
})
// 输出:
/*
Have an error:  ReferenceError: x is not defined
    at /Users/linxueying/Library/Mobile 
    ......
This "then" method  will happend!
*/

18.5 静态方法

18.5.1 Promise.all()

Promise.all()方法用于将多个Promise实例包装成一个新的Promise实例

var p1=new Promise(function(){});
var p2=new Promise(function(){});
var p3=new Promise(function(){});
var p=Promise.all([p1,p2,p3]).then(function(data){
    console.log(data);
},function(err){
    console.log(err);
});
/*
    p内部的状态由p1,p2,p3决定,有两种情况:
    1.只有p1,p2,p3全部成功时p的状态才能使成功,p才能够执行后面成功的回调函数,此时data的值是由             p1,p2,p3共同传入的值组成的数组
    2.只要p1,p2,p3中有一个失败,p的状态就会变成失败,p执行失败后的回调函数,此时err的值为首先完成异步并      返回失败的Promise的返回值
*/

注意:

  • Promise,all()中包含多个Promise实例的数组,只有这多个实例全部成功,或者至少有一个失败时才会调用后面then()中的回调函数

  • 如果作为参数的Promise实例自己定义了catch()方法,那么该实例一旦失败不会触发Promise.all()后面定义的catch()方法,如果没有实例定义catch()方法,那么触发失败后会执行Promise.all()后面的catch()方法

18.5.2 Promise.race()

Promise.race()方法也用于将多个Promise实例包装成一个新的Promise实例

var p1=new Promise(function(){});
var p2=new Promise(function(){});
var p3=new Promise(function(){});
var p=Promise.all([p1,p2,p3]).then(function(data){
    console.log(data);
},function(err){
    console.log(err);
});
/*
    p内部的状态由p1,p2,p3其中一个决定,只有其中有一个先完成异步操作,p的状态就会跟着发生改变,如果先发生    成功则执行第一个回调函数,如果先发生失败则执行第二个回调函数
*/

18.5.3 Promise.resolve()

Promise.resolve()方法用于将现有的对象转换为Promise对象,并且该对象中的值就是成功时的函数传入的参数

var p=Promise.resolve(123);
//等价于
var p=new Promise(success,rejected){
    success(123);
}

该方法的参数有多种情况

  • 参数为一个Promise实例 不作任何改变,直接返回这个Promise实例

  • 参数为一个具有then()方法的thenable对象

    Prommise.resolve()方法会将这个对象转换为Promise对象,然后立即执行里面的then()方法

    var thenable={
        then:function(success,rejected){
            console.log(123);//123
            success(456)
        }
    };
    var p = Promise.resolve(thenable).then(function(data) {
            console.log(data);//456
    });
    /*
        上面的代码会在控制台上打印出来
    */
    
  • 参数为一个普通值类型的或者不是thenable对象时 会直接将传入的参数作为成功时的参数传入到then()的成功函数的参数中

  • 没有参数 直接得到一个成功时不传入参数的Promise对象

18.5.4 Promise.reject()

Promise.reject()方法也用于将现有的对象转换为Promise对象,并且该对象中的值就是失败时的函数传入的参数

**注意:**Promise.reject()方法中传入的参数会原封不动的作为失败时的错误理由,不会发生参数的改变,和Promise.resolve()方法有区别

var p=Promise.reject("error");
//等同于
var p=new Promise(function(success,rejected){
    rejected("error");
})

18.6 Promise优缺点

  • 优点 Promise的状态不会受到外界的影响,并且一旦Promise的状态发生改变时,就不会再进行状态变化,任何时刻都可以得到这个状态结果

  • 缺点

    • 无法取消Promise,一旦新建就会立即执行,无法在中途取消
    • 如果不设置回调函数,Promise的内部机制会报错,不会反应到外部
    • 当处于pending正在执行的状态时,不能知道当前在哪一个阶段,不知道是否是开始还是结束