27

ES6语法——Promise对象

 3 years ago
source link: http://www.cnblogs.com/spoem/p/13329703.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

一、概念

Promise是异步编程的一种解决方案(解决回调地狱的问题),是一个能够获取异步操作信息的对象。Promise的内部保存着某个未来才会结束的事件(通常是一个异步操作)

二、特点

1.Promise对象的状态不受外界影响

Promise对象的状态由异步操作的结果决定当前处于pending(进行中)、fulfilled(已成功)还是rejected(已失败),任何其他操作都无法改变这个状态。

2.状态改变不可逆

一旦状态改变,就不会再变,任何时候都可以得到这个结果。promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对promise对象添加回调函数,也会立即得到这个结果。

3.缺点

(1)无法取消promise,一旦新建它就会立即执行,无法中途取消

(2)如果不设置回调函数,promise内部抛出的错误,不会反应到外部

(3)当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

4.优点

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

三、基本用法

Promise对象是一个构造函数,接受一个函数为参数,这个函数的参数是resolve和reject,它们两个也是函数。

resolve函数的作用是,将promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去,外部用then方法接收

reject函数的作用是,将promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去,外部用catch方法接收

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

四、API详解

1.promise.prototype.then()

then方法是定义在原型对象promise.prototype上的,它是异步操作成功的回调函数,是resolve函数的外部接收器。参数是一个函数。

工作原理:当promise对象执行异步操作成功时,会通过resolve函数向外传递操作结果,由then方法接收后,对该结果继续执行传入then方法内部的函数。

then方法返回的是一个新的promise实例(注意,不是原来那个promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

function async(a,b) {
    return new Promise(function (resolve,reject) {
        //异步代码
        setTimeout(function () {
            //加入数据格式的判断
            if(a!=undefined && (typeof a) =="number"){
                //如果数据格式正确 则调用resolve匹配外部的then
                resolve(a+b);

            }else{
                //数据格式错误 调用reject  匹配外部的catch
                reject(new Error("a is not a number,你瞎啊!!!"));

            }

        },200)
    });
    
}
var promise=async(1,2);
promise.then(function (res) {
    if(res>2){  
        console.log("我是3分支");
      return async(res,3)
    }

    //如果上一个then返回了一个promise 那么可以后面继续跟着then
}).then(function (res) {
    if(res>4){
        console.log("我是4分支");
        return async(res,4);
    }
    
})

2.promise.prototype.catch()

catch方法的使用、原理同then方法,只是catch方法是失败的回调函数。

注意:

(1)如果没有使用catch()方法指定错误处理的回调函数,promise 对象抛出的错误不会传递到外层代码,即不会有任何反应。

(2)catch方法只能有一个,有多个无意义,后面的catch并不会执行,catch方法通常写在最后

var user=new User("niujinghui","123456");
user.checkUsername()
.then(function (res) {
    return user.checkUserpwd();
})
 .then(function (res) {
        console.log("用户名密码都正确");
})
.catch(function (err) {
    if(err) throw err;
});

3.promise.prototype.finally()

finally()方法用于指定不管 promise 对象最后状态如何,都会执行的操作。该方法是 es2018 引入标准的。

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});

上面代码中,不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数。

finally方法源码

Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};

4.promise.all()

promise.all()用于将多个Promise实例包装成一个新的Promise实例。接受一个数组作为参数,如果该数组的元素不是promise对象实例,则使用Promise.resolve()方法将参数转为Promise实例再处理。另外,promise.all()方法的参数可以不是数组,但必须具有 iterator 接口,且返回的每个成员都是 promise 实例。

const p = Promise.all([p1, p2, p3]);

上面代码中p的状态由p1、p2、p3决定,有以下两种情况:

(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。 (都成功,传返回值数组,如果没有返回值,会组成元素为undefined的数组)

(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。 (一失败,传reject的返回值)

注意!!坑点来袭

1、如果作为参数的 promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发promise.all()的catch方法。如果没有自己的catch方法,就会调用promise.all()的catch方法。

2、使用all()方法时,传入的promise对象如果本身定义了then和catch方法,因为这两个方法都会返回一个新的promise实例,所以调用all方法得到的新promise状态一直都是成功。解释起来有点绕,结合下面的代码看就知道了。

const p1 = new Promise((resolve,reject)=>{
	let abc = 10
	let a = 10;
	let b = 20
	if(abc > 30){
		resolve(a+b) 
	}else{
		reject(b-a) 
	}
}).then(function(res){
	return res
}).catch(function(err){
	console.log(err)
})
const p2 = new Promise((resolve,reject)=>{
	let abc = 10
	let a = 10;
	let b = 20
	if(abc > 30){
		resolve(a+b+b) 
	}else{
		reject(b-a-a) 
	}
}).then(function(res){
	return res
}).catch(function(err){
	console.log(err)
})
const p3 = new Promise((resolve,reject)=>{
	let abc = 10
	let a = 10;
	let b = 20
	if(abc > 30){
		resolve(a*b) 
	}else{
		reject(b/a) 
	}
}).then(function(res){
	return res
}).catch(function(err){
	console.log(err)
})

const p=Promise.all([p1,p2,p3])
p.then(function(){
	console.log("正确")
}).catch(function(){
	console.log("错误")
})

上面的代码在abc的值小于30时,就会执行reject方法,当调用promise.all()时,应该会输出错误。但是因为p1、p2、p3都有各自的then和catch方法,返回了新的promise实例,又因为这个新实例已经处于完成状态,所以变量p才会一直处于成功状态。 (小炉写代码的时候还思考了好久,大坑!!!)

5.promise.race()

const p = Promise.race([p1, p2, p3]);

race方法的参数以及对参数中非promise对象的处理都和all方法一致,但是race方法返回的状态只和数组中最先改变状态的实例相同,那个最先改变的 promise 实例的返回值,就传递给p的回调函数。

注意:

(1)如果数组里面promise实例执行的速度一样,返回数组第一个promise执行的结果

(2)如果数组里面promise实例执行的速度不一样,返回最快的promise执行的结果,结果是失败就会直接匹配race的catch方法

(3)如果数组里面promise实例有成功、有失败的只要不是返回的结果,失败的就不影响

6.promise.allsettled()

promise.allsettled()方法接受一组 promise 实例作为参数,包装成一个新的 promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。该方法由 es2020 引入。

该方法返回的新的 promise 实例,一旦结束,状态总是fulfilled,不会变成rejected

const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
const results = await Promise.allSettled(promises);

// 过滤出成功的请求
const successfulPromises = results.filter(p => p.status === 'fulfilled');

// 过滤出失败的请求,并输出原因
const errors = results
  .filter(p => p.status === 'rejected')
  .map(p => p.reason);

7.promise.any()

promise.any()跟promise.race()方法很像,只有一点不同,就是不会因为某个 promise 变成rejected状态而结束。如果所有的promise实例都是rejected,则返回AggregateError: All promises were rejected

经过小炉自己试验,总结规则如下:

(1)如果数组里面promise实例执行的速度不一样,执行最快的promise结果为成功则返回,为失败则按照这个规则找第二快的,依次类推。

(2)如果数组里面promise实例执行的速度一样,则返回第一个成功的promise的返回值

8.promise.resolve()

作用:将现有对象转为 Promise 对象

参数情况:

(1)参数是一个 Promise 实例

如果参数是 promise 实例,那么promise.resolve将不做任何修改、原封不动地返回这个实例。

(2)参数是一个thenable对象

thenable对象指的是具有then方法的对象,promise.resolve方法会将这个对象转为 promise 对象,然后就立即执行thenable对象的then方法

let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};

let p1 = Promise.resolve(thenable);
p1.then(function(value) {
  console.log(value);  // 42
});

上面代码中,thenable对象的then方法执行后,对象p1的状态就变为resolved,从而立即执行最后那个then方法指定的回调函数,输出 42。

(3)参数不是具有then方法的对象,或根本就不是对象

如果参数是一个原始值,或者是一个不具有then方法的对象,则promise.resolve方法返回一个新的 promise 对象,状态为resolved。

(4)不带有任何参数

promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 promise 对象。

9.promise.reject()

promise.reject(reason)方法也会返回一个新的 promise 实例,该实例的状态为rejected。

const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))

p.then(null, function (s) {
  console.log(s)
});

注意:promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。这一点与promise.resolve方法不一致。

五、总结

1.promise的作用:Promise是异步编程的一种解决方案(解决回调地狱的问题)

2.无法取消promise,一旦新建它就会立即执行,无法中途取消

3.如果不设置回调函数,promise内部抛出的错误,不会反应到外部

4.当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

5.promise.then()成功的回调函数,返回的是一个新的promise实例,可以使用链式编程,参数来自resolve函数

6.promise。catch()失败的回调函数,只写一个,多写无意义,通常写在最后,参数来自reject函数

7.promise.all()监听所有的promise实例状态,全成功则成功,一失败则失败

8.promise.allsettled()监听所有的promise实例状态,等到所有这些参数实例都返回结果才结束,结束时状态总是fulfilled,不会变成rejected

9.promise.any()不会因为某个 promise 变成rejected状态而结束。如果所有的promise实例都是rejected,则返回AggregateError: All promises were rejected

10.promise.race()返回的状态只和数组中最先改变状态的实例相同,那个最先改变的 promise 实例的返回值,就传递给p的回调函数。

11.promise.finally()不管 promise 对象最后状态如何,都会执行的操作

12.promise.resolve()和promise.reject()都返回一个promise实例,但是promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK