Yu Ken Code

[筆記] 處理 JavaScript 非同步

2021/10/7js5863 minutes

[筆記] 處理 JavaScript 非同步

目錄

  • 何謂非同步?
  • Promise 相關方法整理

何謂非同步?

在 javaScript 非同步的特性中,程式碼在依序執行若遇到非同步的方法,舉凡像是 setTimeout 或是 setInterval 等等,會跳過他們繼續往下執行

console.log("111");

setTimeout(() => {
  console.log("222");
}, 300);

console.log("333");

// Ans: "111" "333" "222"

解決辦法一,Callback

console.log("111");

setTimeout(() => {
  console.log("222");
  console.log("333");
}, 300);

// Ans: "111" "222" "333"

直接將 console.log("333") 放入 setTimeout 裡面,我們不管非同步的程式何時會結束,反正他結束的時候再執行 console.log("333") 就好

優點:非常直覺,對於那些要等待非同步的程式碼執行完才執行的程式,我們一律丟在非同步的程式裡面

缺點:若今天非同步的程式裡面又有非同步,然後裡面又有非同步...,就容易寫得變很巢狀,未來會較難維護

解決辦法二,Promise

console.log("111");

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("222");
  }, 300);
}).then(value => {
  console.log(value);
  console.log("333");
});

// Ans: "111", "222", "333"

將非同步包裝在 promise 裡面,等待 promise 結果回傳時再執行接下來的東西

優點:相較於 callback,若非同步裡面還有非同步,就不會寫得太巢狀,並且只要處理好,他可以保證結果會回傳,不論是失敗或成功

缺點:若非同步裡面還有非同步,有可能會一直 .then 的寫下去

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("222");
  }, 300);
})
  .then(value => {
    console.log(value);
    return new Promise((resolve, reject) => {
      setTimeout(() => resolve("333"), 300);
    });
  })
  .then(value => {
    console.log(value);
    return new Promise((resolve, reject) => {
      setTimeout(() => resolve("444"), 300);
    });
  })
  .then(value => {
    console.log(value);
    return new Promise((resolve, reject) => {
      setTimeout(() => resolve("555"), 300);
    });
  })
  .then(value => {
    console.log(value);
  });

// Ans "111", "222", "333", "444", "555"

解決辦法三,Async / Await

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("222");
  }, 300);
});

const asyncFunction = async () => {
  console.log("111");
  const result = await promise1;
  console.log(result);
  console.log("333");
};

asyncFunction();

// Ans "111", "222", "333"

優點:程式碼逐一執行下去,如果遇到非同步,會等待該非同步程式結束才繼續往下

缺點:若在 async 內部使用些陣列方法像是 forEach 或是 map 等,會吃不到外面的 async,面對這個狀況可以改用 for 迴圈取代一般陣列方法

Promise 相關方法整理

Promise.all

應用時機:

常見場景為,當我們今天有發送多隻 api request,我們不想要等第一隻 api 發送完才送第二隻,要執行多隻 api 併發,就可以使用 Promise.all 的方法

實作方法:

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("111");
  }, 300);
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("222");
  }, 300);
});

const promise3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("333");
  }, 300);
});

Promise.all([promise1, promise2, promise3]).then(values => {
  console.log(values);
});

// Ans ["111", "222", "333"]

Promise.race

應用時機:

剛好跟 Promise.all 相比,他只要任一項非同步有達到即可,會回傳最先到達的值

實作方法:

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("111");
  }, 300);
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("222");
  }, 100);
});

const promise3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("333");
  }, 300);
});

Promise.race([promise1, promise2, promise3]).then(values => {
  console.log(values);
});

// Ans "222",因為 promise2 它所等待的秒數最短,優先回傳

其他

  • promise 後面可以接 finally,亦即不管失敗或成功都會執行 finally 裡面的事情,這可以避免在 then 和 catch 裡面寫一樣的程式碼
promise1
  .then(values => {
    console.log(values);
  })
  .finally(item => {
    console.log("123");
  });
  • Promise.allSettled 它需要帶多個 promise 當參數,並回傳多個 promise 的值和狀態,跟 finally 一樣是不管成功或失敗都會回傳
Promise.allSettled([promise2, promise1, promise3]).then(results =>
  results.forEach(result => console.log(result))
);

/* 會回傳 
[object Object] {
  status: "fulfilled",
  value: "222"
}
[object Object] {
  status: "fulfilled",
  value: "111"
}
[object Object] {
  status: "fulfilled",
  value: "333"
} */

tags:async

date:2021/10/7