ES6 Promise相关用法详解

Promise 是异步编程的一种解决方案,异步编程的历程经历了:回调函数和事件 -> Promise -> Async/await

相较于传统的回调函数和事件,Promise减少了回调函数可能引发的回调地狱问题,是代码结构有了进一步的提升

初步认识

Promise是一个对象,它代表了一个异步操作的最终完成或者失败。

ES6中,Promise对象存在三种状态:

  • Pending(进行中): 初始状态,表示 Promise 对象正在进行中,尚未成功(resolve)或失败(reject)。
  • Fulfilled(已成功, 又称Resolved): 表示 Promise 对象已成功地完成了异步操作,执行了 resolve 回调函数。
  • Rejected(已失败): 表示 Promise 对象已经失败,执行了 reject 回调函数。

除此之外,Promise对象有以下两个特点

  • 对象的状态不受外界影响: 只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
  • 一旦状态改变,就不会再变: Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected只要发生上述情况,Promise的状态就凝固了,任何时候都可以得到这个结果。即使这时候Promise对象添加回调函数,也会立即得到这个结果。
// 创建一个 Promise 对象
const promise = new Promise((resolve, reject) => {
    // 模拟异步操作,1秒后将 Promise 对象状态设置为已完成,并传递值为 "成功"
    console.log(new Date().getTime())
    setTimeout(() => {
        resolve('成功');
        console.log(new Date().getTime())
    }, 1000)
});

// 添加成功态的回调函数
promise.then(
    value => {
        console.log(new Date().getTime())
        console.log('成功态回调函数被执行:', value); 
    }
);

在这里插入图片描述
Promise的缺点:

  • 无法取消Promise,一旦新建它就会立即执行,无法中途取消
  • 如果不设置回调函数,Promise内部抛出的错误不会反应到外部
  • 当处于 pending状态 时,无法得知目前进展到哪一个阶段

语法

Promise.resolve()

将现有对象转为Promise对象,且该Promise对象的状态为resolve,通常用then处理回调函数。

Promise.resolve方法的参数:

  • 一个Promise实例: Promise.resolve不做任何修改原封不动返回这个实例
  • 一个thenable对象: thenable对象指的是具有then方法的对象。Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。
  • 原始值: Promise.resolve方法返回一个新的 Promise 对象,状态resolved,且这个原始值,作为成功异步的返回值
  • 不带有任何参数: Promise.resolve直接返回一个resolved状态的 Promise 对象
let promise = Promise.resolve($.ajax('/whatever.json'))
// 等价于
let promise = new Promise(resolve => resolve('foo'))
// Promise.resolve方法的参数
// thenable对象参数
let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
  console.log(value);  // 42
});
// 原始值
const p = Promise.resolve('Hello');
p.then(function (s){
  console.log(s) // Hello
});
// 不带参数
const p = Promise.resolve(); // p就是一个 Promise 对象。

Promise.reject()

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

Promise.reject()方法的参数与Promise.resolve()大体一致,区别在于 thenable对象 会原封不动地作为reject的理由,变成后续的参数。

const thenable = {
  then(resolve, reject) {
    reject('出错了');
  }
};
Promise.reject(thenable)
.catch(e => {
  console.log(e === thenable) // true
})

处理Promise抛出内部问题的两种方法:

// 1、使用then(successFn, errorFn) 
// successFn: 成功回调函数,errorFn: 拒绝的回调函数
p.then(null, function (s) {
  console.log(s)
});
// 2、使用catch(err)
p.catch(function (s) {
  console.log(s)
});

Promise.try()

实际开发中,经常遇到一种情况:不知道或者不想区分,函数fn是同步函数还是异步操作,但是想用 Promise 来处理它。

这样的话都可以用 then方法指定下一个流程,且不管fn是不是包含异步函数,fn抛出的错误还可以在catch中进行处理。

但如果fn同步函数,那么它会在本轮事件循环的末尾执行

const f = () => console.log('now');
Promise.resolve().then(f);
console.log('next');
// next
// now

Promise.try() 就是解决了上述问题,让同步函数同步执行异步函数异步执行,并且让它们具有统一API,除此之外,其还能用catch接收同步函数的抛出的错误,这点区别于普通的Promise对象

const f = () => console.log('now');
Promise.try(f).then(...).catch(...);
console.log('next');
// now
// next

Promise.try就是模拟try代码块,就像promise.catch模拟的是catch代码块。

其它语法

  • Promise.then(): then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。

  • Promise.catch(): 用于指定发生错误时的回调函数,是.then(null, rejection).then(undefined, rejection)的别名

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

注意:

  • 嵌套catch 只会捕获其作用域及以下的错误,而不会捕获链中更高层的错误。
doSomethingCritical()
  .then((result) =>
    doSomethingOptional() // 可选操作
      .then((optionalResult) => doSomethingExtraNice(optionalResult))
      .catch((e) => {
        console.log(e.message);
      }),
  ) // 即便可选操作失败了,也会继续执行
  .then(() => moreCriticalStuff())
  .catch((e) => console.log(`严重失败:${e.message}`));
// 内部的 catch 语句仅能捕获到 doSomethingOptional() 和 doSomethingExtraNice() 的失败
// 并将该错误与外界屏蔽,之后就恢复到 moreCriticalStuff() 继续执行。
  • Catch后续链式操作有可能会在一个回调失败之后继续使用链式操作
new Promise((resolve, reject) => {
  console.log("初始化");
  resolve();
}).then(() => {
    throw new Error("有哪里不对了");  
}).catch(() => {
    console.log("执行「那个」");
}).then(() => {
    console.log("执行「这个」,无论前面发生了什么");
});
// 初始化
// 执行「那个」
// 执行「这个」,无论前面发生了什么

Promise的组合工具

Promise有四个组合工具可用来并发异步操作:Promise.all()Promise.allSettled()Promise.any()Promise.race()

Promise.all()

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

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

参数说明:

  • p1p2p3都是 Promise 实例,如果不是,就会先调用 Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。
  • Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。

Promise.all()返回实例的状态情况:

  • 只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3返回值组成一个数组传递p的回调函数,其元素顺序传入的 promise 一致
  • 只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values); // [3, 42, "foo"]
});

注意:如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦rejected并不会触发Promise.all()catch方法,实例请看阮一峰Promise.all()

Promise.race()

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。Promise.race()方法的参数与Promise.all()方法一样。

但与**Promise.all() 的区别**在于:

这个返回的 promise 会随着第一个 promise 的敲定而敲定(如果第一个敲定的 promise 被兑现,那么返回的 promise 也会被兑现;如果第一个敲定的 promise 被拒绝,那么返回的 promise 也会被拒绝),返回值也是第一个被敲定对象的返回值。

const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one');
});
const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'two');
});
Promise.race([promise1, promise2]).then((value) => {
  console.log(value); // two
});

Promise.allSettled()

Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。参数传递与上述两个工具相同。

区别在于,Promise.allSettled()只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束

返回的结果是带有描述每个 Promise 结果的对象数组,下面是返回结果对象属性说明:

  • status: 表示 promise 的最终状态,只有"fulfilled""rejected"两种值
  • value: 仅当 status"fulfilled"才存在promise 兑现的值。
  • reason: 仅当 status"rejected"才存在promsie 拒绝的原因。
const resolved = Promise.resolve(42);
const rejected = Promise.reject(-1);
const allSettledPromise = Promise.allSettled([resolved, rejected]);
allSettledPromise.then(function (results) {
  console.log(results);
});
// [
//    { status: 'fulfilled', value: 42 },
//    { status: 'rejected', reason: -1 }
// ]

Promise.any()

Promise.any()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。参数传递与上述三个工具相同。

区别在于,Promise.any()只要参数实例有一个变成 fulfilled状态,包装实例就会变成fulfilled状态,并返回第一个兑现的值。;如果所有参数实例变成 rejected状态,包装实例就会变成rejected状态,返回一个包含拒绝原因数组AggregateError 拒绝。

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

var resolved = Promise.resolve(42);
var rejected = Promise.reject(-1);
var alsoRejected = Promise.reject(Infinity);
Promise.any([resolved, rejected, alsoRejected]).then(function (result) {
  console.log(result); // 42
});
Promise.any([rejected, alsoRejected]).catch(function (results) {
  console.log(results); // [-1, Infinity]
});

参考资料:

阮一峰-Promise 对象:https://www.bookstack.cn/read/es6-3rd/docs-promise.md

使用 Promise:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Using_promises

  • 26
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值