关于promise的教程很多了,个人也看了不少,但心中总是没有一个清晰的脉络,总感觉知识点有点杂乱,希望在这里做个记录,简单清晰全面地总结promise的用法及注意点,不废话不深入,便于查阅

创建及使用promise

创建

1
2
3
4
5
6
7
8
9
const p = new Promise((resolve, reject) => {
// 此函数立即执行
···
if (···) {
resolve(value); // success
} else {
reject(reason); // failure
}
});

promise创建的时候立即执行参数函数

promise的状态

promise有三种状态:

  • Pending:代表初始状态,结果未知
  • Resolved:结果成功返回(resolve被调用)
  • Rejected:计算过程中发生错误(reject被调用或者参数函数执行过程中有异常抛出throw)

如果promise变成Resolved或Rejected,我们称promise已经稳定了(settled),稳定之后promise的状态不再变化:

1538204868941
1538204868941

消费/查询/使用promise

一般都要关心promise的执行结果:

1
2
3
4
5
6
7
// onFulfilled处理resolved通知,onRejected处理rejected通知
promise.then(onFulfilled, onRejected)
promise.then(value => { /* fulfillment */ }, error => { /* rejection */ })

promise.then(null, error => { /* rejection */ });
// 等价于
promise.catch(error => { /* rejection */ });

我们可以随时查询promise,无论它当时的状态是否是稳定的

promise始终是异步的

即使你查询promise的时候promise的状态已经是稳定的,你传递的通知函数也会被异步调用:

1
2
3
4
5
6
Promise.resolve(1).then(value => console.log(value))
console.log(2)

// 打印结果:
2
1

其他创建promise的方法

const p = Promise.resolve(x)

  • x是普通值:p是一个处于Resolved状态且值为x的promise
  • x也是通过resolve创建的promise:p = x
  • x是其他方式创建的promise:p的状态取决于x的状态,就是说你查询p和查询x是等价的

const p = Promise.reject(error)

返回一个处于Rejected状态且错误为error的promise:

1
2
const myError = new Error('Problem!');
Promise.reject(myError).catch(err => console.log(err === myError)); // true

promise链接(Chaining Promises)

promise的一大优点是可以链接,因为Promise.then的返回值还是一个promise:

1
const q = p.then(onFulfilled, onRejected) // q is a new promise

所以我们可以继续查询q:

1
2
3
4
p
.then(onFulfilled, onRejected)
.then(value => {}) // resolved with what is returned by either onFulfilled or onRejected
.catch(error =>{}) // rejected if either onFulfilled or onRejected throw an exception

使用普通值来resolve q

1
2
3
4
5
6
7
p
.then(function (value1) {
return 123; // q is a promise resolved with 123
})
.then(function (value2) {
console.log(value2); // 123
});

使用另一个promise来resolve q

1
2
3
4
5
6
7
p
.then(function (value1) {
return r; // r is a promise, q = r
})
.then(function (value2) {
console.log(value2);
});

resolve promise with normal value or another promise状态示意图:

resolve q from onRejected

onRejected中返回的值一样可以用来resolve(不是reject) q:

1
2
3
4
5
6
7
8
p
.catch(function () {
// Something went wrong, use a default value
return 'Untitled.txt';
})
.then(function (fileName) {
···
});

通过抛出异常来reject q(这个时候没有reject方法可以调用)

1
2
3
4
5
6
7
p
.then(function (value) {
throw new Error();
})
.catch(function (reason) {
// Handle error here
});

错误随链接传递直到catch

1
2
3
4
5
6
asyncFunc1()
.then(asyncFunc2)
.then(asyncFunc3)
.catch(function (reason) {
// Something went wrong above
});

错误处理要点

不要使用then方法来捕获错误

1
2
3
4
5
6
// Don’t do this
// onRejected能捕获promise抛出的错误,但是不能捕获onFulfilled执行过程中抛出的异常
promise.then(onFulfilled, onRejected)

// 更好的做法
promise.then(onFulfilled).catch(onRejected)

注意区分reject和throw

一般来说reject抛出的是可预期的操作错误,如文件不存在、网络断开等;而throw抛出的是代码错误,如参数类型错误等

Promise中抛出的异常不能被try-catch捕获

1
2
3
4
5
try { 
Promise.resolve(1).then(a => { throw 'err' })
} catch (e) {
console.log(222, e) // 无法捕获上面的错误
}

promise组合

Promise.all([promise1, promise2, …])

1
2
3
4
5
6
7
8
9
10
11
Promise.all([
asyncFunc1(),
asyncFunc2(),
])
.then(([result1, result2]) => {
···
})
.catch(err => {
// Receives first rejection among the Promises
···
});

Promise.race([promise1, promise2, …])

1
2
3
4
5
6
7
8
Promise.race([
httpGet('http://example.com/file.txt'),
delay(5000).then(function () {
throw new Error('Timed out')
});
])
.then(function (text) { ··· })
.catch(function (reason) { ··· });

两个额外的promise函数

done()

前面说过promise中抛出的异常外界捕获不到,所以我们解决这个问题(始终加上catch也不行,catch中也可能抛出异常),可以在promise上定义一个done方法,然后跟在promise链最后面:

1
2
3
4
5
6
7
function doSomething() {
asyncFunc()
.then(f1)
.catch(r1)
.then(f2)
.done();
}

可以这样来定义done方法:

1
2
3
4
5
6
7
Promise.prototype.done = function (onFulfilled, onRejected) {
this.then(onFulfilled, onRejected)
.catch(function (reason) {
// Throw an exception globally
setTimeout(() => { throw reason }, 0);
});
};

finally()

仿造try-catch-finally我们可以在promise上定义一个finally方法,使得无论最后promise状态如何,始终执行一个callback:

1
2
3
4
5
6
7
8
9
10
11
Promise.prototype.finally = function (callback) {
const P = this.constructor;
// We don’t invoke the callback in here,
// because we want then() to handle its exceptions
return this.then(
// Callback fulfills => continue with receiver’s fulfillment or rejection
// Callback rejects => pass on that rejection (then() has no 2nd parameter!)
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
};

async & await

async & await本质上是promise的语法糖,掌握了promise基本知道它们的用法了,这个需要注意的是async函数始终返回一个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
33
34
35
36
37
38
async function asyncFunc() {
return 123;
}

// 等价于:

function asyncFunc() {
return Promise.resolve(123);
}

asyncFunc().then(x => console.log(x)); // 123

//////////////////////////////////////////////////////////////////

async function asyncFunc() {
throw new Error('Problem!');
}

// 等价于:

function asyncFunc() {
return Promise.reject(new Error('Problem!'));
}

asyncFunc().catch(err => console.log(err)); // Error: Problem!

/////////////////////////////////////////////////////////////////

// async可可以直接返回另一个promise
async function asyncFunc() {
return Promise.resolve(123);
}
asyncFunc().then(x => console.log(x)) // 123

async function asyncFunc() {
return Promise.reject(new Error('Problem!'));
}
asyncFunc().catch(err => console.error(err)); // Error: Problem!
1
2
3
4
5
6
7
8
9
10
11
12
13
async function asyncFunc() {
const result = await otherAsyncFunc();
console.log(result);
}

// 等价于:

function asyncFunc() {
return otherAsyncFunc()
.then(result => {
console.log(result);
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
async function asyncFunc() {
const result1 = await otherAsyncFunc1();
console.log(result1);
const result2 = await otherAsyncFunc2();
console.log(result2);
}

// 等价于:

function asyncFunc() {
return otherAsyncFunc1()
.then(result1 => {
console.log(result1);
return otherAsyncFunc2();
})
.then(result2 => {
console.log(result2);
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
async function asyncFunc() {
const [result1, result2] = await Promise.all([
otherAsyncFunc1(),
otherAsyncFunc2(),
]);
console.log(result1, result2);
}

// 等价于:

function asyncFunc() {
return Promise.all([
otherAsyncFunc1(),
otherAsyncFunc2(),
])
.then([result1, result2] => {
console.log(result1, result2);
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async function asyncFunc() {
try {
// 当otherAsyncFunc reject时await语句会抛出异常
await otherAsyncFunc();
} catch (err) {
console.error(err);
}
}

// 等价于:

function asyncFunc() {
return otherAsyncFunc()
.catch(err => {
console.error(err);
});
}

注意:一个async函数asyncFunc有下面4种调用方法:

  • asyncFunc()
  • await asyncFunc()
  • return asyncFunc()
  • return await asyncFunc()

它们的意义各不相同,参考下面的博文

参考:

http://exploringjs.com/es6/ch_promises.html

http://exploringjs.com/es2016-es2017/ch_async-functions.html

https://jakearchibald.com/2017/await-vs-return-vs-return-await/