阿川私房教材:學程式,拿 offer!

63 個專案實戰,直接上手!
無需補習,按步驟打造你的面試作品。

立即解鎖你的轉職秘笈

如果您從事 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


共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。

阿川私房教材:學程式,拿 offer!

63 個專案實戰,直接上手!
無需補習,按步驟打造你的面試作品。

立即解鎖你的轉職秘笈