如果异步函数 (Promise) 花费太多时间则跳过等待

Skip waiting if async function (Promise) is taking too much time

在我的快递申请中,我正在调用 2 APIs。第二个 API 由第 3 方管理,有时可能需要 5 秒以上才能响应。因此,我只想等待 1 秒让 API 响应。如果没有,则继续处理第一个 API.

的数据

下面是被调用函数的模型。 如果 API 花费的时间超过 1 秒,我正在考虑使用 setTimeout 来抛出错误。如果 API 在 1 秒内响应,那么我只需取消 setTimeout 并且不会抛出任何错误。

但是这种方法有问题:

  1. setTimeout 错误无法使用 try...catch 块捕获。

我无法使用 axios 的超时选项,因为我仍然需要等待第 2 个 API 完成处理并将数据保存在数据库中。这当然可以稍后发生,当第二次 API 调用完成时。


// Function to simulate it's taking time.
async function cWait(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// Track whether it took time.
let isTimeOut = false

async function test() {
  console.log('starting')
  try {
    const one = await apiCall1()
    const myt = setTimeout(() => {
      console.log('Its taking time, skip the 2nd API Call')
      isTimeOut = true
      throw new Error('Its taking time')
    })
    const two = await apiCall2(myt)
  } catch (error) {
    console.log(error)
  }
  saveInDB({ ...one, ...two })
}


async function apiCall2(timeOutInstance) {
  console.log('start-apiCall')
  await cWait(1800)
  clearTimeout(timeOutInstance)
  if (isTimeOut) saveInDB()
  console.log('done-apiCall')
}

async function apiCall1() {
  await cWait(5)
}

async function saveInDB(data) {
  console.log('saveInDB')
}

test()

please note, this is not the answer as it was when it was accepted as I misread the question and failed to call saveInDB in a timed out situation

Promise.race 看起来很适合这份工作

此外,您实际上会使用 cWait 函数,不是为了模型,而是为了实际做一些有用的事情......赢得比赛:p

const api2delay = 800;
async function cWait(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
const TIMEDOUT = Symbol('TIMEDOUT');
async function cReject(ms) {
    return new Promise((_, reject) => setTimeout(reject, ms, TIMEDOUT));
}
function apiCall2timeout(timeoutCallback) {
    const resultApi2 = apiCall2();
    const timeout = cReject(1000);
    return Promise.race([resultApi2, timeout])
    .catch(e => {
        if (e === TIMEDOUT) {
            resultApi2.then(timeoutCallback);
        } else {
            throw e;
        }
    });
}
async function test() {
    console.log('starting')
    let one, two;
    try {
        one = await apiCall1();
        two = await apiCall2timeout(saveInDB);
    } catch (error) {
        console.log('error', error)
    }
    saveInDB({
        ...one,
        ...two
    })
}

async function apiCall2() {
    console.log('start-apiCall2')
    await cWait(api2delay)
    console.log('done-apiCall2')
    return {
        api2: 'done'
    }
}

async function apiCall1() {
    await cWait(5)
    return {
        api1: 'done'
    }
}

async function saveInDB(data) {
    console.log('saveInDB', data)
}

test()

注意:我更改了声明一和二的位置,因为 const 是块范围的

我 运行 和 apiCall2 中的 await cWait(800)saveInDB 将 运行 包含这两个数据。 但是如果你运行await cWait(1800)saveInDB就会运行2次。

// Function to simulate it's taking time.
async function cWait(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// https://italonascimento.github.io/applying-a-timeout-to-your-promises/
const promiseTimeout = function (ms, promise) {
  // Create a promise that rejects in <ms> milliseconds
  let timeout = new Promise((resolve, reject) => {
    let id = setTimeout(() => {
      clearTimeout(id);
      reject('Timed out in ' + ms + 'ms.')
    }, ms)
  })

  // Returns a race between our timeout and the passed in promise
  return Promise.race([
    promise,
    timeout
  ])
}

// Track whether it took time.
let isTimeOut = false

async function test() {
  console.log('starting')
  const one = await apiCall1() // get data from 1st API
  let two = {};
  try {
    two = await promiseTimeout(1000, apiCall2())
  } catch (error) {
    isTimeOut = true;
    console.log(error)
  }
  saveInDB({ ...one, ...two })
}


async function apiCall2() {
  console.log('start-apiCall')
  await cWait(800)
  console.log('done-apiCall', isTimeOut)
  if (isTimeOut) {
    saveInDB({ 2: 'two' })
  }
  return { 2: 'two' }
}

async function apiCall1() {
  await cWait(5)
  return { 1: 'one' }
}

async function saveInDB(data) {
  console.log('saveInDB', data)
}

test()