82

As per Understanding the node.js event loop, node.js supports a single thread model. That means if I make multiple requests to a node.js server, it won't spawn a new thread for each request but will execute each request one by one. It means if I do the following for the first request in my node.js code, and meanwhile a new request comes in on node, the second request has to wait until the first request completes, including 5 second sleep time. Right?

var sleep = require('sleep');
    sleep.sleep(5)//sleep for 5 seconds

Is there a way that node.js can spawn a new thread for each request so that the second request does not have to wait for the first request to complete, or can I call sleep on specific thread only?

Brad Koch
  • 19,267
  • 19
  • 110
  • 137
emilly
  • 10,060
  • 33
  • 97
  • 172
  • 4
    you do not need to sleep like that. You should have a look at setTimeout instead -> http://nodejs.org/api/globals.html#globals_settimeout_cb_ms – Alfred Nov 19 '12 at 05:53

5 Answers5

175

If you are referring to the npm module sleep, it notes in the readme that sleep will block execution. So you are right - it isn't what you want. Instead you want to use setTimeout which is non-blocking. Here is an example:

setTimeout(function() {
  console.log('hello world!');
}, 5000);

For anyone looking to do this using es7 async/await, this example should help:

const snooze = ms => new Promise(resolve => setTimeout(resolve, ms));

const example = async () => {
  console.log('About to snooze without halting the event loop...');
  await snooze(1000);
  console.log('done!');
};

example();
David Weldon
  • 63,632
  • 11
  • 148
  • 146
  • so node.js will execute one request at a time which means it follows single threaded model. Right? – emilly Nov 19 '12 at 06:04
  • 7
    Yes, it uses a single thread of execution so it can only be computing one request at a time, but could have events pending on many requests. When you call `setTimeout`, it stores the event in the queue and can then begin executing the next request. After five seconds, the event will be removed from the queue and the callback will be executed (the function containing `console.log` in this case). – David Weldon Nov 19 '12 at 06:10
  • Take a look at [this screencast](http://nodecasts.net/episodes/1-introduction-to-nodejs) for a bit more details on how Node.js runs multiple requests without making them "wait" on each other. – Michelle Tilley Nov 19 '12 at 07:05
  • 7
    It does not block the thread. Code after this setTimeout will keep going, then console print 'hello world' after 5 seconds. – CodeFarmer Jul 31 '14 at 07:12
  • 1
    @DavidWeldon, How Node decides that setTimeout should be fired at some point of time, after removing it from the queue? In other words, what triggers the function to run after five seconds if it is not in execution mode/context? – securecurve Mar 25 '16 at 22:58
  • @securecurve - Node's internal workings, relying on some custom JS/C++, a library called libuv and libev etc. There's some blogs/articles that discuss the details of HOW Node makes it work. But above is the resulting behavior. – Don Cheadle Feb 06 '17 at 14:47
  • @DavidWeldon, So you mean the sleep is a fake sleep? Just a busy loop? – Pacerier Mar 21 '17 at 12:15
  • @Pacerier That package seems to have a low-level v8 binding to pause execution without a busy loop, but conceptually you can think of it that way. Either way, `sleep` is not something that should be in production code as it blocks concurrent execution while it runs. – David Weldon May 18 '17 at 21:12
1

In case you have a loop with an async request in each one and you want a certain time between each request you can use this code:

   var startTimeout = function(timeout, i){
        setTimeout(function() {
            myAsyncFunc(i).then(function(data){
                console.log(data);
            })
        }, timeout);
   }

   var myFunc = function(){
        timeout = 0;
        i = 0;
        while(i < 10){
            // By calling a function, the i-value is going to be 1.. 10 and not always 10
            startTimeout(timeout, i);
            // Increase timeout by 1 sec after each call
            timeout += 1000;
            i++;
        }
    }

This examples waits 1 second after each request before sending the next one.

Jodo
  • 4,515
  • 6
  • 38
  • 50
  • -1. This is **NOT** going to do what you expect. The loop does NOT run asynchronously. It will fire all requests immediately. Be very careful when thinking looping with async -- this combination is tricky. – treecoder May 18 '17 at 00:18
  • Of course the loop gets called all at once. But the timout increases each loop. So 1 sec... 2 sec ... 3 sec. That's why "myAsyncFunc" will be called each second rather than all at once. It works as expected when I teste it. Can you tell me what you think is wrong with it? – Jodo May 18 '17 at 06:22
  • 1
    The code DOES work, by the way, in a little tricky way. The _startTimeout_ function is called 10 times immediately, but each call has a timeout one second bigger, so every call to _myAsyncFunc_ is called one second later. – greuze May 18 '17 at 14:26
  • 1
    But that defeats the whole purpose of the loop. You should be able to decide how many times the loop runs. What you are doing above will execute the loop exactly 10 times -- no matter whether you needed 10 iterations or not. You can not decide whether you want another iteration or not at the end of the previous iteration -- because you don't know when will the end of previous iteration happen. Hence you have to decide in advance how many iterations will occur. The iteration breaking logic will then leak into the subject callback -- `myAsyncFunc` in your case. – treecoder May 18 '17 at 23:56
  • 1
    The second flaw is that you are **assuming** that your callback is going to take not more than 1 second to execute. What if your next iteration depended on your previous iteration's result, and that result was not guaranteed to be available within 1 second. Then your next iteration will start without the needed result -- because it will start exactly 1 second after the previous iteration, paying no attention to whether the callback invoked from the previous iteration has actually concluded or not. – treecoder May 19 '17 at 00:00
1

Please consider the deasync module, personally I don't like the Promise way to make all functions async, and keyword async/await anythere. And I think the official node.js should consider to expose the event loop API, this will solve the callback hell simply. Node.js is a framework not a language.

var node = require("deasync");
node.loop = node.runLoopOnce;

var done = 0;
// async call here
db.query("select * from ticket", (error, results, fields)=>{
    done = 1;
});

while (!done)
    node.loop();

// Now, here you go
Sunding Wei
  • 1,803
  • 17
  • 12
1

It is quite an old question, and though the accepted answer is still entirely correct, the timers/promises API added in v15 provides a simpler way.

import { setTimeout } from 'timers/promises';

// non blocking wait for 5 secs
await setTimeout(5 * 1000);
narendra-choudhary
  • 4,582
  • 4
  • 38
  • 58
0

When working with async functions or observables provided by 3rd party libraries, for example Cloud firestore, I've found functions the waitFor method shown below (TypeScript, but you get the idea...) to be helpful when you need to wait on some process to complete, but you don't want to have to embed callbacks within callbacks within callbacks nor risk an infinite loop.

This method is sort of similar to a while (!condition) sleep loop, but yields asynchronously and performs a test on the completion condition at regular intervals till true or timeout.

export const sleep = (ms: number) => {
    return new Promise(resolve => setTimeout(resolve, ms))
}
/**
 * Wait until the condition tested in a function returns true, or until 
 * a timeout is exceeded.
 * @param interval The frenequency with which the boolean function contained in condition is called.
 * @param timeout  The maximum time to allow for booleanFunction to return true
 * @param booleanFunction:  A completion function to evaluate after each interval. waitFor will return true as soon as the completion function returns true.   
 */
export const waitFor = async function (interval: number, timeout: number,
    booleanFunction: Function): Promise<boolean> {
    let elapsed = 1;
    if (booleanFunction()) return true;
    while (elapsed < timeout) {
        elapsed += interval;
        await sleep(interval);
        if (booleanFunction()) {
            return true;
        }
    }
    return false;
}

The say you have a long running process on your backend you want to complete before some other task is undertaken. For example if you have a function that totals a list of accounts, but you want to refresh the accounts from the backend before you calculate, you can do something like this:

async recalcAccountTotals() : number {
     this.accountService.refresh();   //start the async process.
     if (this.accounts.dirty) {
           let updateResult = await waitFor(100,2000,()=> {return !(this.accounts.dirty)})
     }
 if(!updateResult) { 
      console.error("Account refresh timed out, recalc aborted");
      return NaN;
    }
 return ... //calculate the account total. 
}
GGizmos
  • 3,443
  • 4
  • 27
  • 72