Promise解析

Promise 的含义

Promise 是异步编程的一种解决方案,比起传统的解决方案如:事件的监听和回调函数,要更加强大。ES6 将其写进了语言标准中,统一了用法,原生提供了Promise对象。所谓 Promise,简单来说就是一个装载着在未来才会开始和结束的事件的容器,Promise 有以下两个特点:

  1. Promise 的状态不受外界影响。Promise 中装载着一个在未来才会开始和结束的事件,比如从服务器获取数据,那么 Promise 理所当然地代表着一个异步操作,其拥有着三种状态

    1. pending,进行
    2. fulfilled,已成功
    3. rejected,已失败

    只有这个异步操作的结果才能决定 Promise 当前处于哪一种状态中,其他任何手段都无法改变这个状态,从这句话我们可以了解到 Promise 之所以叫做Promise(承诺)的原因,Promise 承诺在未来会执行其装载的异步操作,且只有这个异步操作的结果能够改变我的状态

  2. Promise 的状态一旦改变,就不会再次改变,任何时候都可以得到这个结果。Promise状态的改变只能是以下两种状况:

    1. pending(进行)——> fulfilled(已成功)
    2. pending(进行)——> rejected(已失败)

    只要上述两种状况中的一种发生,Promise 的状态就会凝固,一直保持不变,这个时候就称Promise 对象 resolved(已定型),(其实我们也可以将 resolved 称其为 Promise 的一个状态,其包含了 fulfilled 和 rejected 两种状态,可是状态需要是精确的,不能像薛定谔的猫一样无法被确定,所以我更愿意称 resolved 为一种概括,为了后续行文简便,我仍称 resolved 为 Promise 的一种状态),如果你再向已经 resolved 的 Promise 添加回调函数,也会立即得到这个结果,这个现象和事件的监听完全不同,事件的监听特点是你一旦错过事件发生的时间点,是得不到结果的

Promise 的基本用法

let promise = new Promise(function(resolve, reject) {
    // some code...
    if (/* 异步操作成功 */) {
        resolve(value);
    } else {
        reject(error);
    }
})

Promise 的构造器接受一个函数作为参数,该函数有两个参数是 resolve 和 reject,它们也是两个函数,由 JavaScript 引擎提供,不需要自己实现,针对这两种状态:

  1. resolve:异步操作的结果作为参数,在异步操作成功的时候被调用,并将 Promise 的状态从 pending 变为 fulfilled
  2. reject:异步操作抛出的错误作为参数,在异步操作失败的时候被调用,并将 Promise 的状态从 pending 变为 rejected

在 Promise 实例生成后可以用 then 方法分别注册 fulfilled 状态和 rejected 状态的回调函数

promise.then(function(value) {
    // success,some code...
}, function(error) {
    // failure,some code...
});
  1. then 函数接收两个函数作为参数,第一个函数是 fulfilled 状态的回调函数,第二个函数是 rejected 状态的回调函数,其中第一个函数是必须的,第二个函数是可选的

  2. catch 函数是函数 then(null, rejection) 或者 then(undefined, rejection) 的别名,用于指定 rejected 状态的回调函数

通常我们不会在 then 函数中同时注册 fulfilled 状态和 rejected 状态的回调函数,因为单纯地只把 fulfilled 状态的回调函数注册在 then 函数中,把 rejected 状态的回调函数注册在 catch 函数中,这样更具有语义化,也更容易理解

let promise = new Promise(function(resolve, reject) {
    console.log('Promise start');
    resolve();
});

promise.then(() => console.log('Promise fulfilled'));

console.log('Hi');

// output:
// Promise start
// Hi
// Promise fulfilled

Promise 新建后就会立即执行,所以第一行输出 ‘Promise start’,Promise是异步的,所以 then 函数中的操作会在本轮宏任务完成之后再执行,所以第二行输出‘Hi’,第三行输出‘Promise fulfilled’(有关‘宏任务’可以查找JavaScript的执行机制有关内容)

如果调用 resolve 和 reject 函数时带有参数的话,那么它们的参数会被传递给 then 函数中注册的回调函数,如果 resolve 函数中的参数是另一个 Promise 对象的话,会出现什么情况呢?

let promise1 = new Promise(function (resolve, reject) {
    // some code...
});

let promise2 = new Promise(function (resolve, reject) {
    // some code...
    resolve(promise1);
});

这时,promise1 的状态就会传递给 promise2,也就是 promise1 的状态取代了 promise2 的状态,如果 promise1 的状态此时是 pending 那么 promise2 就会等待 promise1 的状态改变之后再执行回调函数,如果 promise1 的状态已经是 resolved 或者 rejected,那么 promise2 的回调就会立刻执行

let promise1 = new Promise(function (resolve, reject) {
    setTimeout(() => reject(new Error('fail')), 3000);
});

let promise2 = new Promise(function (resolve, reject) {
    setTimeout(() => resolve(promise1), 1000);
});

promise2.then((value) => console.log('value: ' + value))
	.catch((error) => console.log('error: ' + error)); // 链式写法,见下文

上述代码的执行过程如下:

  1. 此时 promise1 的状态为 pending,在 3s 后改变为 rejected
  2. 此时 promise2 的状态为 pending,在 1s 后改变为 resolveed
  3. 1s 后,由于 promise2 中的 resolve 函数的参数为 promise1,所以 promise2 的状态被 promise1 的状态所取代,所以此时 promise2 的状态仍为 pending
  4. 3s 后,promise2 为 rejected,执行函数 catch 中注册的回调函数
  5. 打印 ‘Error:fail’

Promise 的链式执行

then 函数返回的是一个新的 Promise 实例,因此可以采用链式写法

promise.then((json) => return json.name)
    .then((name) => console.log(name));

上述代码使用 then 函数,依次注册了两个回调函数,当第一个回调函数执行完毕后,会将其返回的结果作为参数传入第二个回调函数

如果第一个回调函数返回的结果是一个 Promise 实例的话,如同上文所述,第二个回调函数就会等待该 Promise 实例的状态发生变化,才会被执行

promise.then(function (url) {
    return getJSON(url); // 返回一个 Promise 实例
}).then(function (json) {
    console.log('promise resolved: ' + json.content);
}).catch(function (error) {
    console.log('promise rejected: ' + error);
})

另外,Promise 实例的错误具有 ‘冒泡’ 性质,会一直往执行链的后端传递,直至被捕获为止,在上述代码中一共有 4 个 Promise 实例,一个 promise、一个由 getJSON 方法创建、两个由 then 方法创建,它们其中仍和一个抛出的错误都会被最后的 catch 捕获

Promise.prototype.finally() 方法用于注册无论 Promise 实例最后的状态如何都会执行的操作,其不接收任何参数,也就等同于无法知道该 Promise 实例的状态如何,不依赖于 Promise 实例的执行结果

promise.then(() => /* some code... */)
    .catch(() => /* some code... */)
    .finally(() => /* some code... */);

Promise.all() 方法用于把把多个 Promise 实例包装成一个新的 Promise 实例,注意不是 Promise 实例的数组

const biggestPromise = Promise.all([promise1, promise2, promise3]);

biggestPromise 的状体由 promise1、promise2、promise3 共同决定,有以下两种状况:

  1. 当 promise1、promise2、promise3 的状态都为 fulfilled 时,biggestPromise 的状态为 fulfilled,此时,前三者的返回值组成一个数组,作为后者回调函数的参数
  2. 当 promise1、promise2、promise3 的状态有一个为 rejected ,biggedPromise 的状态为 rejected,此时,第一个被 reject 的 Promise 实例的返回值,作为后者回调函数的参数

Promise.race() 方法同样是把多个 Promise 实例包装成一个新的 Promise 实例

const fastestPromise = Promise.race([promise1, promise2, promise3]);

只要 promise1、promise2、promise3 之中有一个实例率先改变状态,fastestPromise 的状态就会改变,率先改变状态的 Promise 实例的返回值,作为 fastestPromise 回调函数的参数

水平所限,难免谬误,请指正…

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值