Promise 想必大家都十分熟悉,想想就那么幾個 api,可是你真的了解 Promise 嗎?本文根據 Promise 的一些知識點總結了十道題,看看你能做對幾道。
以下 promise 均指代 Promise 實例,環境是 Node.js。
題目一
1
2
3
4
5
6
7
8
9
|
const promise = new Promise((resolve, reject) => {
console.log(1)
resolve()
console.log(2)
})
promise.then(
() => {
console.log(3)
})
console.log(4)
|
運行結果:
1
2
3
4
|
1
2
4
3
|
解釋:Promise 構造函數是同步執行的,promise.then 中的函數是異步執行的。
題目二
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
const promise1 = new Promise((resolve, reject) => {
setTimeout(
() => {
resolve(
'success')
},
1000)
})
const promise2 = promise1.then(() => {
throw new Error('error!!!')
})
console.log('promise1', promise1)
console.log('promise2', promise2)
setTimeout(
() => {
console.log('promise1', promise1)
console.log('promise2', promise2)
},
2000)
|
運行結果:
1
2
3
4
5
6
7
8
9
|
promise1 Promise { <pending> }
promise2 Promise { <pending> }
(node:50928) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: error!!!
(node:50928) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
promise1 Promise { 'success' }
promise2 Promise {
<rejected> Error: error!!!
at promise.then (...)
at <anonymous> }
|
解釋:promise 有 3 種狀態:pending、fulfilled 或 rejected。狀態改變只能是 pending->fulfilled 或者 pending->rejected,狀態一旦改變則不能再變。上面 promise2 並不是 promise1,而是返回的一個新的 Promise 實例。
題目三
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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)
})
|
運行結果:
1
|
then: success1
|
解釋:構造函數中的 resolve 或 reject 只有第一次執行有效,多次調用沒有任何作用,呼應代碼二結論:promise 狀態一旦改變則不能再變。
題目四
1
2
3
4
5
6
7
8
9
10
11
|
Promise.resolve(1)
.then(
(res) => {
console.log(res)
return 2
})
.catch(
(err) => {
return 3
})
.then(
(res) => {
console.log(res)
})
|
運行結果:
1
2
|
1
2
|
解釋:promise 可以鏈式調用。提起鏈式調用我們通常會想到通過 return this 實現,不過 Promise 並不是這樣實現的。promise 每次調用 .then 或者 .catch 都會返回一個新的 promise,從而實現了鏈式調用。
題目五
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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)
})
|
運行結果:
1
2
3
|
once
success 1005
success 1007
|
解釋:promise 的 .then 或者 .catch 可以被調用多次,但這里 Promise 構造函數只執行一次。或者說 promise 內部狀態一經改變,並且有了一個值,那么后續每次調用 .then 或者 .catch 都會直接拿到該值。
題目六
1
2
3
4
5
6
7
8
9
10
|
Promise.resolve()
.then(
() => {
return new Error('error!!!')
})
.then(
(res) => {
console.log('then: ', res)
})
.catch(
(err) => {
console.log('catch: ', err)
})
|
運行結果:
1
2
3
|
then: Error: error!!!
at Promise.resolve.then (...)
at ...
|
解釋:.then 或者 .catch 中 return 一個 error 對象並不會拋出錯誤,所以不會被后續的 .catch 捕獲,需要改成其中一種:
1
2
|
return Promise.reject(new Error('error!!!'))
throw new Error('error!!!')
|
因為返回任意一個非 promise 的值都會被包裹成 promise 對象,即 return new Error(‘error!!!’) 等價於 return Promise.resolve(new Error(‘error!!!’))。
題目七
1
2
3
4
5
|
const promise = Promise.resolve()
.then(
() => {
return promise
})
promise.catch(
console.error)
|
運行結果:
1
2
3
4
5
6
|
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
|
解釋:.then 或 .catch 返回的值不能是 promise 本身,否則會造成死循環。類似於:
1
2
3
4
|
process.nextTick(
function tick () {
console.log('tick')
process.nextTick(tick)
})
|
題目八
1
2
3
4
|
Promise.resolve(1)
.then(
2)
.then(
Promise.resolve(3))
.then(
console.log)
|
運行結果:
1
|
1
|
解釋:.then 或者 .catch 的參數期望是函數,傳入非函數則會發生值穿透。
題目九
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
Promise.resolve()
.then(
function success (res) {
throw new Error('error')
},
function fail1 (e) {
console.error('fail1: ', e)
})
.catch(
function fail2 (e) {
console.error('fail2: ', e)
})
```text
運行結果:
`
``text
fail2:
Error: error
at success (...)
at ...
|
解釋:.then 可以接收兩個參數,第一個是處理成功的函數,第二個是處理錯誤的函數。.catch 是 .then 第二個參數的簡便寫法,但是它們用法上有一點需要注意:.then 的第二個處理錯誤的函數捕獲不了第一個處理成功的函數拋出的錯誤,而后續的 .catch 可以捕獲之前的錯誤。當然以下代碼也可以:
1
2
3
4
5
6
7
8
9
10
|
Promise.resolve()
.then(
function success1 (res) {
throw new Error('error')
},
function fail1 (e) {
console.error('fail1: ', e)
})
.then(
function success2 (res) {
},
function fail2 (e) {
console.error('fail2: ', e)
})
|
題目十
1
2
3
4
5
6
7
8
9
10
11
|
process.nextTick(
() => {
console.log('nextTick')
})
Promise.resolve()
.then(
() => {
console.log('then')
})
setImmediate(
() => {
console.log('setImmediate')
})
console.log('end')
|
運行結果:
1
2
3
4
|
end
nextTick
then
setImmediate
|
解釋:process.nextTick 和 promise.then 都屬於 microtask,而 setImmediate 屬於 macrotask,在事件循環的 check 階段執行。事件循環的每個階段(macrotask)之間都會執行 microtask,事件循環的開始會先執行一次 microtask。