JavaScript原理篇——Promise原理及筆試題實戰演練

Promise 是 JavaScript 中用于處理異步操作的對象,它代表了一個可能還沒有完成的操作的最終完成或失敗,以及其結果值。Promise 對象有三種狀態:

  1. Pending(進行中):初始狀態,既不是成功,也不是失敗狀態。
  2. Fulfilled(已成功):操作成功完成。
  3. Rejected(已失敗):操作失敗。

一個 Promise 對象一旦從 Pending 狀態轉變為 Fulfilled 或 Rejected,它的狀態就不會再改變。

Promise 的核心原理包括:

  • 構造函數:通過 new Promise(executor) 創建 Promise 對象,其中 executor 是一個帶有兩個參數的函數,通常稱為 resolvereject,它們分別用于將 Promise 狀態改為 Fulfilled 和 Rejected。
  • 狀態管理:Promise 對象的狀態只能從 Pending 變為 Fulfilled 或從 Pending 變為 Rejected,一旦狀態改變,就不能再變。
  • 鏈式調用:通過 .then().catch() 方法可以鏈式調用 Promise,處理異步操作的結果或錯誤。
  • 異步執行:Promise 的 executor 函數是立即執行的,但其 resolvereject 函數是異步調用的。
  • 錯誤處理:通過 .catch() 方法或 .then(null, onRejected) 可以捕獲鏈式調用中的錯誤。

你真的完全了解promise嗎,請輸出以下代碼題的結果

const promise = new Promise((resolve, reject) => {console.log(1);console.log(2);
});
promise.then(() => {console.log(3);
});
console.log(4);
const promise1 = new Promise((resolve, reject) => {console.log('promise1')resolve('resolve1')
})
const promise2 = promise1.then(res => {console.log(res)
})
console.log('1', promise1);
console.log('2', promise2);
Promise.resolve().then(() => {console.log('promise1');const timer2 = setTimeout(() => {console.log('timer2')}, 0)
});
const timer1 = setTimeout(() => {console.log('timer1')Promise.resolve().then(() => {console.log('promise2')})
}, 0)
console.log('start');

?答案:

?如果你回答的和答案不一致,請務必接收下面的Promise解題指南

?Promise必備能力

Promise 是 JavaScript 中處理異步操作的一種機制,它代表了一個異步操作的最終完成或失敗,并可以獲取其結果。以下是 Promise 的關鍵知識點,掌握這些內容將有助于應對 Promise 相關的筆試題:

1. Promise 的基本概念

  • 狀態(State):一個 Promise 有三種狀態:pending(進行中)、fulfilled(已成功)和 rejected(已失敗)。
  • 結果(Result):Promise 的狀態一旦從 pending 變為 fulfilledrejected,就不再改變,且會有一個不可變的終值或拒因。

2. Promise 的創建

  • 使用 new Promise(executor) 構造函數創建 Promise,其中 executor 是一個帶有兩個參數的函數:resolvereject,分別用于將 Promise 的狀態變為 fulfilledrejected

3. Promise 的方法

  • then(onFulfilled, onRejected):注冊成功和失敗時的回調函數,返回一個新的 Promise。
  • catch(onRejected):注冊失敗時的回調函數,相當于 .then(null, onRejected),返回一個新的 Promise。
  • finally(onFinally):注冊無論成功或失敗都會執行的回調函數,返回一個新的 Promise。

4. Promise 鏈

  • .then.catch 方法返回的都是新的 Promise 實例,這允許鏈式調用,形成 Promise 鏈。
  • 鏈中的每個 .then.catch 都會返回一個新的 Promise,其狀態和結果取決于前一個 Promise 的狀態和回調函數的返回值。

5. Promise 的靜態方法

  • Promise.resolve(value):返回一個以給定值解析的 Promise 對象。
  • Promise.reject(reason):返回一個帶有拒絕原因的 Promise 對象。
  • Promise.all(iterable):返回一個 Promise,當 iterable 中所有 Promise 都 fulfilled 時,返回的 Promise 才會 fulfilled,其結果是一個包含所有 Promise 結果的數組。如果有一個 Promise 被 rejected,則返回的 Promise 立即 rejected,且結果為第一個被 rejected 的 Promise 的拒因。
  • Promise.race(iterable):返回一個 Promise,一旦 iterable 中的某個 Promise fulfilled 或 rejected,返回的 Promise 就會采用相同的狀態和結果。

6. Promise 的錯誤處理

  • 未處理的 Promise 拒絕會被 unhandledrejection 事件捕獲。
  • 使用 .catch.then(null, onRejected) 可以捕獲 Promise 鏈中的錯誤。

?Promise 的性能考慮:Promise 的創建和執行是異步的,不會阻塞主線程。避免不必要的 Promise 創建,因為每個 Promise 都會占用一定的內存。

?Promise 的局限性:Promise 一旦狀態改變,就無法再次改變。Promise 沒有提供取消異步操作的機制。

事件循環機制

事件循環(Event Loop)是 JavaScript 運行時環境中的一個重要概念,它與 Promise 的使用密切相關,因為 Promise 的回調是通過事件循環來執行的。理解事件循環機制有助于解決與異步編程、回調函數、Promise 和 async/await 等相關的問題。作為前端開發者,你必須要理解并掌握下面的內容:

  • 理解事件循環的基本流程和宏任務、微任務的區別。
  • 知道 Promise 的回調是如何通過事件循環執行的,以及為什么 .then.catch 的回調通常比 setTimeout 更快執行。
  • 應用到實際問題中,比如理解為什么在 setTimeoutPromise 的回調中,await 前的代碼會立即執行,而 await 后的代碼則在 Promise 解決后執行。
  • 理解事件循環與異步編程(如 async/await)的關系,以及如何避免回調地獄(Callback Hell)。

1. 事件循環的工作原理

  • 宏任務(Macrotask):主要包括 setTimeoutsetIntervalI/O 操作UI 渲染(如瀏覽器的重繪和重排)

  • 微任務(Microtask):主要包括 Promise 的 .thenprocess.nextTick 中的回調。

  • 執行棧(Execution Context Stack):用于執行同步代碼,每次執行棧空時,事件循環會從宏任務隊列中取出一個宏任務執行,然后執行所有微任務,再繼續取下一個宏任務。

  • 事件循環的流程

    1. 執行同步代碼直到執行棧為空。
    2. 從宏任務隊列中取出一個宏任務執行,執行完畢后執行所有微任務。
    3. 重復步驟 2 直到宏任務隊列和微任務隊列都空。
    4. 如果有新的微任務(如 process.nextTick),將其添加到微任務隊列。

2. 與 Promise 的關系

  • 當一個 Promise 被 resolve 或 reject 時,它會創建一個微任務,這個微任務會將回調函數添加到微任務隊列中,等待執行。
  • .then.catch 的回調會在微任務隊列中執行,這意味著它們會等待當前的宏任務和微任務執行完畢后才會運行。
  • 如果在回調中又創建了新的 Promise,會形成一個微任務鏈,直到微任務隊列為空

注意:宏任務是一次執行一個,微任務是一次清空一個隊列。兩者有本質區別

Promise的幾道基礎題

1. 題目一

const promise1 = new Promise((resolve, reject) => {console.log('promise1')
})
console.log('1', promise1);

思路:Promise本身是同步的。從上至下,先遇到new Promise,執行該構造函數中的代碼promise1。然后執行同步代碼1,此時promise1沒有被resolve或者reject,因此狀態還是pending

答案:

2 題目二

const promise = new Promise((resolve, reject) => {console.log(1);resolve('success')console.log(2);
});
promise.then(() => {console.log(3);
});
console.log(4);

思路:從上至下,先遇到new Promise,執行其中的同步代碼1

再遇到resolve('success'), 將promise的狀態改為了resolved并且將值保存下來

繼續執行同步代碼2

跳出promise,往下執行,碰到promise.then這個微任務,將其加入微任務隊列

執行同步代碼4

本輪宏任務全部執行完畢,檢查微任務隊列,發現promise.then這個微任務且狀態為resolved,執行它。

答案:

3 題目三

const promise = new Promise((resolve, reject) => {console.log(1);console.log(2);
});
promise.then(() => {console.log(3);
});
console.log(4);

?思路:和題目二相似,只不過在promise中并沒有resolve或者reject。因此promise.then并不會執行,它只有在被改變了狀態之后才會執行

答案:

4 題目四

const promise1 = new Promise((resolve, reject) => {console.log('promise1')resolve('resolve1')
})
const promise2 = promise1.then(res => {console.log(res)
})
console.log('1', promise1);
console.log('2', promise2);

思路:從上至下,先遇到new Promise,執行該構造函數中的代碼promise1

碰到resolve函數, 將promise1的狀態改變為resolved, 并將結果保存下來

碰到promise1.then這個微任務,將它放入微任務隊列

promise2是一個新的狀態為pending的Promise`

執行同步代碼1, 同時打印出promise1的狀態是resolved

執行同步代碼2,同時打印出promise2的狀態是pending

宏任務執行完畢,查找微任務隊列,發現promise1.then這個微任務且狀態為resolved,執行它。

5 題目五

const fn = () => (new Promise((resolve, reject) => {console.log(1);resolve('success')
}))
fn().then(res => {console.log(res)
})
console.log('start')

?思路:fn函數它是直接返回了一個new Promise的,而且fn函數的調用是在start之前,所以它里面的內容應該會先執行。

答案:

6 題目六

如果把fn的調用放到start之后呢?

const fn = () =>new Promise((resolve, reject) => {console.log(1);resolve("success");});
console.log("start");
fn().then(res => {console.log(res);
});

思路:fn是一個函數,只有在函數調用的時候才會執行。因此先執行console.log("start"),再處理fn,之后是then方法

答案:

Promise結合setTimeout

Promise通常會結合事件循環機制進行考察異步輸出結果

1 題目一?

console.log('start')
setTimeout(() => {console.log('time')
})
Promise.resolve().then(() => {console.log('resolve')
})
console.log('end')

思路:剛開始整個腳本作為一個宏任務來執行,對于同步代碼直接壓入執行棧進行執行,因此先打印出start和end。

setTimout作為一個宏任務被放入宏任務隊列(下一個)

Promise.then作為一個微任務被放入微任務隊列

本次宏任務執行完,檢查微任務,發現Promise.then,執行它

接下來進入下一個宏任務,發現setTimeout,執行。

答案:

2. 題目二

const promise = new Promise((resolve, reject) => {console.log(1);setTimeout(() => {console.log("timerStart");resolve("success");console.log("timerEnd");}, 0);console.log(2);
});
promise.then((res) => {console.log(res);
});
console.log(4);

思路:從上至下,先遇到new Promise,執行該構造函數中的代碼1

然后碰到了定時器,將這個定時器中的函數放到下一個宏任務的延遲隊列中等待執行 執行同步代碼2

跳出promise函數,遇到promise.then,但其狀態還是為pending,這里理解為先不執行 執行同步代碼4

一輪循環過后,進入第二次宏任務,發現延遲隊列中有setTimeout定時器,執行它

首先執行timerStart,然后遇到了resolve,將promise的狀態改為resolved且保存結果并將之前的promise.then推入微任務隊列

繼續執行同步代碼timerEnd

宏任務全部執行完畢,查找微任務隊列,發現promise.then這個微任務,執行它

答案:

3 題目三

題目三分了兩個題目,因為看著都差不多,不過執行的結果卻不一樣,大家不妨先猜猜下面兩個題目分別執行什么:

setTimeout(() => {console.log('timer1');setTimeout(() => {console.log('timer3')}, 0)
}, 0)
setTimeout(() => {console.log('timer2')
}, 0)
console.log('start')
setTimeout(() => {console.log('timer1');Promise.resolve().then(() => {console.log('promise')})
}, 0)
setTimeout(() => {console.log('timer2')
}, 0)
console.log('start')

思路:分析代碼,還是先執行同步代碼;然后將setTimeout放入宏任務隊列中。對于第一個題,同步執行完,宏任務隊列執行,輸出timer1,此時又遇到一個setTimeout,繼續加入隊列,并從隊頭取出timer2并輸出。

對于第二個代碼:先執行同步代碼,輸出start;然后執行setTimeout,輸出timer1,遇到微任務promise.resolve.then,此時promise的狀態也是resolve,因此可以執行then。并且then是微任務,timer1之后后執行微任務,輸出promise。

答案:

第一個輸出結果

 

第二個輸出結果

?4.題目四

Promise.resolve().then(() => {console.log('promise1');const timer2 = setTimeout(() => {console.log('timer2')}, 0)
});
const timer1 = setTimeout(() => {console.log('timer1')Promise.resolve().then(() => {console.log('promise2')})
}, 0)
console.log('start');

思路:剛開始整個腳本作為第一次宏任務來執行,我們將它標記為宏1,從上至下執行。

遇到Promise.resolve().then這個微任務,將then中的內容加入第一次的微任務隊列標記為微1

遇到定時器timer1,將它加入下一次宏任務的延遲列表,標記為宏2,等待執行(先不管里面是什么內容)

執行宏1中的同步代碼start

第一次宏任務(宏1)執行完畢,檢查第一次的微任務隊列(微1),發現有一個promise.then這個微任務需要執行

執行打印出微1中同步代碼promise1,然后發現定時器timer2,將它加入宏2的后面,標記為宏3

第一次微任務隊列(微1)執行完畢,執行第二次宏任務(宏2),首先執行同步代碼timer1

然后遇到了promise2這個微任務,將它加入此次循環的微任務隊列,標記為微2

宏2中沒有同步代碼可執行了,查找本次循環的微任務隊列(微2),發現了promise2,執行它

第二輪執行完畢,執行宏3,打印出timer2

 

5.題目五

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)

思路:從上至下,先執行第一個new Promise中的函數,碰到setTimeout將它加入下一個宏任務列表。跳出new Promise,碰到promise1.then這個微任務,但其狀態還是為pending,這里理解為先不執行。

  • promise2是一個新的狀態為pending的Promise
  • 執行同步代碼console.log('promise1'),且打印出的promise1的狀態為pending
  • 執行同步代碼console.log('promise2'),且打印出的promise2的狀態為pending
  • 碰到第二個定時器,將其放入下一個宏任務列表
  • 第一輪宏任務執行結束,并且沒有微任務需要執行,因此執行第二輪宏任務
  • 先執行第一個定時器里的內容,將promise1的狀態改為resolved且保存結果并將之前的promise1.then推入微任務隊列
  • 該定時器中沒有其它的同步代碼可執行,因此執行本輪的微任務隊列,也就是promise1.then,它拋出了一個錯誤,且將promise2的狀態設置為了rejected
  • 第一個定時器執行完畢,開始執行第二個定時器中的內容
  • 打印出'promise1',且此時promise1的狀態為resolved
  • 打印出'promise2',且此時promise2的狀態為rejected
'promise1' Promise{<pending>}
'promise2' Promise{<pending>}
test5.html:102 Uncaught (in promise) Error: error!!! at test.html:102
'promise1' Promise{<resolved>: "success"}
'promise2' Promise{<rejected>: Error: error!!!}

6.題目六

如果你上面這道題搞懂了之后,我們就可以來做做這道了,你應該能很快就給出答案:

const promise1 = new Promise((resolve, reject) => {setTimeout(() => {resolve("success");console.log("timer1");}, 1000);console.log("promise1里的內容");
});
const promise2 = promise1.then(() => {throw new Error("error!!!");
});
console.log("promise1", promise1);
console.log("promise2", promise2);
setTimeout(() => {console.log("timer2");console.log("promise1", promise1);console.log("promise2", promise2);
}, 2000);

思路:promise的then方法一定要在promise本身執行并且resolve的時候才會執行

答案:

'promise1里的內容'
'promise1' Promise{<pending>}
'promise2' Promise{<pending>}
'timer1'
test5.html:102 Uncaught (in promise) Error: error!!! at test.html:102
'timer2'
'promise1' Promise{<resolved>: "success"}
'promise2' Promise{<rejected>: Error: error!!!}

Promise中的then、catch、finally

  • Promise 的狀態一經改變(resolve 或 reject),就不能再改變。
  • .then.catch 返回一個新的 Promise。
  • .catch 能捕獲上層的錯誤,無論被連接到哪里。
  • 在 Promise 中,返回任意一個非 Promise 的值都會被包裹成 Promise 對象。
  • 如果 Promise 內部的狀態一經改變,并且有了一個值,那么后續每次調用 .then.catch 都會直接拿到該值。
  • .then.catch 中返回一個 error 對象并不會拋出錯誤,不會被后續的 .catch 捕獲。
  • .then.catch 返回的值不能是 Promise 本身,否則會造成死循環。
  • .then.catch 的參數期望是函數,傳入非函數會發生值穿透。
  • .then 方法可以接收兩個參數,第一個是處理成功的函數,第二個是處理失敗的函數。
  • .catch 可以認為是 .then 第二個參數的簡便寫法。
  • .finally 方法也是返回一個 Promise,在 Promise 結束時(resolved 或 rejected)都會執行里面的回調函數。

1 題目一?

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);})

思路:考察promise的狀態之后被更改一次

答案:

"then: success1"

2 題目二

const promise = new Promise((resolve, reject) => {reject("error");resolve("success2");
});
promise.then((res) => {console.log("then1: ", res);}).then((res) => {console.log("then2: ", res);}).catch((err) => {console.log("catch: ", err);}).then((res) => {console.log("then3: ", res);});

?思路:先被reject改變狀態。因此promise不會走then方法,而是被catch捕獲。catch不管放在哪個位置都能捕獲到error。同時catch默認返回一個新的promise,通過then方法輸出then3

答案:

3 題目三

Promise.resolve(1).then(res => {console.log(res);return 2;}).catch(err => {return 3;}).then(res => {console.log(res);});

?思路:Promise通過.resolve手動更改Promise狀態,因此執行then方法。return 2會被包裝成resolve(2)

4.題目四

const promise = new Promise((resolve, reject) => {setTimeout(() => {console.log('timer')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)
})

? 思路:Promise被執行一次,但Promise的then和catch可以被執行多次

答案:

5 題目五

Promise.resolve().then(() => {return new Error('error!!!')
}).then(res => {console.log("then: ", res)
}).catch(err => {console.log("catch: ", err)
})

?思路:通過new Error不會拋出錯誤,而是一個Promise包裝的對象。這里的return new Error('error!!!')也被包裹成了return Promise.resolve(new Error('error!!!'))

  • 你可能想到的是進入.catch然后被捕獲了錯誤。
  • 結果并不是這樣的,它走的是.then里面:

答案:

6. 題目六

Promise.resolve(1).then(2).then(Promise.resolve(3)).then(console.log)

思路:.then 或者 .catch 的參數期望是函數,傳入非函數則會發生值穿透。

第一個then和第二個then中傳入的都不是函數,一個是數字類型,一個是對象類型,因此發生了穿透,將resolve(1) 的值直接傳到最后一個then里。

7.題目七

下面來介紹一下.then函數中的兩個參數。

第一個參數是用來處理Promise成功的函數,第二個則是處理失敗的函數。 也就是說Promise.resolve('1')的值會進入成功的函數,Promise.reject('2')的值會進入失敗的函數。

讓我們來看看這個例子

Promise.reject('err!!!').then((res) => {console.log('success', res)}, (err) => {console.log('error', err)}).catch(err => {console.log('catch', err)})
思路:then方法的第二個參數接收一個回調,如果Promise失敗,執行then的第二個參數。這里的執行結果是:
'error' 'error!!!'

而如果把第二個參數去掉,就進入了catch()中:

Promise.reject('err!!!').then((res) => {console.log('success', res)}).catch(err => {console.log('catch', err)})

執行結果:

'catch' 'error!!!'

但是有一個問題,如果是這個案例呢?

Promise.resolve().then(function success (res) {throw new Error('error!!!')}, function fail1 (err) {console.log('fail1', err)}).catch(function fail2 (err) {console.log('fail2', err)})

思路:如果是then里面的方法拋出錯誤會被外面的catch住,而不會走then的第二個參數。由于Promise調用的是resolve(),因此.then()執行的應該是success()函數,可是success()函數拋出的是一個錯誤,它會被后面的catch()給捕獲到,而不是被fail1函數捕獲。

因此執行結果為:

fail2 Error: error!!!at success

8.題目八

接著來看看.finally(),這個功能一般不太用在面試中,不過如果碰到了你也應該知道該如何處理。

function promise1 () {let p = new Promise((resolve) => {console.log('promise1');resolve('1')})return p;
}
function promise2 () {return new Promise((resolve, reject) => {reject('error')})
}
promise1().then(res => console.log(res)).catch(err => console.log(err)).finally(() => console.log('finally1'))promise2().then(res => console.log(res)).catch(err => console.log(err)).finally(() => console.log('finally2'))
思路:finally方法不管promise是什么狀態都會執行
'promise1'
'1'
'error'
'finally1'
'finally2'

?Promise中的all和race

  • 在做下面的題目之前,讓我們先來了解一下Promise.all()和Promise.race()的用法。
  • 通俗來說,.all()的作用是接收一組異步任務,然后并行執行異步任務,并且在所有異步操作執行完后才執行回調。
  • .race()的作用也是接收一組異步任務,然后并行執行異步任務,只保留取第一個執行完成的異步操作的結果,其他的方法仍在執行,不過執行結果會被拋棄。

1. 題目一

function runAsync(x) {const p = new Promise((resolve) =>setTimeout(() => {console.log(x);resolve(x);}, 1000));return p;
}
Promise.all([runAsync(1), runAsync(2), runAsync(3)]).then(res => console.log(res))

?思路:并行執行多個異步操作,并且在一個回調中處理所有的返回數據。

  • all()后面的.then()里的回調函數接收的就是所有異步操作的結果。
  • 而且這個結果中數組的順序和Promise.all()接收到的數組順序一致

2 題目二

新增一個runReject函數,它用來在1000 * x秒后reject一個錯誤。

function runAsync(x) {const p = new Promise((resolve) =>setTimeout(() => {console.log("runAsync", x);resolve(x);}, 1000));return p;
}
function runReject(x) {const p = new Promise((resolve, reject) =>setTimeout(() => {console.log("runReject", x);reject(`Error: ${x}`);}, 1000 * x));return p;
}Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)]).then((res) => console.log(".all成功結果", res)).catch((err) => console.log(".all失敗結果", err));

?思路:Promise.all 接收一個 Promise 數組,這里包含兩個 runAsync 和兩個 runReject 的調用。Promise.all 會等待所有 Promise 全部成功解決后返回一個結果數組,如果其中任何一個 Promise 拒絕,它會立即返回拒絕的 Promise 的結果。

具體執行過程如下:

  1. runAsync(1)runAsync(3) 同時啟動,因為它們的 setTimeout 都設置為1秒,所以它們會在1秒后打印 "runAsync 1" 和 "runAsync 3",然后各自解決并返回 13
  2. runReject(4)runReject(2) 同時啟動,它們的 setTimeout 根據傳入的參數設置為4秒和2秒。runReject(4) 在4秒后拒絕,打印 "runReject 4" 和 "Error: 4",返回一個拒絕的 Promise。runReject(2) 在2秒后拒絕,打印 "runReject 2" 和 "Error: 2",同樣返回一個拒絕的 Promise。.catch是會捕獲最先的那個異常,在這道題目中最先的異常就是runReject(2)的結果

答案:

3 題目三

將上一題的Promise.all改成Promise.race

function runAsync(x) {const p = new Promise((resolve) =>setTimeout(() => {console.log("runAsync", x);resolve(x);}, 1000));return p;
}
function runReject(x) {const p = new Promise((resolve, reject) =>setTimeout(() => {console.log("runReject", x);reject(`Error: ${x}`);}, 1000 * x));return p;
}Promise.race([runAsync(1), runReject(4), runAsync(3), runReject(2)]).then((res) => console.log(".all成功結果", res)).catch((err) => console.log(".all失敗結果", err));

思路:使用.race()方法,它只會獲取最先執行完成的那個結果,其它的異步任務雖然也會繼續進行下去,不過race已經不管那些任務的結果了 。Promise.race的then方法只會返回第一個成功的Promise返回結果。

?如果第一個成功的是reject狀態呢

Promise.race([runReject(0),runAsync(1),runReject(4),runAsync(3),runReject(2),
]).then((res) => console.log(".all成功結果", res)).catch((err) => console.log(".all失敗結果", err));

那么將返回失敗的結果,也就是Promise.race后的catch方法

async/await的幾道題

既然談到了Promise,那就肯定得再說說async/await,在很多時候async和Promise的解法差不多,又有些不一樣。不信你來看看題目一。

1. 題目一

async function async1() {console.log("async1 start");await async2();console.log("async1 end");
}
async function async2() {console.log("async2");
}
async1();
console.log('start')

思路:

  • 首先一進來是創建了兩個函數的,我們先不看函數的創建位置,而是看它的調用位置 發現async1函數被調用了,然后去看看調用的內容
  • 執行函數中的同步代碼async1 start,之后碰到了await,它會阻塞async1后面代碼的執行,因此會先去執行async2中的同步代碼async2,然后跳出async1
  • 跳出async1函數后,執行同步代碼start
  • 在一輪宏任務全部執行完之后,再來執行剛剛await后面的內容async1 end。

答案:

?

2. 題目二

async function async1() {console.log("async1 start");await async2();console.log("async1 end");
}
async function async2() {setTimeout(() => {console.log('timer')}, 0)console.log("async2");
}
async1();
console.log("start")

思路:

這段代碼中,首先執行?async1()?函數。在?async1()?函數中,遇到?await async2()?時會暫停?async1()?函數的執行,等待?async2()?函數執行完畢。然而,在?async2()?函數中,雖然有一個?setTimeout,但是它是一個宏任務,會被放入事件隊列中等待執行,而不會阻塞當前的微任務執行。

因此,執行順序如下:

  1. 執行?async1()?函數,打印出 "async1 start"。
  2. 遇到?await async2(),暫停?async1()?函數的執行,轉而執行?async2()?函數。
  3. 在?async2()?函數中,首先打印出 "async2",然后設置了一個 0 秒后執行的?setTimeout,但是這個?setTimeout?是一個宏任務,會被放入事件隊列中等待執行。
  4. 繼續執行主程序,打印出 "start"。
  5. 主程序執行完畢后,開始執行微任務隊列中的任務,繼續執行?async1()?函數中的剩余部分,打印出 "async1 end"。
  6. 最后,事件隊列中的宏任務執行,執行?setTimeout?中的回調函數,打印出 "timer"。

3.題目三

async function async1() {console.log("async1 start");await async2();console.log("async1 end");setTimeout(() => {console.log('timer1')}, 0)
}
async function async2() {setTimeout(() => {console.log('timer2')}, 0)console.log("async2");
}
async1();
setTimeout(() => {console.log('timer3')
}, 0)
console.log("start")

其實如果你能做到這里了,說明你前面的那些知識點也都掌握了,我就不需要太過詳細的步驟分析了。注意:await后面是微任務,打印start后,先去async1里將await后面的微任務執行完

4.題目四

async function async1 () {console.log('async1 start');await new Promise(resolve => {console.log('promise1')})console.log('async1 success');return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')

思路:在async1中await后面的Promise是沒有返回值的,也就是它的狀態始終是pending狀態,因此相當于一直在await,await,await卻始終沒有響應...所以在await之后的內容是不會執行的,也包括async1后面的 .then。

5.題目五

在上題基礎上給Promise加上resolve

async function async1 () {console.log('async1 start');await new Promise(resolve => {console.log('promise1')resolve('promise1 resolve')}).then(res => console.log(res))console.log('async1 success');return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/10230.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/10230.shtml
英文地址,請注明出處:http://en.pswp.cn/web/10230.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

JavaScript BOM - 瀏覽器對象模型

BOM&#xff08;瀏覽器對象模型&#xff09;是JavaScript中與瀏覽器交互的一組API&#xff0c;它提供了一種方法來操作瀏覽器窗口和文檔。BOM由一組對象組成&#xff0c;這些對象允許您訪問瀏覽器本身的功能&#xff0c;而不僅僅是網頁內容。 BOM對象包括&#xff1a; window對…

融知財經:期貨和現貨的區別是什么?哪個風險大?

期貨和現貨在交易對象等方面存在明顯的區別。期貨交易是一種衍生金融工具&#xff0c;主要用于價格發現、風險管理和投機&#xff0c;而現貨交易則是商品和服務的實際買賣。在選擇進行期貨交易還是現貨交易時&#xff0c;投資者需要根據自己的需求和市場情況來決定。 期貨和現貨…

二叉搜索樹 題解 二叉搜索樹的構建 DFS

二叉搜索樹 題目描述 判斷兩序列是否為同一個二叉搜索樹序列。 輸入描述 第一行是一個數 n ( 1 < n < 20 )&#xff0c;表示有 n 個二叉搜索樹序列需要判斷。 接下去一行是一個序列&#xff0c;序列長度小于 10 &#xff0c;包含 0 ~ 9 的數字&#xff0c;沒有重復數…

【Android】Kotlin學習之Lambda表達式

java和kotlin對比 Lambda語法 Lambda隱形參數 it 也可以不使用指定的名稱it, 可以 自定義 Lambda 使用下劃線

原來Python處理word這么簡單:關于python操作文檔的問題

關于python操作文檔的問題 文檔類型&#xff1a;docx 語言&#xff1a;python 我想在文檔中姓名后面的下劃線之上插入一個姓名&#xff0c;并保存為新的文檔&#xff0c; 用python應該怎么實現呢 文檔見下圖 一般情況下&#xff0c;我們在看到題目的時候&#xff0c;應該先審題…

PHP+B/S架構 不良事件管理系統源碼 醫院不良事件報告系統源碼,開發技術vue2+element+laravel8

PHPB/S架構 不良事件管理系統源碼 醫院不良事件報告系統源碼&#xff0c;開發技術vue2elementlaravel8 技術架構&#xff1a;前后端分離&#xff0c;倉儲模式&#xff0c;BS架構&#xff0c; 開發技術&#xff1a;PHPvscodevue2elementlaravel8mysql5.7&#xff0c;專業團隊研…

[AutoSar]lauterbach_001_ORTI_CPUload_Trace

目錄 關鍵詞平臺說明一、ORTI概述二、ORTI文件的生成三、ORTI文件的導入四、Trace 功能4.1 Trace 功能菜單介紹4.2 Trace功能的配置4.3 Trace MCDS 設置4.4 Task Switches斷點的設置4.5 Trace 數據的錄取4.6 CPU 負載和Task調度的查看 關鍵詞 嵌入式、C語言、autosar、OS、BSW…

【高階數據結構】圖--最短路徑問題

圖--最短路徑問題 一、單源最短路徑--Dijkstra算法1、簡介2、解析3、代碼4、測試用例5、打印最小路徑代碼和測試6、缺陷&#xff1a;不能使用負路徑 二、單源最短路徑--Bellman-Ford算法1、簡介2、解析&#xff08;1&#xff09;詳情i、負權問題&#xff1a;一個點只跑一趟找最…

A股行情訂閱工具,支持股票/可轉債level2/level2數據

簡單使用 ./hqCenter -h-initCodesFile string啟動即訂閱的code (default "./data/initCodes.json")-listen stringhttp監聽地址 (default ":31800")-saveHqFile string行情寫入文件,自動加日期后綴。為空則不寫入文件。 (default "./data/hq")-…

PostGIS之pointcloud

瀚高數據庫 目錄 環境 文檔用途 詳細信息 環境 系統平臺&#xff1a;Linux x86-64 Red Hat Enterprise Linux 7 版本&#xff1a;14 文檔用途 本文詳細介紹pointcloud&#xff0c;包括&#xff1a;安裝配置、兩個核心數據類型、功能函數、使用PDAL讀寫pgpoingcloud數據等。 詳…

學習前端第三十四天(call,apply,函數綁定;箭頭函數;對象屬性配置)

一、call、apply function fn(x, y) { console.log("hello", x, y, this) }; 1.call方法 作用&#xff1a;調用后執行函數&#xff0c;可以給“this”傳參數 fn.call({ a: 1 }, 1, 2,); 2.apply方法 第一個給“this”傳參數&#xff0c;第二個參數需要是數組形式…

ElementUi中el-table組件使用row-class-name修改指定行顏色

可以通過指定 Table 組件的 row-class-name 屬性來為 Table 中的某一行添加 class&#xff0c;表明該行處于某種狀態。 注意&#xff1a;如果在el-table中使用了stripe這個屬性&#xff0c;這個屬性是帶斑馬紋的表格樣式&#xff0c;它和row-class-name共存時是沒有效果。 注…

【微信開發】微信支付前期準備工作(申請及配置)

1、申請并配置公眾號或微信小程序 1.1 賬戶申請 通過微信公眾平臺&#xff0c;根據指引申請微信小程序或公眾號&#xff0c;申請時需要微信認證&#xff0c;申請流程不在贅述 1.2 信息配置 申請通過后&#xff0c;需進入小程序和公眾號內進行信息配置 1.2.1 小程序信息配置…

Mac YOLO V9推理測試(基于ultralytics)

環境&#xff1a; Mac M1 (MacOS Sonoma 14.3.1) Python 3.11PyTorch 2.1.2 一、準備工作 使用YOLO一般都會接觸ultralytics這個框架&#xff0c;今天來試試用該框架進行YOLO V9模型的推理。 YOLOv9目前提供了四種模型下載&#xff1a;yolov9-c.pt、yolov9-e.pt、gelan-c.p…

lint 代碼規范,手動修復,以及vscode的第三方插件eslint自動修復

ESlint代碼規范 不是語法規范&#xff0c;是一種書寫風格&#xff0c;加多少空格&#xff0c;縮進多少&#xff0c;加不加分號&#xff0c;類似于書信的寫作格式 ESLint:是一個代碼檢查工具&#xff0c;用來檢查你的代碼是否符合指定的規則(你和你的團隊可以自行約定一套規則)…

【管理篇】如何橫向溝通?

目錄標題 什么是橫向溝通&#xff1f;常見溝通問題 如何處理橫向溝通中的問題&#xff1f; 什么是橫向溝通&#xff1f; 所謂橫向溝通&#xff0c;就是和沒有直接匯報關系的合作方之間的溝通&#xff0c;指的是與平級間進行的與完成工作有關的交流&#xff1b;橫向溝通核心的挑…

mqtt定時腳本

需求描述 給mqtt的topic發送信息 對應的topic接收請求后&#xff0c;執行發送短信指令 信息內容 SMS,10086,102 lua的mqtt里面&#xff0c;截取判斷即可 –>可以實現 定時任務&#xff0c; 每月開機一次 發短信&#xff1f; 或者使用開機通知&#xff1f; 定時消費…

Npm Install Docusaurus Demo【npm 安裝 docusaurus 實踐 】

文章目錄 1. 簡介2. 前提2.1 安裝 git2.2 安裝 node 3. 安裝4. 項目結構5. 訪問5.1 localhost 訪問5.2 ip 訪問 1. 簡介 Docusaurus 是一個facebook的開源項目&#xff0c;旨在幫助開發者構建易于維護和部署的文檔網站。它提供了一個簡單的方法來創建專業的文檔網站&#xff0…

共享旅游卡免費旅游真實反饋,有圖有真相?

新伙伴體驗&#xff0c;云南昆大麗6天5晚品質雙人游&#xff0c;真實反饋&#xff01;珠海伙伴蔡總&#xff0c;加入千益暢行共享旅游卡團隊&#xff0c;自己親自體驗“云南昆大麗6天5晚品質雙人游”真實反饋&#xff0c;分享全程內容截圖&#xff0c;無半點虛假&#xff01; …