如果您從事 Web 開發,那麼您 100% 的機會至少使用過一些非同步函數。使用非同步函數有多種方式,例如.then()
和async/await.
但是,如果我告訴您有更好的方法來使用非同步函數,可以將請求時間減少一半呢? 🤯
對,是真的! JavaScript 執行時提供了我們通常不知道或不使用的各種功能。這些功能之一是 Promise 類別的靜態方法。
在這篇簡短的部落格文章中,我們將了解如何使用它們來改進我們的非同步函數呼叫。
Promise.all()
Promise.all()
方法採用可迭代的 Promise 作為輸入,並傳回解析為輸入 Promise 結果陣列的單一 Promise。如果任何輸入 Promise 拒絕或非 Promise 拋出錯誤,它會立即拒絕,並將使用第一個拒絕方法拒絕。
這是一個例子:
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve,reject)=>{
setTimeout(resolve,100,'foo');
})
Promise.all([promise1,promise2,promise3]).then((values)=>{
console.log(values);
})
// expected output Array [3,42,'foo']
現在讓我們看看如何使用它來加速非同步呼叫:
通常,當一個接一個地進行非同步函數呼叫時,每個請求都會被前面的請求阻塞,這種模式也稱為「瀑布」模式,因為每個請求只有在前一個請求返回一些資料後才能開始。
// Simulate two API calls with different response times
function fetchFastData() {
return new Promise(resolve => {
setTimeout(() => {
resolve("Fast data");
}, 2000);
});
}
function fetchSlowData() {
return new Promise(resolve => {
setTimeout(() => {
resolve("Slow data");
}, 3000);
});
}
// Function to demonstrate sequential execution
async function fetchDataSequentially() {
console.log("Starting to fetch data...");
const startTime = Date.now();
// Start both fetches concurrently
const fastData = await fetchFastData();
const slowData = await fetchSlowData();
const endTime = Date.now();
const totalTime = endTime - startTime;
console.log(`Fast data: ${fastData}`);
console.log(`Slow data: ${slowData}`);
console.log(`Total time taken: ${totalTime}ms`);
}
fetchDataSequentially()
/*
expected output:
Starting to fetch data...
Fast data: Fast data
Slow data: Slow data
Total time taken: 5007ms
*/
這是一個更好的可視化圖表
使用Promise.all()
我們可以一次觸發所有請求,然後等待所有請求完成,這樣,由於請求不必等待前一個請求完成,因此它們可以儘早開始,從而儘早解決。一旦傳遞給它的所有承諾都得到解決, Promise.all()
將傳回一個包含已解決承諾的陣列。
以下是我們如何使用 Promise 改進fetchData
函數。
async function fetchDataConcurrently() {
console.log("Starting to fetch data...");
const startTime = Date.now();
// Start both fetches concurrently
const fastDataPromise = fetchFastData();
const slowDataPromise = fetchSlowData();
// Wait for both promises to resolve
const [fastData, slowData] = await Promise.all([fastDataPromise, slowDataPromise]);
const endTime = Date.now();
const totalTime = endTime - startTime;
console.log(`Fast data: ${fastData}`);
console.log(`Slow data: ${slowData}`);
console.log(`Total time taken: ${totalTime}ms`);
}
/*
expected output:
Starting to fetch data...
Fast data: Fast data
Slow data: Slow data
Total time taken: 3007ms
*/
這是一個更好的可視化圖表
我們將陣列中的所有 Promise 傳遞給 Promise.all(),然後等待它。如果有多個請求,這樣我們可以節省很多時間。
不過,有一點要考慮:如果一個承諾被拒絕呢?如果我們在這種情況下使用Promise.all()
,它只會拒絕被拒絕的 Promise。如果我們想得到所有承諾的結果,無論是解決還是拒絕怎麼辦?
為了處理這種情況,我們可以使用Promise.allSettled()
。我們也來了解一下吧。
Promise.allSettled()
它是Promise.all()
的一個微小變體,不同之處在於Promise.allSettled()
總是解析,無論傳遞給它的 Promise 解析還是拒絕,它都會傳回包含傳遞給它的 Promise 結果的陣列。
const promise1 = Promise.reject("failure");
const promise2 = 42;
const promise3 = new Promise((resolve) => {
setTimeout(resolve, 100, 'foo');
});
Promise.allSettled([promise1, promise2, promise3]).then((results) => {
console.log(results);
});
// expected output: Array [
// { status: "rejected", reason: "failure" },
// { status: "fulfilled", value: 42 },
// { status: "fulfilled", value: 'foo' }
// ]
另一個有用的靜態方法是Promise.race()
,它可用來實作非同步函數的逾時。讓我們看看如何:
Promise.race()
Promise.race() 方法傳回一個 Promise,只要在陣列中傳遞給它的任何一個 Promise 滿足或拒絕,該 Promise 就會立即滿足或拒絕,並提供拒絕 Promise 的值或原因。
const promise1 = new Promise((resolve,reject)=>{
setTimeout(resolve,500,'one');
})
const promise2 = new Promise((resolve,reject)=>{
setTimeout(resolve,100,'two');
})
Promise.race([promise1,promise2]).then((value)=>{
console.log(value);
// Both resolves but promise2 is faster.
})
// expected output: 'two'
讓我們看看如何使用它來實現超時:
function fetchData() {
return new Promise(resolve => {
setTimeout(() => {
resolve("Fast data");
}, 6000);
});
}
const fetchDataPromise = fetchData();
function fetchDataWithTimeout(promise,duration){
return Promise.race(
[
promise,
new Promise((_,reject)=>{
setTimeout(reject,duration,"Too late.")
})
]
)
}
fetchDataWithTimeout(fetchDataPromise,5000).then((result)=>{
console.log(result)
}).catch((error)=>{
console.log(error)
})
/*
expected result:
Too late
*/
這就是這篇文章的內容。如果您想了解有關異步 Promise 的更多訊息,請查看我關於異步 Promise 的其他部落格文章: 非同步 JavaScript:您將永遠記得的 TL;DR 版本。
感謝您的閱讀,希望您學到新東西!
原文出處:https://dev.to/adityabhattad/better-way-to-make-async-calls-calls-in-javascript-dag