promise 入门

161 阅读8分钟

基础概念

Promise 是异步编程的一种解决方案。 有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。

特点

1.对象的状态不受外界影响 (3种状态)

  • 1.pending (进行中)
  • 2.Fulfilled (已成功)
  • 3.Rejected (已失败) 2.一旦状态改变就不会再改变 (成功或失败)
  • Pending -> Fulfilled
  • Pending -> Rejected

3.用法

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

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 和 reject,他们是两个函数,由 js 引擎提供,不用自己部署。 resolve 是由 未完成 -> 已完成的状态,在异步操作成功时调用,并将异步操作的结果作为参数传递出去。rejected 是由未完成 -> 失败的状态,在异步操作失败是调用,resolve 是由 未完成 -> 已完成的状态,在异步操作成功时调用,并将异步操作的结果作为参数传递出去。rejected 是由未完成 -> 失败的状态,在异步操作失败是调用,并将异步操作的结果作为参数传递出去。

  • promise 实例生成以后,可以用 then 方法分别指定 resolved 状态和 rejected 状态的回调函数
  • Promise 新建后就会立即执行。
var promise = new Promise(function(resolve, reject) {
      if(true) {
        resolve(value)
      } else {
        reject(error)
      }
})
promise.then(function(res) {
  // 成功回调
},function(error) {
  // 失败回调
})
const p1 = new Promise(function(res,reject) {
  // reject(new Error('fail'))
  res('小仙女')
  console.log(123) // 123
})
const p2 = new Promise(function(res,reject) {
  res(p1)
})
p2.then((res)=>{
  console.log(res,1) // 小仙女
})
.catch((res)=>{
  console.log(res,2)
})

p1 , p2 都是 promise 实例,但是 p2 的 resolve 方法将 p1 作为参数传递过去,既一个异步操作的结果是返回另一个异步操作.上诉看出p1的状态决定了p2的状态

注意 调用 resolve 和 reject 并不会终结的参数函数的执行

4.Promise.prototype.then()

  • Promise 实例具有 then 方法,也就是说,then的方法是定义在原型对象Promise.prototype 上的,它的作用是为 Promise 实例添加改变时的回调函数.then 方法的第一个参数是 resloved状态的回调参数,第二个参数是 rejected状态的回调函数,他妈都是可选的.
  • then 方法返回的是一个新的 Promise 实例,不是原来的那个 Promise 实例.因此可以进行链式调用.第一个回调函数完成以后,会将返回结果作为参数,参入给第二个回调函数.
new Promise((resolve,reject)=>{
  resolve(1)
}).then(()=>{
 return '小仙女'
}).then((res)=>{
  console.log(res) // 小仙女
})

5.Promise.prototype.catch()

  • Promise.prototype.catch 方法是.then(null,rejection) 或.then(undefined,rejection) 的别名,用于指定发生错误时的回调函数.
let getData = new Promise((res)=>{
  res()
})
getData.then((res)=>{
  // 成功回调
}).catch((error)=>{
  // 发生错误
})

暂举 两种错误方式捕获方式

const promise = new Promise(function(resolve, reject) {
  throw new Error('test');
});
promise.catch(function(error) {
  console.log(error);
});
// Error: test
const promise = new Promise(function(resolve, reject) {
  reject(new Error('test'));
});
promise.catch(function(error) {
  console.log(error); // test
});

如果Promise 的状态已经是 resolved,在抛出错误是无效的,也不会被捕捉上 Promise 内部的错误不会影响到Promise 外部的代码,通俗说 Promise 会吃掉错误.

const promise = new Promise(function(resolve, reject) {
  resolve('ok');
  throw new Error('test'); // 无效的
});
promise
  .then(function(value) { console.log(value) })
  .catch(function(error) { console.log(error) }); // 捕获不到
// ok

6.Promise.prototype.finally()

  • finally() 方法用于指定 Promise 对象最后状态如何,都会执行的操作。

7.Promise.all()

  • 该方法用于将多个Promise实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1,p2,p3])

promise.all() 接受数组作为参数,Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。

const p1 = Promise.resolve(1)
const p2 = Promise.resolve(2)
const p3 = Promise.reject(3)
const p =  Promise.all([p1,p2,p3])
  p.then((res)=>{
    console.log(res)  // 报错
  })

1)只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

const p1 = Promise.resolve(1)
const p2 = Promise.resolve(2)
const p3 = Promise.resolve(3)
const p =Promise.all([p1,p2,p3])
  p.then((res)=>{
    console.log(res) // [1,2,3]
  })

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

const p1 = Promise.reject(1)
const p2 = Promise.resolve(2)
const p3 = Promise.resolve(3)
const p = Promise.all([p1,p2,p3])
    .then((res)=>{
        console.log(res,123)
    })
  .catch((res)=>{
    console.log(res,'qqq') // 1,'qqq'
  })
  • 注意,如果作为参数的Promise实例,自己定义catch方法,那么他一旦被 rejected,并不会出发Peomise.all() 的 catch方法。
const p1 = new Promise((res,reject)=>{
    reject(1)
}).catch(e=>e)
const p2 = Promise.resolve(2)
const p3 = Promise.resolve(3)
const p = Promise.all([p1,p2,p3])
    .then((res)=>{
        console.log(res,123) // [1, 2, 3] 123
    })
    .catch((res)=>{
        console.log(res,'qqq') 
    })

上述代码中 p1 rejected,但是p1也有自己的 catch 方法,该方法返回一个新的 Peimise 实例,p1 指向的实际是这个实例,该实例执行完catch方法后,也会变成resolved,p2,p3 都是 resloved,所以 Promise.all()方法参数里面的两个实例都是 preslved,因此会调用then的方法,不会调用 catch 方法

  • 如果 p1 没有自己的 catch, 就是调用 Promise.all()catch 方法,如果 p1 没有自己的 catch 方法,就是 Promise.all()的catch 方法了

8.Promise.race()

·Promise.race() 方法同样将多个Promise 实例,包装成一个新的 Promise 实例

const p1 = new Promise((res,reject)=>{
    res(1)
})
const p2 = Promise.resolve(2)
const p3 = Promise.resolve(3)
const p = Promise.race([p1,p2,p3])
    .then((res)=>{
        console.log(res) // 1
    })
    .catch((res)=>{
        console.log(res,'qqq')
    })

p1状态改变 就是立马调用 p 的 then 的方法,如果p1是reject,里面调用p的catch方法

9.Promise.allStetied()

  • 有时候,我们希望等到一组异步操作都结束了,不管每一个操作是成功还是失败,再进行下一步操作。但是,现有的 Promise 方法很难实现这个要求。
  • ES2020 引入了Promise.allSettled()方法,用来确定一组异步操作是否都结束了(不管成功或失败)。所以,它的名字叫做”Settled“,包含了”fulfilled“和”rejected“两种情况。
  • Promise.allSetted() 方法接受一个数组作为参数,数组的每一个成员都是 Promise 对象,并返回一个新的 Promise 对象。只有等待数组的所有 Peomise 对象状态改变,成功或失败,返回的 Promise 对象才会发生状态变更。
  • 该方法返回的新的 Promise 实例,一旦状态变更,状态总是 fulfilled ,不会 rejected。状态变成fulfilled后,它的回调函数会接受到一个数组作为参数,该数组的每一个成员对应前面数组的每一个Promise 对象。
const resolved = Promise.resolve(5);
const rejected = Promise.reject(7);
const allSettledPromise = Promise.allSettled([resolved, rejected]);
allSettledPromise.then(function (results) {
    console.log(results);
   // (2) [{…}, {…}]
   // 0: {status: 'fulfilled', value: 42}
   // 1: {status: 'rejected', reason: -1}
   // length: 2
    //  [[Prototype]]: Array(0)
});

上面的代码中,**Promise.allSettled()**的返回值 allSettledPromise,状态只能变成 fulfilled。它的回调函数接受到的参数事数组 results.该数组的每一个成员都是一个对象,对应传入 Promise.allSettled() 的数组里面的两个 Promise 对象。

  • results 的每一个成员都是一个对象,对象的格式事固定的
// 异步操作成功时
{status: 'fulfilled', value: value}

// 异步操作失败时
{status: 'rejected', reason: reason}

10.Promise.any()

ES2021 引入了Promise.any()方法。该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。

Promise.any([
  fetch('https://v8.dev/').then(() => 'home'),
  fetch('https://v8.dev/blog').then(() => 'blog'),
  fetch('https://v8.dev/docs').then(() => 'docs')
]).then((first) => {  // 只要有一个 fetch() 请求成功
  console.log(first);
}).catch((error) => { // 所有三个 fetch() 全部请求失败
  console.log(error);
})

只有参数实例有一个变成 fulfilled 状态,包装实例就会变成 fulfilled 状态,如果所有参数都变成 rejected 状态,包装实例就会变成 rejected 状态

  • Promise.any()Promise.race()方法很像,只有一点不同,就是Promise.any()不会因为某个 Promise 变成rejected状态而结束,必须等到所有参数 Promise 变成rejected状态才会结束。

11 Promise.resolve()

有时需要将现有的对象转为 Promise 对象,Promise.resolve() 方法就起到这个作用。

Promise.resolve('foo')
new Promise(resolve => ('foo'))

写法相等

Promise 的方法参数分成4种情况
  • 参数是一个 Promise 实例 如果参数事 Promise 实例,那么Promise.resolve 将不做任何修改,原封不动的返回这个实例。
  • 参数是一个 thenable 对象 thenable对象指的是具有then方法的对象
let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};

Promise.sesolve() 方法会将这个对象转为Promise对象,然后就立即执行 thenable 对象方法的 then() 方法

let thenable = {
  then: function(resolve, reject) {
    console.log(123)
    resolve(42);
  }
};

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

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

  • 参数不是具有 then() 方法的对象,或根本不是对象 如果参数是一个原始值,或者是一个不具有then()方法的对象,则Promise.resolve()方法返回一个新的 Promise 对象,状态为resolved
const p = Promise.resolve('Hello');

p.then(function (s) {
  console.log(s)
});
// Hello

上面代码生成一个新的 Promise 对象的实例p。由于字符串Hello不属于异步操作(判断方法是字符串对象不具有 then 方法),返回 Promise 实例的状态从一生成就是resolved,所以回调函数会立即执行。Promise.resolve()方法的参数,会同时传给回调函数

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

12.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 对象的实例p,状态为rejected,回调函数会立即执行。

Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。