Promise

2020年2月24日

概念

Promise 对象用于表示一个异步操作的最终完成 (或失败), 及其结果值.让异步方法可以像同步方法那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的promise对象.

Promise有三种状态,就好像你向一个女孩表白

  • 表白过程中: 不知道她会是拒绝还是同意,先鼓起勇气表白就对了
  • 表白成功: 兴奋得跳起来,然后做下一步计划
  • 表白失败: 找找原因,想想为什么会失败

Promise的三种状态分别为:

  • pending: 初始状态,既不是成功,也不是失败状态。
  • fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失败。

无论执行的结果是成功还是失败,Promise 对象的 then方法绑定的处理方法就会被调用,then有俩个为Function 类型的参数:

  • onfulfilled:成功时调用
  • onrejected:失败时调用

catch() 方法和then失败调用时函数的作用相同,可以理解为其简写.

同时Promise.prototype.thenPromise.prototype.catch 方法返回promise 对象,所以它们可以被链式调用。

实例

function sayLove(){
    new Promise(function (resolve, reject) {
        var love = Math.random() * 2 //随机生成 0 - 2 之间的随机数
        setTimeout(function () {
            if (love > 1) {
                resolve('表白成功');
            }
            else {
                reject('表白失败');
            }
        }, love * 1000);
    }).then(function (result) {
        console.log(result +',进行下一步计划');
    }).catch(function (reason) {
        console.log(reason+ ',总结原因,继续努力');
    });
}
sayLove()

catch方法还有另一个作用,就是在执行resolve的回调时,如果抛出出错了,那么并不会报错卡死js,而是会进到这个catch方法中。

function sayLove(){
    new Promise(function (resolve, reject) {
        var love = Math.random() * 2 //随机生成 0 - 2 之间的随机数
        setTimeout(function () {
            if (love > 1) {
                resolve('表白成功');
            }
            else {
                reject('表白失败');
            }
        }, love * 1000);
    }).then(function (result) {
        console.log(result +',进行下一步计划')
        console.log(plan);//plan 未定义
    }).catch(function (reason) {
        console.log(reason+ ',总结原因,继续努力')
    })
}
sayLove()
//输出

方法

all()

all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。

简单来说就是等待速度最慢的人完成后一起回调,如果一人出错,全部完蛋.

const p = Promise.all([p1, p2, p3]);
  • 只有p1、p2、p3的状态都变成fulfilled(成功)p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
  • 只要p1、p2、p3之中只要有一个被rejected(失败)p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
function sayLove(id){
    return new Promise(function (resolve, reject) {
        var love = Math.random() * 2 //随机生成 0 - 2 之间的随机数
        setTimeout(function () {
            if (love > 0) {//这里改为 0,让所有测试通过
                resolve(id+'表白成功');
            }
            else {
                reject(id+'表白失败');
            }
        }, love * 1000);
    })
}

const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
  return sayLove(id);
})
Promise.all(promises).then(function (results) {
  console.log(results);
}).catch(function(reason){
 console.log(reason)
})

//输出:["2表白成功", "3表白成功", "5表白成功", "7表白成功", "11表白成功", "13表白成功"]

//若有一个失败,便会执行catch,例如:5表白失败

race()

race 方法是当任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄,并返回该promise对象。

就是谁跑得快,就执行谁.

function sayLove(id){
    return new Promise(function (resolve, reject) {
        var love = Math.random() * 2 //随机生成 0 - 2 之间的随机数
        setTimeout(function () {
            if (love > 1) {//这里改为 0,让所有测试通过
                resolve(id+'表白成功');
            }
            else {
                reject(id+'表白失败');
            }
        }, id * 1000) //改变每个执行的时间
    }).then(function (result) {
        console.log(result +',进行下一步计划')
    }).catch(function (reason) {
        console.log(reason+ ',总结原因,继续努力')
    })
}

const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
  return sayLove(id);
})
Promise.race(promises).then(function (results) {
  console.log(results);
}).catch(function(reason){
 console.log(reason)
})
//会先输出: 2表白成功,进行下一步计划
//然后陆续输出:3,5,7,11,13 的结果

// 若子 promise 对象没有 then 回调函数
// 那么只会输出 2表白成功

raceall方法不同,谁的速度最快就执行谁,无论它是失败还是成功.所以通常会被用来给一个请求设定请求时间,如果超过限定时间,就返回失败.

const p = Promise.race([
  fetch('/resource-that-may-take-a-while'),
  new Promise(function (resolve, reject) {
    setTimeout(() => reject(new Error('request timeout')), 5000)
  })
]);

p
.then(console.log)
.catch(console.error);

上面代码中,如果 5 秒之内fetch方法无法返回结果,变量p的状态就会变为rejected,从而触发catch方法指定的回调函数。

allSettled()

该方法与 all方法一样,会等待所以执行完成才回调返回一个数组,不同的是它不会在乎子 promise 是否成功.该方法由 ES2020 引入。

返回例子如下:

any()

any()只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。

简单来说就是除非所有子 promise都失败,否则就会返回成功.不过这个方法的返回值还未确定,该方法目前是一个第三阶段的提案 。

Promise.any()Promise.race()方法很像,只有一点不同,就是不会因为某个 Promise 变成rejected状态而结束。

实现

all

function promiseAll (promises){
  return new Promise(function(resolve, reject) {
    if(!Array.isArray(promises)) {
      throw new TypeError('argumenbt must be an array')
    }
    var resolvedCounter = 0;
    var promiseNum = promises.length;
    var resolvedResult = [];
    for(let i = 0; i < promiseNum; i++) {
      Promise.resolve(promises[i]).then(value => {
        resolvedCounter++;
        resolvedResult[i] = value;
        if(resolvedCounter === promiseNum) {
          return resolve(resolvedResult);
        }
      }, error => {
        return reject(error)
      })
    }
  })
}

allSettled

function allSettled(promises) {
  if (promises.length === 0) return Promise.resolve([])
  
  const _promises = promises.map(
    item => item instanceof Promise ? item : Promise.resolve(item)
    )
  
  return new Promise((resolve, reject) => {
    const result = []
    let unSettledPromiseCount = _promises.length
    
    _promises.forEach((promise, index) => {
      promise.then((value) => {
        result[index] = {
          status: 'fulfilled',
          value
        }
        
        unSettledPromiseCount -= 1
        // resolve after all are settled
        if (unSettledPromiseCount === 0) {
          resolve(result)
        }
      }, (reason) => {
        result[index] = {
          status: 'rejected',
          reason
        }
        
        unSettledPromiseCount -= 1
        // resolve after all are settled
        if (unSettledPromiseCount === 0) {
          resolve(result)
        }
      })
    })
  })
}

总结

  • then: 通常执行成功回调
  • catch: 通常执行成功回调
  • all: 通常用于异步并发
  • race: 通常用于限定请求时间
  • allSettled: 不在乎子 promise 结果,全部返回
  • any: 还在提案阶段

参考资料: