681

I am trying to use the new async features and I hope solving my problem will help others in the future. This is my code which is working:

  async function asyncGenerator() {
    // other code
    while (goOn) {
      // other code
      var fileList = await listFiles(nextPageToken);
      var parents = await requestParents(fileList);
      // other code
    }
    // other code
  }

  function listFiles(token) {
    return gapi.client.drive.files.list({
      'maxResults': sizeResults,
      'pageToken': token,
      'q': query
    });
  }

The problem is, that my while loop runs too fast and the script sends too many requests per second to the google API. Therefore I would like to build a sleep function which delays the request. Thus I could also use this function to delay other requests. If there is another way to delay the request, please let me know.

Anyway, this is my new code which does not work. The response of the request is returned to the anonymous async function within the setTimeout, but I just do not know how I can return the response to the sleep function resp. to the initial asyncGenerator function.

  async function asyncGenerator() {
    // other code
    while (goOn) {
      // other code
      var fileList = await sleep(listFiles, nextPageToken);
      var parents = await requestParents(fileList);
      // other code
    }
    // other code
  }

  function listFiles(token) {
    return gapi.client.drive.files.list({
      'maxResults': sizeResults,
      'pageToken': token,
      'q': query
    });
  }

  async function sleep(fn, par) {
    return await setTimeout(async function() {
      await fn(par);
    }, 3000, fn, par);
  }

I have already tried some options: storing the response in a global variable and return it from the sleep function, callback within the anonymous function, etc.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
JShinigami
  • 7,317
  • 3
  • 14
  • 22

19 Answers19

1145

Your sleep function does not work because setTimeout does not (yet?) return a promise that could be awaited. You will need to promisify it manually:

function timeout(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
async function sleep(fn, ...args) {
    await timeout(3000);
    return fn(...args);
}

Btw, to slow down your loop you probably don't want to use a sleep function that takes a callback and defers it like this. I recommend:

while (goOn) {
  // other code
  var [parents] = await Promise.all([
      listFiles(nextPageToken).then(requestParents),
      timeout(5000)
  ]);
  // other code
}

which lets the computation of parents take at least 5 seconds.

Rob Bednark
  • 25,981
  • 23
  • 80
  • 125
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • 6
    what does the notation of `var [parents]` represent? I haven't seen it before and it's a difficult thing to google – natedog Aug 16 '17 at 02:05
  • 10
    @NateUsher It's [array destructuring](https://stackoverflow.com/q/3986348/1048572) – Bergi Aug 16 '17 at 02:06
  • 6
    @tinkerr ["*timeout needs to be declared async if it needs to be awaited*"](https://stackoverflow.com/review/suggested-edits/18044022) - Nope. A function only needs to return a promise that can be awaited (or actually, a thenable is enough). How it achieves that is up to the implementation of the function, it does not need to be an `async function`. – Bergi Nov 23 '17 at 17:09
  • I thought async/await was replacing all uses of Promises? Is there a way to use async/await and setTimeout without Promises? – naisanza Mar 13 '18 at 07:22
  • 3
    @naisanza No, `async`/`await` is *based on* promises. The only thing it replaces are `then` calls. – Bergi Mar 13 '18 at 14:05
  • function timeout(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } is not exactly the same as setTimeout behaviour on it own if you wait "ms" time after you call the function and before you use the promise value (for example register with a then, you might get the result immediately which is something one might not expect ... – benchuk Aug 04 '19 at 13:00
  • @benchuk Not sure what you mean by "not exactly the same", or how that would matter. And no, when you register a `then` callback, you never get the result immediately, it always is asynchronous. – Bergi Aug 04 '19 at 13:06
  • you can await setTimeout in Node.js 15, update your answer – Abhishek Choudhary Oct 26 '20 at 18:36
  • In ` return new Promise(resolve => setTimeout(resolve, ms));` -- what is the resolve argument? – fotoflo Nov 26 '20 at 05:15
  • 2
    @fotoflo https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise. It's the function given to you by the promise constructor that lets you resolve the promise by calling it – Bergi Nov 26 '20 at 15:29
  • @naisanza In fact, `async/await` is just a syntactic sugar for promises. This makes me think that we should know how promises work under the hood. – Eldar Scott Mar 21 '21 at 10:43
  • Promise.all is evaluated in parallel, not sequentially. So this will kind of break if you try to run it in a loop – AAverin Nov 11 '21 at 14:56
  • @AAverin The loop is meant to be sequential. All the `Promise.all` does is to ensure each iteration takes at least 5s. – Bergi Nov 11 '21 at 19:05
  • @natedog here is a direct Mozilla link about `var [parents]` [Destructuring Assignment](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) – Stas Sorokin Dec 27 '21 at 08:56
  • `setTimeout` returns an ID that is meant to be used with `clearTimeout` in order to stop the timer. It's not meant to return a `Promise`. – Someone Mar 22 '23 at 23:15
  • @Someone If the API was designed today, the function would accept a millisecond count and an optional [abort signal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal), and return a promise. See for example [`setTimeout` from *timers/promises* of nodejs](https://nodejs.org/api/timers.html#timerspromisessettimeoutdelay-value-options). – Bergi Mar 22 '23 at 23:25
556

The quick one-liner, inline way

 await new Promise(resolve => setTimeout(resolve, 1000));
FlavorScape
  • 13,301
  • 12
  • 75
  • 117
  • 13
    what does it mean when you guys use "resolve" x 2 times in the same line? Like: await new Promise(resolve => setTimeout(resolve, 1000)); does it ref. to itself or what? I would do something like this instead: function myFunc(){}; await new Promise(resolve => setTimeout(myFunc, 1000)); – PabloDK Apr 29 '20 at 16:30
  • 1
    @PabloDK that would block forever because the promise never resolves. – FlavorScape Sep 10 '20 at 17:22
  • 12
    @PabloDK You can expand the one-liner above to [this (more verbose) version](https://gist.github.com/mrienstra/8aa4eeeeab2012d2aa8ffc7f5e45f280), which hopefully makes it obvious why `resolve` appears twice. If it's still confusing, take a look at the [MDN docs for Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). – mrienstra Apr 30 '21 at 23:03
  • 5
    @PabloDK It could also be represented like this: `await new Promise((resolve, reject) => setTimeout(resolve, 1000));`. So, `resolve` and `reject` are callbacks exposed when you create a Promise. You're simply telling `setTimeout` to execute `resolve()`. – crimson_king Sep 06 '21 at 04:45
  • this fail with eslint 8, no-promise-executor-return – Joe Apr 12 '22 at 06:44
  • you deserve a Nobel Prize – jimmystackoverflowjiim May 14 '22 at 13:29
  • So I didn't quite understand how to use this one liner at first, but if I'm not mistaken, it basically allows me to add a timeout on a single line BEFORE a for loop so I don't need to wrap anything? It appeared to work that way, I just want to make sure it won't fail on me later. I basically needed a 10 second delay before a for loop started and so I dropped this above my for loop and changed the 1000 to 10000 and it seemed to work perfectly. Anything I'm missing? – garek007 Feb 22 '23 at 18:44
  • @Joe, it's due to the arrow function which "implicitly returns". So, it should not result in the ESLint error with the following: `await new Promise(resolve => { setTimeout(resolve, 1000); });`. On the subject: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#function_body – Artfaith Jul 15 '23 at 22:07
251

Since Node 7.6, you can combine the functions promisify function from the utils module with setTimeout() .

Node.js

const sleep = require('util').promisify(setTimeout)

Javascript

const sleep = m => new Promise(r => setTimeout(r, m))

Usage

(async () => {
    console.time("Slept for")
    await sleep(3000)
    console.timeEnd("Slept for")
})()
Harry
  • 2,965
  • 1
  • 13
  • 19
  • 2
    In nodeJS `await require('util').promisify(setTimeout)(3000)` can also be achieved without require by: `await setTimeout[Object.getOwnPropertySymbols(setTimeout)[0]](3000)` – Shl Aug 24 '18 at 13:16
  • 12
    Interesting @Shl. I think it is less readable than my solution though. If people disagree I can add it to the solution? – Harry Aug 24 '18 at 13:19
  • 1
    Nice, love the conciseness of the new native `util` promisify. – LeOn - Han Li Oct 23 '18 at 19:12
  • 2
    The require version is clearly much better than the `getOwnPropertySymbols` version... if it ain't broke...! – Matt Fletcher Jan 25 '19 at 15:20
  • 2
    Hey there @Harry. It appears you incorporated the one liner from FlavorScape's answer in your own answer. I don't want to presume of your intentions, but that isn't really fair to them. Could you rollback your edit? Right now it looks a bit like plagiarism.. – Félix Adriyel Gagnon-Grenier Jun 18 '19 at 22:50
  • 2
    @FélixGagnon-Grenier why should he roll back, let him keep his plagiarism. Good ideas should be copied. – gregn3 Aug 30 '19 at 20:58
  • @gregn3 Not on Stack Overflow. – Félix Adriyel Gagnon-Grenier Aug 30 '19 at 22:42
  • @FélixGagnon-Grenier He can keep it on my Stack Overflow. – gregn3 Sep 01 '19 at 22:17
  • @FélixGagnon-Grenier he should be called out for plagiarism, but let him keep it. Maybe he can make a better answer with it than FlavorScape , maybe not. Let the votes decide. – gregn3 Sep 01 '19 at 22:25
  • 5
    I've removed the one-liner as the answer is right below, however I have seen many popular answers update their answers to include other new answers as most readers don't bother looking past the first few responses. – Harry Sep 03 '19 at 08:43
  • You should remember that async IIFE will wait only for functions inside it: same-level code will not be paused: ~~~ console.log('pre'); (async () => { console.log('start') console.time("Slept for") await sleep(3000) console.timeEnd("Slept for") console.log('end') })() console.log('post') ~~~ will print: `pre,start,post,end` – countTheRow Apr 25 '20 at 01:19
  • Also to be fair, I think FlavourScape probably adapted my answer to create his anyway :) – Harry Jan 27 '21 at 11:17
60

Timers Promises API

await setTimeout finally arrived with Node.js 16, removing the need to use util.promisify():

import { setTimeout } from 'timers/promises';

(async () => {
  const result = await setTimeout(2000, 'resolved')
  // Executed after 2 seconds
  console.log(result); // "resolved"
})()

Official Node.js docs: Timers Promises API (library already built in Node)

t_dom93
  • 10,226
  • 1
  • 52
  • 38
54

setTimeout is not an async function, so you can't use it with ES7 async-await. But you could implement your sleep function using ES6 Promise:

function sleep (fn, par) {
  return new Promise((resolve) => {
    // wait 3s before calling fn(par)
    setTimeout(() => resolve(fn(par)), 3000)
  })
}

Then you'll be able to use this new sleep function with ES7 async-await:

var fileList = await sleep(listFiles, nextPageToken)

Please, note that I'm only answering your question about combining ES7 async/await with setTimeout, though it may not help solve your problem with sending too many requests per second.


Update: Modern node.js versions has a buid-in async timeout implementation, accessible via util.promisify helper:

const {promisify} = require('util');
const setTimeoutAsync = promisify(setTimeout);
Leonid Beschastny
  • 50,364
  • 10
  • 118
  • 122
  • 2
    You shouldn't do that, when `fn` throws the errror would not be caught. – Bergi Oct 23 '15 at 00:19
  • @Bergi I think it bubbles up to the `new Promise` where you can `sleep.catch` it. – Florian Wendelborn Mar 04 '17 at 23:29
  • 4
    @Dodekeract No, it's in an asynchronous `setTimeout` callback and the `new Promise` callback has been done for long. It will bubble to the global context and be thrown as an unhandled exception. – Bergi Mar 05 '17 at 11:22
  • 1
    > problem with sending too many requests per second. You want to use "debounce" perhaps to prevent things like UI firing too many ruquests. – FlavorScape Aug 05 '19 at 18:17
  • settimeout is async, read here:https://developer.mozilla.org/en-US/docs/Web/API/setTimeout – OriEng Feb 26 '22 at 11:50
17

If you would like to use the same kind of syntax as setTimeout you can write a helper function like this:

const setAsyncTimeout = (cb, timeout = 0) => new Promise(resolve => {
    setTimeout(() => {
        cb();
        resolve();
    }, timeout);
});

You can then call it like so:

const doStuffAsync = async () => {
    await setAsyncTimeout(() => {
        // Do stuff
    }, 1000);

    await setAsyncTimeout(() => {
        // Do more stuff
    }, 500);

    await setAsyncTimeout(() => {
        // Do even more stuff
    }, 2000);
};

doStuffAsync();

I made a gist: https://gist.github.com/DaveBitter/f44889a2a52ad16b6a5129c39444bb57

Dave Bitter
  • 171
  • 1
  • 3
  • 1
    a function name like `delayRun` would make more sense here, since it will delay the running of the callback function by X seconds. Not a very await-ey example, IMO. – mix3d Apr 18 '19 at 20:53
14

I leave this code snippet here for someone who wants to fetch API call (e.g. get clients) with setTimeout:

const { data } = await new Promise(resolve => setTimeout(resolve, 250)).then(() => getClientsService())
setName(data.name || '')
setEmail(data.email || '')
Erik P_yan
  • 608
  • 5
  • 6
10
await new Promise(resolve => setTimeout(() => { resolve({ data: 'your return data'}) }, 1000))
General Grievance
  • 4,555
  • 31
  • 31
  • 45
8
var testAwait = function () {
    var promise = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('Inside test await');
        }, 1000);
    });
    return promise;
}

var asyncFunction = async function() {
    await testAwait().then((data) => {
        console.log(data);
    })
    return 'hello asyncFunction';
}

asyncFunction().then((data) => {
    console.log(data);
});

//Inside test await
//hello asyncFunction
vignesh
  • 101
  • 1
  • 3
5

This is my version with nodejs now in 2020 in AWS labdas

const sleep = require('util').promisify(setTimeout)

async function f1 (some){
...
}

async function f2 (thing){
...
}

module.exports.someFunction = async event => {
    ...
    await f1(some)
    await sleep(5000)
    await f2(thing)
    ...
}
IgorAlves
  • 5,086
  • 10
  • 52
  • 83
  • What is `promisify` doing to `setTimeout` for your custom `sleep` function that causes it to no longer need a function as the first argument? For example, if you run `setTimeout(5000);` (not having a function as the first argument) you get `Uncaught TypeError [ERR_INVALID_CALLBACK]: Callback must be a function. Received 5000`. – Lonnie Best Jul 25 '20 at 06:15
4
await setTimeout(()=>{}, 200);

Will work if your Node version is 15 and above.

D. Schreier
  • 1,700
  • 1
  • 22
  • 34
Leask
  • 51
  • 1
2

Made a util inspired from Dave's answer

Basically passed in a done callback to call when the operation is finished.

// Function to timeout if a request is taking too long
const setAsyncTimeout = (cb, timeout = 0) => new Promise((resolve, reject) => {
  cb(resolve);
  setTimeout(() => reject('Request is taking too long to response'), timeout);
});

This is how I use it:

try {
  await setAsyncTimeout(async done => {
    const requestOne = await someService.post(configs);
    const requestTwo = await someService.get(configs);
    const requestThree = await someService.post(configs);
    done();
  }, 5000); // 5 seconds max for this set of operations
}
catch (err) {
  console.error('[Timeout] Unable to complete the operation.', err);
}
Jee Mok
  • 6,157
  • 8
  • 47
  • 80
2

With the marked answer I had a lint error [no-promise-executor-return] so I found here the corrected version, using curly brackets in order to make explicit the intention of not returning anything:

const timeout = (ms) =>
  new Promise(resolve => {
    setTimeout(resolve, ms)
  })
Kbre
  • 41
  • 5
1

The following code works in Chrome and Firefox and maybe other browsers.

function timeout(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
async function sleep(fn, ...args) {
    await timeout(3000);
    return fn(...args);
}

But in Internet Explorer I get a Syntax Error for the "(resolve **=>** setTimeout..."

Kuya
  • 7,280
  • 4
  • 19
  • 31
Shadowned
  • 21
  • 4
1

I would like to point out a robust extension to Promise.all. A rather elegant solution that works with one promise to be time-limited only is to race the promise with a timeout (such as new Promise((resolve) => setTimeout(resolve, timeout))).

await new Promise.race([myPromise, timeoutPromise])

will continue as soon as one of the promises finished. myPromise then can internally await a different timeout, or simply make use of Promise.all

const timeout = ms => new Promise((resolve) => setTimeout(resolve, ms));
await Promise.race([
    Promise.all([myPromise, timeout(500)]),
    timeout(5000)
]);

The result is an asynchronous call that does not run more often than twice a second, with a timeout of 5 seconds in case of some (network/server?) error.

Moreover, you can make this very versatile and customizable function as such:

function callWithTimeout(promise, msTimeout=5000, throws=false) {
    const timeout = ms => new Promise((resolve, reject) =>
        setTimeout(throws ? reject : resolve, ms));
    await Promise.race([
        //depends whether you want to wait there or just pass the promise itself
        Promise.all([promise, timeout(500)]), 
        timeout(msTimeout)
    ]);
}
    

Which ultimately lets you customize the timeout time and the whether the promise should succeed or throw on timeout. Having such robust general implementation can save you a lot of pain in the future. You can also set a string instead of boolean as throws and bind this variable to the reject for custom error message: reject.bind(undefined, throws)

Note that you should not pass your promise with await:

const myPromise = async x => x;
//will never time out and not because myPromise will finish immediatelly
callWithTimeout(await myPromise(), 2000, true); 
//will possibly timeout after 2 sec with an exception, or finish 
callWithTimeout(myPromise(), 2000, true); 
Jiří
  • 415
  • 6
  • 16
0

How to Log all the responses at once?

async function sayHello(name) {
  let greet = `Hey! ${name} very nice to meet you bud.`;
  setTimeout(() => {
    return {
      greet,
      createdAt: new Date(),
    };
  }, 1000);
}

const response1 = async () => await sayHello("sounish");
const response2 = async () => await sayHello("alex");
const response3 = async () => await sayHello("bill");

async function getData() {
  const data1 = await sayHello("sounish");
  const data2 = await sayHello("alex");
  const data3 = await sayHello("bill");
  return { data1, data2, data3 };
}

Promise.all([sayHello("sounish"), sayHello("alex"), sayHello("bill")]).then(
  (allResponses) => {
    console.log({ allResponses });
  }
);

getData().then((allData) => {
  console.log({ allData });
});
sounish nath
  • 567
  • 4
  • 3
0

I wanted to solve what I think is a related problem: the ability to auto-refresh a page every 30 seconds, but also live update some timers on-screen once a second and I wanted to make sure these 2 things didn't conflict. My google searches led me to your question here, so I thought I would share what I came up with in case it helps others:

class Elapsed
{
    constructor()
    {
        this.start = performance.now()
    }
    elapsed_seconds( seconds )
    {
        let elapsed_seconds = ( performance.now() - this.start ) * 0.001
        return elapsed_seconds >= seconds
    }
}

async function sleep( seconds )
{
    let promise = new Promise(( resolve, _ ) => {
        setTimeout( () => resolve(), seconds * 1000 )
    })
    await promise
}

async function refresh()
{
    // do something
    return new Elapsed()
}

async function auto_refresh()
{
    while( true )
    {
        let last_refresh = await refresh()
        while( !last_refresh.elapsed_seconds( 30 ))
        {
            await update_counters()
            await sleep( 1 )
        }
    }
}
royce3
  • 1,203
  • 1
  • 10
  • 17
0
setTimeout(async () => {
   var x = await asyncFunction(); 
}, 5000)
brk.gs
  • 11
  • 1
  • 2
    Answer needs supporting information Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](https://stackoverflow.com/help/how-to-answer). – moken Aug 01 '23 at 11:50
-7

This is a quicker fix in one-liner.

Hope this will help.

// WAIT FOR 200 MILISECONDS TO GET DATA //
await setTimeout(()=>{}, 200);
  • 2
    Doesn't work. This: `await setTimeout(()=>{console.log('first')}, 200); console.log ('second')` prints **second** then **first** – gregn3 Aug 30 '19 at 20:52
  • 1
    @gregn3 that is the point yes. This is a non blocking solution where code outside the function can continue to execute while a "blocking operation" is completed outside the main program flow. Although the syntax you and Rommy and Mohamad have provided isn't strictly correct due to the requirement for an await to be rapped in an async function (might be a fairly recent addition), also I'm using node.js. This is my tweaked solution. ```var test = async () => { await setTimeout(()=>{console.log('first')}, 1000); console.log ('second') }``` I've extended the timeout to show it's usefulness. – azariah Nov 09 '19 at 05:57