您好,登錄后才能下訂單哦!
因?yàn)?JavaScript 是單線程語言,所以同步代碼一次只能執(zhí)行一行。這就意味著同步代碼的運(yùn)行時(shí)間超過瞬間的話,它將停止其余代碼的運(yùn)行,直到完成運(yùn)行為止。為了防止運(yùn)行時(shí)間不確定的代碼阻止其他代碼的運(yùn)行,我們需要使用異步代碼。
Promise
為此,我們可以在代碼中使用 Promise。Promise 表示流程運(yùn)行時(shí)間不確定并且結(jié)果可能是成功或失敗的對(duì)象。在 JavaScript 中創(chuàng)建一個(gè) promises,我們使用 Promise 對(duì)象的構(gòu)造函數(shù)來創(chuàng)建 promise。Promise 構(gòu)造函數(shù)接受一個(gè)擁有 resolve 和 reject 參數(shù)的執(zhí)行函數(shù)。兩個(gè)參數(shù)也都是函數(shù),它們讓我們可以回調(diào) promise 完成(成功調(diào)用得到返回值)或者拒絕(返回錯(cuò)誤值并標(biāo)記 promise 失?。?。函數(shù)的返回值被忽略。因此,promise 只能返回 promise。
例如,我們?cè)?JavaScript 定義一個(gè)承諾像下面的代碼:
const promise = new Promise((resolve, reject) => { setTimeout(() => resolve('abc'), 1000); });
上面的代碼會(huì)創(chuàng)建一個(gè)一秒后返回 abc 的 promise。因?yàn)槲覀冞\(yùn)行的 setTimeout 是在 executor 函數(shù)內(nèi)一秒后返回值為 abc 的完成 promise,所以它是異步代碼。我們無法在 setTimeout 的回調(diào)函數(shù)中返回值 abc,因此我們必須調(diào)用 resolve('abc') 來獲取解析后的值。我們可以使用 then 函數(shù)來訪問已完成 promise 的返回值。then 函數(shù)接受一個(gè)回調(diào)函數(shù),該回調(diào)函數(shù)將已完成的 promise 的返回值作為參數(shù)。你可以得到你想要的值。例如,我們可以這么做:
const promise = new Promise((resolve, reject) => { setTimeout(() => resolve('abc'), 1000); }); promise.then((val) => { console.log(val); })
當(dāng)我們運(yùn)行上方代碼,我們應(yīng)該可以得到記錄 abc。正如我們看到的,promise 會(huì)在其完成后調(diào)用 resolve 函數(shù)時(shí)提供值。
一個(gè) promise 有三種狀態(tài)。初始狀態(tài),promise 既沒有成功也沒有失敗。完成狀態(tài),意味著操作成功完成?;蛘呤鞘顟B(tài),意味著 promise 操作失敗。
一個(gè)掛起的 promise 可以通過返回值完成或者返回錯(cuò)誤失敗。當(dāng) promise 完成后,then 函數(shù)將得到相應(yīng)的返回值,并傳遞給 then 函數(shù)的回調(diào)函數(shù)來調(diào)用。如果 promise 失敗后,我們可以選擇使用 catch 函數(shù)來捕獲錯(cuò)誤,該函數(shù)還可以傳遞給回調(diào)函數(shù)一個(gè)錯(cuò)誤。then 和 catch 都返回一個(gè) promise,因此它們可以一起鏈?zhǔn)绞褂谩?/p>
例如,我們可以這么寫:
const promise = (num) => { return new Promise((resolve, reject) => { setTimeout(() => { if (num === 1) { resolve('resolved') } else { reject('rejected') } }, 1000); }); } promise(1) .then((val) => { console.log(val); }) .catch((error) => { console.log(error); })promise(2) .then((val) => { console.log(val); }) .catch((error) => { console.log(error); })
在上面的代碼中,我們有一個(gè)函數(shù) promise,它返回一個(gè) JavaScript promise,其在 num 為 1 時(shí)以 resolved 值完成承諾,在 num 不為 1 時(shí)通過錯(cuò)誤 rejected 拒絕承諾。因此我們運(yùn)行:
promise(1) .then((val) => { console.log(val); }) .catch((error) => { console.log(error); })
然后 then 函數(shù)運(yùn)行,由于 num 為 1,promise(1) 函數(shù)調(diào)用會(huì)返回 promise 完成,并且解析后的值在 val 中是可用的。因此當(dāng)我們運(yùn)行 console.log(val),我們會(huì)得到 resolved。當(dāng)我們運(yùn)行下面的代碼:
promise(2) .then((val) => { console.log(val); }) .catch((error) => { console.log(error); })
catch 會(huì)運(yùn)行,因?yàn)?promise(2) 函數(shù)調(diào)用失敗返回 promise,并且被拒絕的錯(cuò)誤值可用并設(shè)置為錯(cuò)誤。因此我們運(yùn)行 console.log(error) 會(huì)得到 rejected 輸出。
一個(gè) JavaScript promise 對(duì)象有一下屬性:length 和 prototype。 length 是構(gòu)造器參數(shù)的數(shù)量,其設(shè)置為 1,也總是只有一個(gè)。prototype 屬性表示 promise 對(duì)象的原型。
promise 還有一個(gè) finally 方法無論 promise 完成還是失敗都運(yùn)行的代碼。finally 方法接受一個(gè)回調(diào)函數(shù)作為參數(shù),可以運(yùn)行任何你想運(yùn)行的代碼, 并且無論 promise 運(yùn)行結(jié)果如何,都可以執(zhí)行。例如,我們運(yùn)行:
Promise.reject('error') .then((value) => { console.log(value); }) .catch((error) => { console.log(error); }) .finally(() => { console.log('finally runs'); })
然后我們得到 error 和 finally runs 的記錄,因?yàn)樵嫉?promise 得到 error 而拒絕。然后運(yùn)行 finally 方法中的所有代碼。
使用 promise 最主要的好處是編寫的異步代碼,我們可以使用 promise 順序運(yùn)行它們。為此,我們可以使用 then 函數(shù)鏈?zhǔn)?promise。then 函數(shù)在 promise 完成后接收一個(gè)回調(diào)函數(shù)并運(yùn)行它。在 promise 拒絕后,它還接受第二個(gè)參數(shù)。鏈?zhǔn)绞褂?promise,我們必須將其第一個(gè)回調(diào)函數(shù) then 函數(shù)返回另外一個(gè) promise。如果我們不想將另外一個(gè) promise 鏈接到現(xiàn)有的 promise,我們可以返回其他值,就像沒有一樣。我們可以返回一個(gè)值,該值將在下一個(gè) then 函數(shù)中可獲取到。它還可以拋出一個(gè)錯(cuò)誤。然后 then 返回的 promise 將被拒絕,并拋出錯(cuò)誤。它還可以返回已經(jīng)完成或拒絕的 promise,擋在其后鏈接的 then 函數(shù)時(shí)將獲得其完成后的值,或者 catch 函數(shù)的回調(diào)中獲得錯(cuò)誤原因。
例如,我們可以這樣寫:
Promise.resolve(1) .then(val => { console.log(val); return Promise.resolve(2) }) .then(val => { console.log(val); })Promise.resolve(1) .then(val => { console.log(val); return Promise.reject('error') }) .then(val => { console.log(val); }) .catch(error => console.log(error));Promise.resolve(1) .then(val => { console.log(val); throw new Error('error'); }) .then(val => { console.log(val); }) .catch(error => console.log(error));
在第一個(gè)例子中,我們鏈?zhǔn)秸{(diào)用 promise,并且都 resolve 一個(gè)值。所有的 promise 都準(zhǔn)備返回為值。在第二個(gè)和最后一個(gè)例子中,我們拒絕了第二個(gè) promise 或拋出一個(gè)錯(cuò)誤。它們都做了同樣的事情。第二個(gè) promise 被拒絕了,并且錯(cuò)誤原因會(huì)被回調(diào)函數(shù) catch 函數(shù)記錄下來。我們還可以鏈接掛起狀態(tài)的 promise,如下方代碼所示:
const promise1 = new Promise((resolve, reject) => { setTimeout(() => resolve(1), 1000); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => resolve(2), 1000); }); promise1 .then(val => { console.log(val); return promise2; }) .then(val => { console.log(val); }) .catch(error => console.log(error));
回調(diào)函數(shù) then 函數(shù)返回了 promise2,這是一個(gè)掛起狀態(tài)的 promise。
方法
JavaScript 的 promise 有以下方法。
Promise.all (可迭代對(duì)象)
Promise.all 接受一個(gè)可迭代對(duì)象,該對(duì)象允許我們?cè)谀承┯?jì)算機(jī)上并行運(yùn)行多個(gè) promise,并在其他計(jì)算機(jī)上連續(xù)運(yùn)行多個(gè) promise。這對(duì)于運(yùn)行多個(gè)不依賴于彼此的值的 promise 非常方便。它接受一個(gè)包含 promise 的列表 (通常是一個(gè)數(shù)組) 的可迭代對(duì)象,然后返回一個(gè) Promise,這個(gè) Promise 在可迭代對(duì)象中的 promise 被解析時(shí)解析。
例如,我們像下面這樣寫代碼,使用 Promise.all 來運(yùn)行多個(gè) promise:
const promise1 = Promise.resolve(1); const promise2 = 2; const promise3 = new Promise((resolve, reject) => { setTimeout(() => resolve(3), 1000); }); Promise.all([promise1, promise2, promise3]) .then((values) => { console.log(values); });
如果我們運(yùn)行了上方的代碼,然后 console.log 應(yīng)該會(huì)記錄下 [1,2,3]。如我們所見,只有所有的 promise 都完成后返回它的解析值。如果其中的拒絕了,我們不會(huì)得到任何解析值。相反,我們將得到由被拒絕的 promise 返回的任何錯(cuò)誤值。它將會(huì)在第一個(gè)被拒絕的 promise 處停止,并且發(fā)送值給回調(diào)函數(shù) catch 函數(shù)。例如,如果我們這樣:
const promise1 = Promise.resolve(1); const promise2 = Promise.reject(2); const promise3 = new Promise((resolve, reject) => { setTimeout(() => reject(3), 1000); }); Promise.all([promise1, promise2, promise3]) .then((values) => { console.log(values); }) .catch(error => { console.log(error); });
然后我們可以在回調(diào)函數(shù) catch 函數(shù)的 console.log 中得到兩個(gè)記錄。
Promise.allSettled
Promise.allSettled 返回一個(gè) promise,該 promise 的解析在所有的 promise 解析完或拒絕后。它接受帶有一組 promise 的可迭代對(duì)象,例如,一個(gè) promise 數(shù)組。返回的 promise 的解析值是每個(gè) promise 的最終狀態(tài)的數(shù)組。例如,假設(shè)我們有:
const promise1 = Promise.resolve(1); const promise2 = Promise.reject(2); const promise3 = new Promise((resolve, reject) => { setTimeout(() => reject(3), 1000); }); Promise.allSettled([promise1, promise2, promise3]) .then((values) => { console.log(values); })
若我們運(yùn)行上方代碼,我們將獲得一個(gè)包含三個(gè)條目的數(shù)組,每個(gè)條目都是一個(gè)對(duì)象,該對(duì)象有已經(jīng)完成 promise 的 status 和 value 屬性以及被拒絕的 promise 的 status 和 reason 屬性。例如,上面的代碼會(huì)記錄 {status: “fulfilled”, value: 1},{status: “rejected”, reason: 2},{status: “rejected”, reason: 3}。 fulfilled 狀態(tài)記錄的是成功的 promise,rejected 狀態(tài)為被拒絕的 promise。
Promise.race
Promise.race 方法返回一個(gè) promise,該 promise 會(huì)解析首先完成的 promise 的解析值。它接受一個(gè)帶有 promise 集合的可迭代對(duì)象,例如,一個(gè) promise 數(shù)組。如果傳入的可迭代對(duì)象為空,則返回的 promise 將一直掛起。若可迭代對(duì)象包含一個(gè)或多個(gè)非 promise 值或者已經(jīng)完成的 promise,Promise.race 將會(huì)返回這些條目中的第一個(gè)。例如,我們有:
const promise1 = Promise.resolve(1); const promise2 = Promise.resolve(2); const promise3 = new Promise((resolve, reject) => { setTimeout(() => resolve(3), 1000); }); Promise.race([promise1, promise2, promise3]) .then((values) => { console.log(values); })
然后我們看到 1 會(huì)被記錄 。那是因?yàn)?promise1 是第一個(gè)被解析的,那是因?yàn)樗窃谙乱恍羞\(yùn)行之前就被解析了。同樣,如果我們的數(shù)組中有一個(gè)非 promise 值作為參數(shù)進(jìn)行傳入,如下代碼所示:
const promise1 = 1; const promise2 = Promise.resolve(2); const promise3 = new Promise((resolve, reject) => { setTimeout(() => resolve(3), 1000); }); Promise.race([promise1, promise2, promise3]) .then((values) => { console.log(values); })
然后我們會(huì)得到相同的記錄,因?yàn)樗俏覀儌鬟f給 Promise.race 方法的數(shù)組中的非 promise 值。同步代碼始終運(yùn)行在異步代碼之前,而不論同步代碼在哪里。如果我們有:
const promise1 = new Promise((resolve, reject) => { setTimeout(() => resolve(1), 2000); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => resolve(2), 1000); }); const promise3 = 3; Promise.race([promise1, promise2, promise3]) .then((values) => { console.log(values); })
然后我們記錄下 3,因?yàn)?setTimeout 將回調(diào)函數(shù)放入隊(duì)列中以便稍后運(yùn)行,所以它將比同步代碼更晚執(zhí)行。
最后,如果我們有:
const promise1 = new Promise((resolve, reject) => { setTimeout(() => resolve(1), 2000); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => resolve(2), 1000); }); Promise.race([promise1, promise2]) .then((values) => { console.log(values); })
然后我們?cè)诳刂婆_(tái)中得到記錄 2,因?yàn)樵谝幻虢馕龅?promise 要比兩秒解析的 promise 更早解析。
Promise.reject
Promise.reject 返回一個(gè)因某種原因拒絕的 promise。拒絕帶有 Error 的實(shí)例對(duì)象的 promise 非常有用。例如,如果我們有以下代碼:
Promise.reject(new Error('rejected')) .then((value) => { console.log(value); }) .catch((error) => { console.log(error); })
然后我們得到 rejected 記錄。
Promise.resolve
Promise.resolve 返回一個(gè)已解析為傳入 resolve 函數(shù)參數(shù)的值的 promise。我們也可以傳遞一個(gè)帶有 then 屬性的對(duì)象,它的值是 promise 的回調(diào)函數(shù)。如果該值具有 then 方法,則將使用 then 函數(shù)完成 promise。也就是說,then 函數(shù)值的第一個(gè)參數(shù)與 resolve 相同,以及第二個(gè)參數(shù)與 reject 相同。例如,我們可以編寫如下代碼:
Promise.resolve(1) .then((value) => { console.log(value); })
然后我們得到 1 記錄,因?yàn)?1 是我們傳遞給 resolve 函數(shù)來返后具有解析值 1 的承諾的值。
如果我們傳入的對(duì)象內(nèi)部帶有 then 方法,如下代碼所示:
Promise.resolve({ then(resolve, reject) { resolve(1); } }) .then((value) => { console.log(value); })
然后我們得到記錄的值 1。這是因?yàn)?Promise.resolve 函數(shù)將運(yùn)行 then 函數(shù),設(shè)置為 “then” 屬性的函數(shù)的 “resolve” 參數(shù)將被假定為承諾中稱為 “resolve” 函數(shù)的函數(shù)。并將該函數(shù)的 resolve 參數(shù)設(shè)置為 then 屬性可以看作 promise 中一個(gè)叫做 resolve 函數(shù)。如果我們將傳入 then 中的對(duì)象替換為 inject 函數(shù),然后我們就可以得到被拒絕的 promise。代碼如下所示:
Promise.resolve({ then(resolve, reject) { reject('error'); } }) .then((value) => { console.log(value); }) .catch((error) => { console.log(error); })
在上面的代碼中,我們會(huì)得到 error 記錄,這是因?yàn)?promise 被拒絕了。
Async 和 Await
使用 async 和 await,我們可以縮短 promise 代碼。使用 async 和 await 之前前,我們必須得用 then 函數(shù)并且在 then 函數(shù)中放入回調(diào)函數(shù)作為所有的參數(shù)。這就使得我們有很多 promise 時(shí)代碼冗長至極。相反,我們可以使用 async 和 await 語法來替代 then 函數(shù)以及相關(guān)回調(diào)。例如,我們可將以下代碼縮短為:
const promise1 = new Promise((resolve, reject) => { setTimeout(() => resolve(1), 2000); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => resolve(2), 1000); }); promise1 .then((val1) => { console.log(val1); return promise2; }) .then((val2) => { console.log(val2); })
寫成:
const promise1 = new Promise((resolve, reject) => { setTimeout(() => resolve(1), 2000); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => resolve(2), 1000); }); (async () => { const val1 = await promise1; console.log(val1) const val2 = await promise2; console.log(val2) })()
我們使用 await 來替換 then 和回調(diào)函數(shù)。然后,我們就可以將每個(gè) promise 的解析值分配為變量。注意,如果我們?yōu)?promise 代碼使用 await,那么我們必須像上例那樣添加 async 到函數(shù)簽名中。為了捕獲錯(cuò)誤,我們使用 catch 子句取代鏈?zhǔn)?catch 函數(shù)。另外,我們沒有在底部鏈?zhǔn)秸{(diào)用 finally 函數(shù)以在 promise 結(jié)束時(shí)運(yùn)行代碼,而是在 catch 子句后使用 finally 子句。
例如,我們可以這樣寫:
const promise1 = new Promise((resolve, reject) => { setTimeout(() => resolve(1), 2000); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => reject('error'), 1000); }); (async () => { try { const val1 = await promise1; console.log(val1) const val2 = await promise2; console.log(val2) } catch (error) { console.log(error) } finally { console.log('finally runs'); }})()
在上面的代碼中,我們獲得了分配給變量的 promise 的解析值,而不是在 then 函數(shù)的回調(diào)中獲取值,例如在 const response = await promise1 上面的一行。另外,我們使用 try...catch...finally 塊來捕獲被拒絕的 promise 的錯(cuò)誤,以及 finally 子句替代 finally 函數(shù),其無論 promise 執(zhí)行結(jié)果如何,該代碼都可以運(yùn)行。
想其他使用 promise 的函數(shù)一樣,async 函數(shù)始終返回 promise,并且不能返回其他任何東西。在上面的示例中,我們證明了與使用帶有回調(diào)函數(shù)作為參數(shù)傳遞的 then 函數(shù)相比,我們可以更短的方式使用 promise。
結(jié)束語
使用 promise,讓我們編寫異步代碼更容易。promise 是表示一個(gè)處理的運(yùn)行時(shí)間不確定并且結(jié)果會(huì)成功也會(huì)失敗的對(duì)象。在 JavaScript 中創(chuàng)建一個(gè) promises,我們使用 Promise 對(duì)象,該對(duì)象是用于創(chuàng)建 promise 的構(gòu)造函數(shù)。
Promise 構(gòu)造函數(shù)接受一個(gè)擁有 resolve 和 reject 參數(shù)的執(zhí)行函數(shù)。兩個(gè)參數(shù)都是函數(shù),它們讓我們可以回調(diào) promise 完成(成功調(diào)用得到返回值)或者拒絕(返回錯(cuò)誤值并標(biāo)記 promise 失敗)。 The 函數(shù)的返回值被忽略。因此,promise 只能返回 promise。
因?yàn)?promise 返回 promise,所以 promise 是可鏈?zhǔn)秸{(diào)用。promise 的 then 函數(shù)可以拋出一個(gè)錯(cuò)誤,返回解析值,或者其他 promise(掛起、已完成或已拒絕的 promise)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。