ES 6 系列之三种异步 - Promise 对象

含义

Promise 是异步编程的一种解决方案,在 Promise 之前,采用的是回调函数和事件,但是会存在一些问题,像回调地狱。而在 ES6 中,提供了 Promise 对象。

Promise 简单来讲,其实就是一个容器,用来存储异步事件(例如网络请求等)。

语法上来说,Promise 是一个对象,可以获取异步操作的消息。

主要特点

  • 对象状态不受外界影响。
    • 一共只有三种状态:pending (进行中)、fulfilled(完成)和 rejected(失败)。只受异步操作的结果,可以决定当前是哪种状态,其他任何操作都无法改变这个状态。
  • 一旦状态改变,就不能再变,任何时候都可以得到这个结果。
    • Promise 对象状态改变只有两种可能性,从pendingfulfilled 和从 pendingrejected

主要缺陷

  • 一旦创建会立即执行,无法中途取消。
  • 如果内部不设置回调函数,外部是不能知道内部情况的。
  • 当状态处于 pending 时,无法得知目前进展到哪个阶段。

基本用法

这里一般需要进行异步操作,但是为了学习方便,我们直接同步进行了。
Promise 构造函数接受一个函数作为参数,该函数的参数为 resolverejectresolvereject 是两个函数,有 JS 引擎提供,不需要自己部署。
resolve 函数作用是将 Promise 的状态从 pending 变为 resolved,并且将操作结果作为参数传递出去。
reject 函数作用是将 Promise 的状态从 pending 变为 rejected,并且将错误信息传递出去。
Promise 生成实例之后,可以调用 then 方法,其中包含两个函数参数。第一个函数包含 resolved 状态的结果,第二个包含 rejected 状态结果。两个函数都是可选参数。

我们将 flag 比做异步操作,true 表示异步成功,false 表示异步失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const promise = new Promise((resolve, reject) => {
let flag = true;
if (flag) {
resolve('操作成功');
} else {
reject('操作失败');
}
});
promise.then(
(value) => {
console.log('value = ', value);
},
(error) => {
console.log('error = ', error);
}
);

从上面的代码来看,flagtrue 时,会打印 value = 操作成功,为 false 时,打印 error = 操作失败

我们来看看 then 的异步。
Promise 创建时,就会立即执行,而 then 会在当前脚本所有同步任务执行完才会执行。

1
2
3
4
5
6
7
8
9
10
11
const promise = new Promise((resolve, reject) => {
console.log('1');
resolve('2');
});
promise.then((val) => {
console.log(val);
});
console.log('3');
// '1'
// '3'
// '2'

实例方法

Promise.prototype.then()

Promise.prototype.then() 方法作用是为 Promise 实例添加状态改变时的回调函数。
then 方法的第一个参数是 resolved 状态的回调函数,第二个参数是 rejected 状态的回调函数,它们都是可选的值,不一定必须传入。

then 方法返回的是一个新的 Promise 实例,所以可以采用链式写法。

1
2
3
4
5
6
7
8
9
10
11
const promise = new Promise((resolve, reject) => {
resolve('1');
});
promise
.then((val) => {
return val;
})
.then((val) => {
console.log(val);
});
// '1'

Promise.prototype.catch()

Promise.prototype.catch() 方法就是在 then 方法时 reject 的情况,用于发生错误时的回调函数。

1
2
3
4
5
6
7
const promise = new Promise((resolve, reject) => {
reject('1');
});
promise.catch((error) => {
console.log(error);
});
// '1'

如果在 Promise 中报错,进行错误抛出,状态会变为 rejected,就会调用 catch() 方法指定的回调函数,然后在其中处理错误。

当然,如果在运行过程中,抛出错误,也会被 catch 捕获。

1
2
3
4
5
6
7
const promise = new Promise((resolve, reject) => {
throw new Error('Error');
});
promise.catch((error) => {
console.log(error);
});
// 'Error'

Promise.prototype.finally()

Promise.prototype.finally() 方法不管 Promise 对象最后状态如何,都会进行执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const promise1 = new Promise((resolve, reject) => {
resolve('成功');
});
promise1.finally(() => {
console.log('执行finally');
});
// 执行finally
const promise2 = new Promise((resolve, reject) => {
reject('失败');
});
promise2.finally(() => {
console.log('执行finally');
});
// 执行finally

Promise 类方法

Promise.all()

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

  • 多个 Promise 中,其中一个状态不是 fulfilled,p 的结果就不会是 fulfilled
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const promise1 = new Promise((resolve, reject) => {
resolve('1');
});
const promise2 = new Promise((resolve, reject) => {
resolve('2');
});
const promise3 = new Promise((resolve, reject) => {
resolve('3');
});
const promise = Promise.all([promise1, promise2, promise3]);

promise.then((val) => {
console.log(val);
});
// ['1', '2', '3']
  • 只要有一个状态变为 rejected,此时第一个reject 的实例的返回值,会被传递为 promise 的回调函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 我们将上面的代码其中一个 resolve 修改为 reject('1')
const promise3 = new Promise((resolve, reject) => {
reject('3');
});
const promise = Promise.all([promise1, promise2, promise3]);

promise.then(
(val) => {
console.log(val);
},
(error) => {
console.log(error); // 3
}
);

Promise.race()

Promise.race() 方法同样是将多个实例包装为一个新的 Promise 实例。

  • 多个 Promise 时,哪一个率先改变状态,promise 的状态就跟着哪个改变。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('1');
}, 1000);
});
const promise2 = new Promise((resolve, reject) => {
resolve('2');
});
const promise3 = new Promise((resolve, reject) => {
resolve('3');
});
const promise = Promise.race([promise1, promise2, promise3]);

promise.then((val) => {
console.log(val);
});
// 2

我们在第一个使用了 setTimeout,进行延迟,所以会是第二个的状态先改变,输出第二个的值。

Promsie.allSettled()

Promise.allSettled() 方法,用来确定一组异步操作是否都结束了。
数组的每个成员都是一个 Promise 对象,并返回一个新的 Promise 对象,只有数组中的 Promsie 对象状态都发生改变时,返回的 Promise 对象才发生改变。
返回的值会接收到一个数组作为参数,该数组的每一个成员对应前面数组的每个 Promise 对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const promise1 = new Promise((resolve, reject) => {
reject('1');
});
const promise2 = new Promise((resolve, reject) => {
resolve('2');
});
const promise3 = new Promise((resolve, reject) => {
reject('3');
});
const promise = Promise.allSettled([promise1, promise2, promise3]);
promise.then(
(val) => {
console.log(val);
},
(error) => {
console.log(error);
}
);
// [
// {
// status: 'rejected',
// reason: '1',
// },
// {
// status: 'fulfilled',
// value: '2',
// },
// {
// status: 'rejected',
// reason: '3',
// },
// ];

数组成员对象的 status 属性的值只可能是 fulfilledrejected,用来区别异步操作结果是成功(fulfilled)还是失败(rejected)。
如果是成功是会有 value 属性,记录成功值,如果失败,会有 reason 属性,记录失败原因。

Promise.any()

Promise.any() 方法,将一组 Promise 实例作为参数,包装成一个新的 Promise 实例并返回。

  • 只要有一个变成 fulfilled 状态,新实例就会被包装成 fulfilled 状态
  • 如果所有参数实例都变成 rejected 状态,包装实例就会变成 rejected 状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const promise1 = new Promise((resolve, reject) => {
reject('1');
});
const promise2 = new Promise((resolve, reject) => {
resolve('2');
});
const promise3 = new Promise((resolve, reject) => {
reject('error');
});
Promise.any([promise1, promise2, promise3])
.then((val) => {
// 只要有一个成功
console.log(val);
})
.catch((error) => {
console.log(error);
});
// 2

Promise.resolve()

Promise.resolve() 方法有四种情况。

1
2
3
const promise = Promise.resolve('请求成功');
// 等价于
const promise = new Promise((resolve) => resolve('请求成功'));
  • 参数为 Promise 时,不做任何修改,直接返回这个实例。
1
2
3
4
5
const promise = new Promise((resolve) => resolve('请求成功'));
const promise1 = Promise.resolve(promise);
promise1.then((val) => {
console.log(val);
});
  • 参数是 thenable 对象
    thenable 对象就是有 then 方法的对象。
    Promise.resolve() 方法会将 thenable 转为 Promise 对象,并立即执行其中的 then 方法。
1
2
3
4
5
6
7
8
9
10
const thenable = {
then: function (resolve, reject) {
resolve('成功');
},
};
const promise = Promise.resolve(thenable);
promise.then((val) => {
console.log(val);
});
// 成功
  • 参数无 then 的对象,或不是对象的参数
    Promise.resolve() 方法返回一个心的 Promise 对象,状态转为 resolved
1
2
3
4
5
6
const promise = Promise.resolve('成功');

promise.then((val) => {
console.log(val);
});
// 成功
  • 不带参数
    Promise.resolve() 方法可以不传参数,直接返回一个 resolved 状态的 Promise 对象。
    所以在希望得到一个 Promise 对象,直接调用比较该方法方便。

注意,立即 resolve()Promise 对象,是在本轮事件循环(event loop)的结束时执行。

1
2
3
4
5
6
7
8
const promise = Promise.resolve();

promise.then(() => {
console.log('不带参数');
});
// 事件循环
// 不带参数
console.log('事件循环');

Promise.reject()

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

1
2
3
4
5
6
7
8
9
10
const promise = Promise.reject('出错了的Promise');
// 同价
const promise = new Promise((resolve, reject) => {
reject('出错了的Promise');
});

promise.then(null, (error) => {
console.log(error);
});
// 出错了的Promise
-------------------- 本文结束 感谢阅读 --------------------