點燈坊

失くすものさえない今が強くなるチャンスよ

使用 Promise.race() 實現 Timeout 機制

Sam Xiao's Avatar 2020-04-06

由於 Promise 是 Asynchronous,因此回傳時間不確定,若想實作出超過指定時間就 Timeout,可使用 Promise.race() 實作。

Version

macOS Mojave 10.14.6
VS Code 1.38.1
Quokka 1.0.253
ECMAScript 2015

Promise.race()

import { resolveAfter, log } from 'wink-fp'

let data = [
  resolveAfter(1000)(1),
  resolveAfter(2000)(2),
  resolveAfter(3000)(3),
]

Promise.race(data)
  .then(log)
  .catch(log)

第 3 行

let data = [
  resolveAfter(1000)(1),
  resolveAfter(2000)(2),
  resolveAfter(3000)(3),
]

data 內有 3 個 promise,依次在 1 秒、2 秒、3 秒後產生。

第 9 行

Promise.race(data)
  .then(log)

使用 Promise.race() 從兩個 promise 中選擇 最先產生 的 promise。

無論是 fulfilled project 或 rejected promise,只要是最先 settled promise 即可

race000

Timeout Promise

import { resolveAfter, log, rejectAfter, error } from 'wink-fp'

let data = resolveAfter(1000)(1)
let timeout = rejectAfter(500)(new Error('Timeout after 500 ms'))

Promise.race([data, timeout])
  .then(log)
  .catch(error)

若想要實作非同步只等待一段時間,超過時間就 timeout 放棄,可另外建立一個 rejected promise 由 Promise.race() 決定。

race002

Timeout Promise

import { resolveAfter, log, error } from 'wink-fp'

let timeout = ms => ps => {
  let timeoutID;

  let timeoutPromise = new Promise((_, reject) => {
    timeoutID = setTimeout(() => {
      reject(new Error(`Timeout after ${ms} ms`))
    }, ms);
  });

  return Promise.race([ ps, timeoutPromise ])
                .finally(() => clearTimeout(timeoutID));
};

let promise0 = resolveAfter(1000)(1);

timeout(500)(promise0)
  .then(log)
  .catch(error);

亦可以相同原理實作出 timeout()

第 3 行

let timeout = ms => ps => {
  let timeoutID;

  let timeoutPromise = new Promise((_, reject) => {
    timeoutID = setTimeout(() => {
      reject(new Error(`Timeout after ${ms} ms`))
    }, ms);
  });

  return Promise.race([ ps, timeoutPromise ])
                .finally(() => clearTimeout(timeoutID));
};

timeout() 可建立一個 timeout promise,並與傳入的 promise 透過 Promise.race() 比較:

  • 若傳入 fulfilled promise 比 timeout promise 快,則回傳 fulfilled promise
  • 若傳入 fulfilled promise 比 timeut promise 快,則回傳 rejected promise

race001

Conclusion

  • Promise.race() 一般不常使用,但透過 Promise.race() 與自行建立的 timeout rejected promise 比較,可實作出若超過時間 timeout 就放棄 promise 的需求

Reference

Marius Schulz, Wait for the Fastest JavaScript Promise to Settle with Promise.race()
MDN, Promise.race()