W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
在 ?Promise
? 類中,有 6 種靜態(tài)方法。我們在這里簡單介紹下它們的使用場景。
假設我們希望并行執(zhí)行多個 promise,并等待所有 promise 都準備就緒。
例如,并行下載幾個 URL,并等到所有內(nèi)容都下載完畢后再對它們進行處理。
這就是 Promise.all
的用途。
語法:
let promise = Promise.all(iterable);
Promise.all
接受一個可迭代對象(通常是一個數(shù)組項為 promise 的數(shù)組),并返回一個新的 promise。
當所有給定的 promise 都 resolve 時,新的 promise 才會 resolve,并且其結(jié)果數(shù)組將成為新 promise 的結(jié)果。
例如,下面的 Promise.all
在 3 秒之后 settled,然后它的結(jié)果就是一個 [1, 2, 3]
數(shù)組:
Promise.all([
new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1
new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2
new Promise(resolve => setTimeout(() => resolve(3), 1000)) // 3
]).then(alert); // 1,2,3 當上面這些 promise 準備好時:每個 promise 都貢獻了數(shù)組中的一個元素
請注意,結(jié)果數(shù)組中元素的順序與其在源 promise 中的順序相同。即使第一個 promise 花費了最長的時間才 resolve,但它仍是結(jié)果數(shù)組中的第一個。
一個常見的技巧是,將一個任務數(shù)據(jù)數(shù)組映射(map)到一個 promise 數(shù)組,然后將其包裝到 Promise.all
。
例如,如果我們有一個存儲 URL 的數(shù)組,我們可以像這樣 fetch 它們:
let urls = [
'https://api.github.com/users/iliakan',
'https://api.github.com/users/remy',
'https://api.github.com/users/jeresig'
];
// 將每個 url 映射(map)到 fetch 的 promise 中
let requests = urls.map(url => fetch(url));
// Promise.all 等待所有任務都 resolved
Promise.all(requests)
.then(responses => responses.forEach(
response => alert(`${response.url}: ${response.status}`)
));
一個更真實的示例,通過 GitHub 用戶名來獲取一個 GitHub 用戶數(shù)組中用戶的信息(我們也可以通過商品 id 來獲取商品數(shù)組中的商品信息,邏輯都是一樣的):
let names = ['iliakan', 'remy', 'jeresig'];
let requests = names.map(name => fetch(`https://api.github.com/users/${name}`));
Promise.all(requests)
.then(responses => {
// 所有響應都被成功 resolved
for(let response of responses) {
alert(`${response.url}: ${response.status}`); // 對應每個 url 都顯示 200
}
return responses;
})
// 將響應數(shù)組映射(map)到 response.json() 數(shù)組中以讀取它們的內(nèi)容
.then(responses => Promise.all(responses.map(r => r.json())))
// 所有 JSON 結(jié)果都被解析:"users" 是它們的數(shù)組
.then(users => users.forEach(user => alert(user.name)));
如果任意一個 promise 被 reject,由 Promise.all
返回的 promise 就會立即 reject,并且?guī)в械木褪沁@個 error。
例如:
Promise.all([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).catch(alert); // Error: Whoops!
這里的第二個 promise 在兩秒后 reject。這立即導致了 Promise.all
的 reject,因此 .catch
執(zhí)行了:被 reject 的 error 成為了整個 Promise.all
的結(jié)果。
如果出現(xiàn) error,其他 promise 將被忽略
如果其中一個 promise 被 reject,
Promise.all
就會立即被 reject,完全忽略列表中其他的 promise。它們的結(jié)果也被忽略。
例如,像上面那個例子,如果有多個同時進行的
fetch
調(diào)用,其中一個失敗,其他的fetch
操作仍然會繼續(xù)執(zhí)行,但是Promise.all
將不會再關(guān)心(watch)它們。它們可能會 settle,但是它們的結(jié)果將被忽略。
Promise.all
沒有采取任何措施來取消它們,因為 promise 中沒有“取消”的概念。在 另一個章節(jié) 中,我們將介紹可以幫助我們解決這個問題(譯注:指的是“取消” promise)的AbortController
,但它不是 Promise API 的一部分。
?
Promise.all(iterable)
? 允許在 ?iterable
? 中使用非 promise 的“常規(guī)”值通常,
Promise.all(...)
接受含有 promise 項的可迭代對象(大多數(shù)情況下是數(shù)組)作為參數(shù)。但是,如果這些對象中的任何一個不是 promise,那么它將被“按原樣”傳遞給結(jié)果數(shù)組。
例如,這里的結(jié)果是
[1, 2, 3]
:
Promise.all([ new Promise((resolve, reject) => { setTimeout(() => resolve(1), 1000) }), 2, 3 ]).then(alert); // 1, 2, 3
所以我們可以在方便的地方將準備好的值傳遞給
Promise.all
。
最近新增的特性
這是一個最近添加到 JavaScript 的特性。 舊式瀏覽器可能需要 polyfills.
如果任意的 promise reject,則 Promise.all
整個將會 reject。當我們需要 所有 結(jié)果都成功時,它對這種“全有或全無”的情況很有用:
Promise.all([
fetch('/template.html'),
fetch('/style.css'),
fetch('/data.json')
]).then(render); // render 方法需要所有 fetch 的數(shù)據(jù)
Promise.allSettled
等待所有的 promise 都被 settle,無論結(jié)果如何。結(jié)果數(shù)組具有:
{status:"fulfilled", value:result}
? 對于成功的響應,{status:"rejected", reason:error}
? 對于 error。例如,我們想要獲取(fetch)多個用戶的信息。即使其中一個請求失敗,我們?nèi)匀粚ζ渌母信d趣。
讓我們使用 Promise.allSettled
:
let urls = [
'https://api.github.com/users/iliakan',
'https://api.github.com/users/remy',
'https://no-such-url'
];
Promise.allSettled(urls.map(url => fetch(url)))
.then(results => { // (*)
results.forEach((result, num) => {
if (result.status == "fulfilled") {
alert(`${urls[num]}: ${result.value.status}`);
}
if (result.status == "rejected") {
alert(`${urls[num]}: ${result.reason}`);
}
});
});
上面的 (*)
行中的 results
將會是:
[
{status: 'fulfilled', value: ...response...},
{status: 'fulfilled', value: ...response...},
{status: 'rejected', reason: ...error object...}
]
所以,對于每個 promise,我們都得到了其狀態(tài)(status)和 value/reason
。
如果瀏覽器不支持 Promise.allSettled
,很容易進行 polyfill:
if (!Promise.allSettled) {
const rejectHandler = reason => ({ status: 'rejected', reason });
const resolveHandler = value => ({ status: 'fulfilled', value });
Promise.allSettled = function (promises) {
const convertedPromises = promises.map(p => Promise.resolve(p).then(resolveHandler, rejectHandler));
return Promise.all(convertedPromises);
};
}
在這段代碼中,promises.map
獲取輸入值,并通過 p => Promise.resolve(p)
將輸入值轉(zhuǎn)換為 promise(以防傳遞了非 promise 值),然后向每一個 promise 都添加 .then
處理程序。
這個處理程序?qū)⒊晒Φ慕Y(jié)果 value
轉(zhuǎn)換為 {status:'fulfilled', value}
,將 error reason
轉(zhuǎn)換為 {status:'rejected', reason}
。這正是 Promise.allSettled
的格式。
然后我們就可以使用 Promise.allSettled
來獲取 所有 給定的 promise 的結(jié)果,即使其中一些被 reject。
與 Promise.all
類似,但只等待第一個 settled 的 promise 并獲取其結(jié)果(或 error)。
語法:
let promise = Promise.race(iterable);
例如,這里的結(jié)果將是 1
:
Promise.race([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).then(alert); // 1
這里第一個 promise 最快,所以它變成了結(jié)果。第一個 settled 的 promise “贏得了比賽”之后,所有進一步的 result/error 都會被忽略。
與 Promise.race
類似,區(qū)別在于 Promise.any
只等待第一個 fulfilled 的 promise,并將這個 fulfilled 的 promise 返回。如果給出的 promise 都 rejected,那么返回的 promise 會帶有 AggregateError
—— 一個特殊的 error 對象,在其 errors
屬性中存儲著所有 promise error。
語法如下:
let promise = Promise.any(iterable);
例如,這里的結(jié)果將是 1
:
Promise.any([
new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 1000)),
new Promise((resolve, reject) => setTimeout(() => resolve(1), 2000)),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).then(alert); // 1
這里的第一個 promise 是最快的,但 rejected 了,所以第二個 promise 則成為了結(jié)果。在第一個 fulfilled 的 promise “贏得比賽”后,所有進一步的結(jié)果都將被忽略。
這是一個所有 promise 都失敗的例子:
Promise.any([
new Promise((resolve, reject) => setTimeout(() => reject(new Error("Ouch!")), 1000)),
new Promise((resolve, reject) => setTimeout(() => reject(new Error("Error!")), 2000))
]).catch(error => {
console.log(error.constructor.name); // AggregateError
console.log(error.errors[0]); // Error: Ouch!
console.log(error.errors[1]); // Error: Error!
});
正如你所看到的,我們在 AggregateError
錯誤類型的 error 實例的 errors
屬性中可以訪問到失敗的 promise 的 error 對象。
在現(xiàn)代的代碼中,很少需要使用 Promise.resolve
和 Promise.reject
方法,因為 async/await
語法(我們會在 稍后 講到)使它們變得有些過時了。
完整起見,以及考慮到那些出于某些原因而無法使用 async/await
的人,我們在這里對它們進行介紹。
Promise.resolve(value)
用結(jié)果 value
創(chuàng)建一個 resolved 的 promise。
如同:
let promise = new Promise(resolve => resolve(value));
當一個函數(shù)被期望返回一個 promise 時,這個方法用于兼容性。(譯注:這里的兼容性是指,我們直接從緩存中獲取了當前操作的結(jié)果 value
,但是期望返回的是一個 promise,所以可以使用 Promise.resolve(value)
將 value
“封裝”進 promise,以滿足期望返回一個 promise 的這個需求。)
例如,下面的 loadCached
函數(shù)獲?。╢etch)一個 URL 并記住其內(nèi)容。以便將來對使用相同 URL 的調(diào)用,它能立即從緩存中獲取先前的內(nèi)容,但使用 Promise.resolve
創(chuàng)建了一個該內(nèi)容的 promise,所以返回的值始終是一個 promise。
let cache = new Map();
function loadCached(url) {
if (cache.has(url)) {
return Promise.resolve(cache.get(url)); // (*)
}
return fetch(url)
.then(response => response.text())
.then(text => {
cache.set(url,text);
return text;
});
}
我們可以使用 loadCached(url).then(…)
,因為該函數(shù)保證了會返回一個 promise。我們就可以放心地在 loadCached
后面使用 .then
。這就是 (*)
行中 Promise.resolve
的目的。
Promise.reject(error)
用 error
創(chuàng)建一個 rejected 的 promise。
如同:
let promise = new Promise((resolve, reject) => reject(error));
實際上,這個方法幾乎從未被使用過。
?Promise
? 類有 6 種靜態(tài)方法:
Promise.all(promises)
? —— 等待所有 promise 都 resolve 時,返回存放它們結(jié)果的數(shù)組。如果給定的任意一個 promise 為 reject,那么它就會變成 ?Promise.all
? 的 error,所有其他 promise 的結(jié)果都會被忽略。Promise.allSettled(promises)
?(ES2020 新增方法)—— 等待所有 promise 都 settle 時,并以包含以下內(nèi)容的對象數(shù)組的形式返回它們的結(jié)果:status
?: ?"fulfilled"
? 或 ?"rejected"
?value
?(如果 fulfilled)或 ?reason
?(如果 rejected)。Promise.race(promises)
? —— 等待第一個 settle 的 promise,并將其 result/error 作為結(jié)果返回。
Promise.any(promises)
?(ES2021 新增方法)—— 等待第一個 fulfilled 的 promise,并將其結(jié)果作為結(jié)果返回。如果所有 promise 都 rejected,?Promise.any
? 則會拋出 ?AggregateError
? 錯誤類型的 error 實例。
Promise.resolve(value)
? —— 使用給定 value 創(chuàng)建一個 resolved 的 promise。
Promise.reject(error)
? —— 使用給定 error 創(chuàng)建一個 rejected 的 promise。以上所有方法,Promise.all
可能是在實戰(zhàn)中使用最多的。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: