Timeout in async/await

41,315

Solution 1

You can use Promise.race to make a timeout:

Promise.race([
    doSomethingInSeries(),
    new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), 11.5e3))
]).catch(function(err) {
    // errors in res1, res2, res3 and the timeout will be caught here
})

You cannot use setTimeout without wrapping it in a promise.

Solution 2

Ok I found this way:

async function _doSomethingInSeries() {
    const res1 = await callApi();
    const res2 = await persistInDB(res1);
    const res3 = await doHeavyComputation(res1);
    return 'simle';
}

async function doSomethingInSeries(): Promise<any> {
  let timeoutId;

  const delay = new Promise(function(resolve, reject){
    timeoutId = setTimeout(function(){
      reject(new Error('timeout'));
    }, 1000);
  });

  // overall timeout
  return Promise.race([delay, _doSomethingInSeries()])
    .then( (res) => {
      clearTimeout(timeoutId);
      return res;
    });

}

Anyone errors?

The things that smells a bit to me is that using Promises as asynchronicity strategy will send us to allocate too many object that some other strategy needs but this is off-topic.

Share:
41,315
nkint
Author by

nkint

recursive: adjective, see recursive.

Updated on August 23, 2020

Comments

  • nkint
    nkint over 3 years

    I'm with Node.js and TypeScript and I'm using async/await. This is my test case:

    async function doSomethingInSeries() {
        const res1 = await callApi();
        const res2 = await persistInDB(res1);
        const res3 = await doHeavyComputation(res1);
        return 'simle';
    }
    

    I'd like to set a timeout for the overall function. I.e. if res1 takes 2 seconds, res2 takes 0.5 seconds, res3 takes 5 seconds I'd like to have a timeout that after 3 seconds let me throw an error.

    With a normal setTimeout call is a problem because the scope is lost:

    async function doSomethingInSeries() {
        const timerId = setTimeout(function() {
            throw new Error('timeout');
        });
    
        const res1 = await callApi();
        const res2 = await persistInDB(res1);
        const res3 = await doHeavyComputation(res1);
    
        clearTimeout(timerId);
    
        return 'simle';
    }
    

    And I cannot catch it with normal Promise.catch:

    doSomethingInSeries().catch(function(err) {
        // errors in res1, res2, res3 will be catched here
        // but the setTimeout thing is not!!
    });
    

    Any ideas on how to resolve?

    • Bergi
      Bergi almost 8 years
      Are you using a specific promise library?
    • nkint
      nkint almost 8 years
      No, just standard promise.
    • Bergi
      Bergi almost 8 years
      So 2 + 0.5 + 5 + 3 makes a timeout of 11.5 seconds?
  • Bergi
    Bergi almost 8 years
    @nkint: We don't need to clear it because the rejection is not considered when doSomethingInSeries() settles first. We could clear it, but that would be rather complicated (optimally race would have a way to cancel the slower promises, but that's not possible with native promises).
  • Bergi
    Bergi almost 8 years
    You're not clearing the timeout if _doSomethingInSeries() fails. You should use try { return await Promise.race(…); } finally { clearTimeout(timeoutId); }
  • nkint
    nkint almost 8 years
    Maybe I have problem in testing async stuff but seems to me that even if race rejects, doSomethingInSeries keep on going its own stuffs. How to be sure to stop everything inside doSomethingInSeries if the timeout rejects?
  • Bergi
    Bergi almost 8 years
    @nkint: You cannot stop a promise - it only represents a result (disclaimer: unless you use a promise implementation that supports cancellation). So if there is anything you need to do about the ongoing stuff, you need to do it manually (in the same way you did it with clearTimeout in your answer below).
  • nkint
    nkint almost 8 years
    it only represents a result - vary clear, thanks. It's a little bit messy. Some promise I'm await-ing in doSomethingInSeries is a call to request with a retry.. I need some other way to handle it, thank you anyway.
  • vitalets
    vitalets over 6 years
    To have promise-wraped version of setTimeout / clearTimeout you may have a look on await-timeout