# Promise
# Promise 用法
new Promise((resolve, reject) => {
// do something
resolve();
});
2
3
4
resolve()
将 Promise 对象的状态由pending
转变为fulfilled
, 相应地, reject()
执行时, Promise 对象的状态由pending
转换为rejected
.
# Promise#then()
new Promise()
后返回一个 Promise 对象, 这个对象的then
属性是一个函数, 也是我们最常用的属性. 它接受两个参数分别是fulfilled
和rejected
的回调函数.
new Promise(...).then(
function fulfilled() {},
function rejected() {}
);
2
3
4
# Promise#catch()
catch
用于处理 Promise 对象里抛出的错误, 实际上也是一个语法糖:
new Promise(...)
.then(undefined, (e)=>{...})
// 等价于
new Promise(...)
.catch((e)=>{...})
2
3
4
5
6
7
# Promise.resolve()/reject()
我们有时可以直接将 Promise 对象的状态直接改变, 无论是 fulfilled 还是 rejected. 可以分别使用Promise.resolve(value)
和Promise.rejected(value)
. 它也返回一个 Promise 对象, 这意味着我们可以继续使用then
和catch
.
Promise.resolve(1).then(value => {
console.log(value);
}); // 1
2
3
# Promise.all()
当我们需要同时进行多个异步操作, 并且需要将这些异步结果拿到才能进行下一步工作时. 可以使用Promise.all()
const p1 = Promise.resolve(1);
const p2 = 32;
const p3 = axios("https://www.baidu.com");
Promise.all([p1, p2, p3]).then(res => {
// handle result
});
2
3
4
5
6
7
当所有 promise 对象的状态都变为fulfilled
后, promise 对象的resolve()
的参数会传递到then()
的回调函数里, 以数组的形式. 如果有一个状态变为rejected
, 那么将把第一个错误的 promise 对象的rejected()
参数传递给then()
的回调函数里
# Promise.race()
race
顾名思义, 就是比赛的意思. Promise.race()
也接受一个可迭代的对象.
const p1 = Promise.resolve(1);
const p2 = 32;
const p3 = axios("https://www.baidu.com");
Promise.race([p1, p2, p3]).then(res => {
// handle result
});
2
3
4
5
6
7
传入数组的 promise 对象将会竞争谁最先变为fulfilled
或者rejected
, 实际上也就是争谁状态变化最快, 最先的那个对象就能夺取下一个then()
的使用权(听起来有点扯).
# Promise 永远异步地处理结果
考虑如下代码:
Promise.resolve(42).then(value => {
console.log(value);
});
console.log("outer promise");
//outer promise
// 42
2
3
4
5
6
7
由于outer promise
在42
之前先打印, 我们可以发现这个代码并不是同步地执行下去的. 那是由于在设计 JavaScript Promise 之初的考量: 同步调用和异步调用同时存在会导致混乱. 在之前的博文提到, 回调并不是只用于异步场景, 同步场景下一样可以使用回调. 那么, 早先的通过回调来解决异步就会带来执行顺序的问题:
function onReady(fn) {
var readyState = document.readyState;
if (readyState === "interactive" || readyState === "complete") {
fn();
} else {
window.addEventListener("DOMContentLoaded", fn);
}
}
onReady(function() {
console.log("DOM fully loaded and parsed");
});
console.log("==Starting==");
2
3
4
5
6
7
8
9
10
11
12
上面代码有两个结果, 取决于 DOM 元素是否加载好.如果在调用onReady
之前 DOM 已经载入的话,对回调函数进行同步调用.如果在调用onReady
之前 DOM 还没有载入的话,通过注册 DOMContentLoaded
事件监听器来对回调函数进行异步调用. 在这里只是打印顺序发生了变化, 但是这个 bug 可能会引起一些难以解决的问题. 为了避免这样的问题, Promise 规定无论异步同步, 回调函数永远将会异步执行.
从技术的实现手段上, Promise
被定义为微任务, 微任务队列里的 task 会在执行上下文的同步任务结束后开始执行.
下面是改写的版本:
function onReadyPromise() {
return new Promise(function(resolve, reject) {
var readyState = document.readyState;
if (readyState === "interactive" || readyState === "complete") {
resolve();
} else {
window.addEventListener("DOMContentLoaded", resolve);
}
});
}
onReadyPromise().then(function() {
console.log("DOM fully loaded and parsed");
});
console.log("==Starting==");
2
3
4
5
6
7
8
9
10
11
12
13
14
# 错误处理
当我们的 Promise 链式调用出现错误时, 它是怎么处理的呢. 下面几张图会展示它的处理逻辑:
前一个 Promise 对象变为fulfilled
, 那么就触发成功回调, 由于then()
也返回一个新的 Promise 对象, 所以当成功回调成功执行后, 就会继续执行下一个成功回调. 一旦有 Promise 对象变为rejected
, 那么就会触发失败回调, 当失败回调处理好后, 变化触发下一个成功回调; 如果失败回调也rejected
了, 那么就会执行下一个的失败回调.
# 回调结果的传递
回调结果需要在回调函数里 return 给下一个回调函数:
Promise.resolve(12)
.then(value => value + 1)
.then(value => value * 2)
.then(value => console.log(value));
// 26
2
3
4
5