promise 特性
1. Promise 构造函数是同步执行的,promise.then 中的函数(也就是回调函数)是异步执行的。
const promise = new Promise((resolve, reject) => {
console.log(1)
resolve()
console.log(2)
})
promise.then(() => {
console.log(3)
})
console.log(4)
// 结果是:1,2,4,3
2. promise 有 3 种状态:pending、fulfilled 或 rejected。状态改变只能是 pending->fulfilled 或者 pending->rejected,状态一旦改变则不能再变
const promise = new Promise((resolve, reject) => {
resolve('success1')
reject('error')
resolve('success2')
})
promise
.then((res) => {
console.log('then: ', res)
})
.catch((err) => {
console.log('catch: ', err)
})
// then: success1
3. promise 每次调用 .then 或者 .catch 都会返回一个新的 promise,从而实现了链式调用
Promise.resolve(1)
.then((res) => {
console.log(res)
return 2
})
.catch((err) => {
return 3
})
.then((res) => {
console.log(res)
})
// 1, 2
4. promise 的 .then 或者 .catch 可以被调用多次,但这里 Promise 构造函数只执行一次。或者说 promise 内部状态一经改变,并且有了一个值,那么后续每次调用 .then 或者 .catch 都会直接拿到该值
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('once')
resolve('success')
}, 1000)
})
const start = Date.now()
promise.then((res) => {
console.log(res, Date.now() - start)
})
promise.then((res) => {
console.log(res, Date.now() - start)
})
// once
// success 1005
// success 1007
5. .then 或者 .catch 中 return 一个 error 对象并不会抛出错误,所以不会被后续的 .catch 捕获
Promise.resolve()
.then(() => {
return new Error('error!!!')
})
.then((res) => {
console.log('then: ', res)
})
.catch((err) => {
console.log('catch: ', err)
})
// then: Error: error!!!
// at Promise.resolve.then (...)
// at ...
返回任意一个非 promise 的值都会被包裹成 promise 对象,即 return new Error('error!!!') 等价于 return Promise.resolve(new Error('error!!!'))。
所以要返回一个被 catch 捕获的错误,需要用 reject / throw new Error('error!!!')来返回。
return Promise.reject(new Error('error!!!'))
throw new Error('error!!!')
特别提示:async 也遵循这个规则,不难理解,async 也是返回了一个 promise。
async function q() {
return new Error('Errorrrr')
};
q().then(res => console.log('then:', res));
/*then: Error: Errorrrr
at q (<anonymous>:1:28)
at <anonymous>:1:51
Promise{<resolve>: undefined}
*/
async function q() {
throw new Error('Errorrrr')
};
q().then(res => console.log('then:', res));
// Promise {<rejected>: Error: Errorrrr
// VM6544:1 Uncaught (in promise) Error: Errorrrr
6. .then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环
const promise = Promise.resolve()
.then(() => {
return promise
})
promise.catch(console.error)
/*
TypeError: Chaining cycle detected for promise #<Promise>
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
at Function.Module.runMain (module.js:667:11)
at startup (bootstrap_node.js:187:16)
at bootstrap_node.js:607:3
*/
7. .then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透。
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
// 1
8. 链式调用顺序
链式调用的一些关键知识点:
- 如果前面的 promise 已经是 resolved 状态,则会立即将回调推入微任务队列(但是执行回调还是要等到所有同步任务都结束后),如果没有同步任务,就会逐个取出再执行
- 如果前面的 promise 是 pending 状态则会将回调存储在 promise 的内部,一直等到 promise 被 resolve 才将回调推入微任务队列/执行
- 对于 then 方法返回的 promise 它是没有 resolve 函数的,取而代之只要 then 中回调的代码执行完毕并获得同步返回值,这个 then 返回的 promise 就算被 resolve。假如 (promise1)then 中的回调返回了一个 promise(内部promise2),那么根据第3点可知 .then 需要等promise(内部promise2) resolve后才会返回一个promise
来看一段代码
new Promise((resolve, reject) => {
// 同步代码,第一个打印 log 1
console.log("log: 外部promise");
// 将两个 .then 推入微任务队列,此时没有同步任务,取出第一个 .then 执行
resolve();
})
.then(() => {
// 同步代码 log2
console.log("log: 外部第一个then");
new Promise((resolve, reject) => {
// 同步代码 log 3
console.log("log: 内部promise");
// 将第二个 Promise 的 .then 推入微任务队列,且执行第一个 .then
resolve();
})
.then(() => {
// 同步代码 log 4
// 此时,第二个Promise resolve了,第一个 Promise 的第一个 .then 返回了一个新的 Promise 供链式调用
console.log("log: 内部第一个then");
})
.then(() => {
// 第一个 Promise 的 .then 全部执行完毕了,执行微任务中剩下的第二个Promise 的第二个 .then
// log 6
console.log("log: 内部第二个then");
});
})
.then(() => {
// 第一个 Promise 的 .then 执行完毕,现在执行第二个 .then
// log 5
console.log("log: 外部第二个then");
});
// log: 外部promise
// log: 外部第一个then
// log: 内部promise
// log: 内部第一个then
// log: 外部第二个then
// log: 内部第二个then
9. async,await
async 函数是 Generator 函数的语法糖。使用 关键字 async 来表示,在函数内部使用 await 来表示异步。
先看一段代码
async function async1() {
cosnole.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
})
console.log('script end');
如果不了解 async,await的运行机制的话,上面的代码的输出将会很难理解。
已知 async 函数会返回一个 Promise 对象,那么在 这个 Promise resolve 之前,await 之后的代码不会执行,如
async function async1() {
cosnole.log('async1 start');
await async2();
console.log('async1 end');
}
// 效果等同于以下代码
async function async1() {
cosnole.log('async1 start');
new Promise(resolve => resolve(async2());).then(() => {
console.log('async1 end');
})
}
这样看起来,就很熟悉了吧。所以以上的代码可转化为这样:
async function async1() {
// log 2
cosnole.log('async1 start');
new Promise(resolve => resolve(async2());).then(() => {
// log 6
console.log('async1 end');
})
}
async function async2() {
// log 3
console.log('async2');
}
// log 1
console.log('script start');
setTimeout(function() {
// log 8
console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
// log 4
console.log('promise1');
resolve();
}).then(function() {
// log 7
console.log('promise2');
})
// log 5
console.log('script end');
// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise 2
// settimeout
本文的代码及部分结论均来自参考文章中。用于自己记录总结所用。具体的内容,请看参考文章。