1

I think I have a misunderstanding of what setTimeout does exactly in JavaScript. I have this script:

function recursiveFibonacci(n) {
    if ( n === 0 ) {
        return 1;
    } else if ( n === 1 ) {
        return 1;
    } else {
        return recursiveFibonacci(n-1) + recursiveFibonacci(n-2);
    }
}

setTimeout( () => {console.log("one second");}, 1000);

console.log(recursiveFibonacci(42));

What I would expect to happen is that recursiveFibonacci starts chugging away on the 43rd value in the Fibonacci sequence. This takes about 4 seconds on my computer. So, after 1 second of work the evaluation would be interrupted and the console would log:

one second

and then about 3 seconds later log:

433494437

Instead what happens is that, after 4 seconds, the console logs:

433494437
one second

all at once. Why is this the case? How can I get setTimeout to work? Is it the case that the JavaScript interpreter is not actually interrupted by setTimeout but rather if it finishes its other jobs then it will wait until the given amount of time has passed before calling the given function?

Edit:

I found this tool very useful for understanding the relevant concepts:

Loupe

Sean Letendre
  • 2,623
  • 3
  • 14
  • 32
  • 1
    `setTimeout` is a function that will execute a command in a given amount of time, in parallel (almost) with other running code. You seem to be more interested in a delay, like the `$.delay()` function. And yes, the interpreter is not interrupted. – Mikael Jul 08 '17 at 05:07
  • @Mikael, `$` is not defined in node. What do I import to use it? – Sean Letendre Jul 08 '17 at 05:13
  • jQuery. `$` in JavaScript context usually means jQuery, sorry for not making it more clear. Good luck. – Mikael Jul 08 '17 at 05:14
  • Ok, I did `sudo npm install jquery` and then, in the REPL, I did `var $ = require('jquery');` and that worked, so then I typed in `$.delay(()=>{console.log("hi");},1000);` and that caused the error `TypeError: $.delay is not a function`. In fact, `> $` in the REPL yields `[Function]`. – Sean Letendre Jul 08 '17 at 05:26
  • Sorry, the delay is only useful in jQuery effects. But I searched around, and this link seems to explain everything you need to know about `setTimeout`: https://javascript.info/settimeout-setinterval – Mikael Jul 08 '17 at 05:30
  • 1
    @SeanLetendre - Mikael has sent you on a wild goose chase. `$.delay()` will not help you here at all and you should not be trying to use jQuery on the server for timers. This is a core issue with how nodejs works. Please see my answer below for detailed explanation. – jfriend00 Jul 08 '17 at 05:35
  • Totally agreed, never use jQuery server-side (and client-side too...) – Lazyexpert Jul 08 '17 at 05:41

2 Answers2

6

Javascript in node.js is event driven and single threaded. Since your reverseFibonacci() function is synchronous, it does not allow anything else to run until it is done.

So, even though the timer fires and the timer callback is put into the internal nodejs event queue, the JS interpreter can't get to the next event in the event queue until your reverseFibonacci() function is done.

setTimeout() does not interrupt the currently running Javascript. Instead, it puts an event in the event queue and when the currently running Javascript is done, the JS interpreter will then pull the next event out of the event queue and run it.

So, the order of events in your scenario is this:

  1. Schedule a timer event for 1 second from now.
  2. Start running reverseFibonacci()
  3. Timer fires (internally in nodejs) 1 second later and inserts an event into the nodejs event queue.
  4. 4+ seconds later your reverseFibonacci(42) finishes running.
  5. Since the currently running JS has finished, the JS interpreter pulls the next event out of the event queue and processes it, resulting in your timer callback finally getting called.

Timers schedule with setTimeout() runs as soon as possible after their scheduled time, but if the Javascript interpreter is busy running other code, they don't interrupt that code. The timer system puts an event into the event queue and, when that other code is done, then the Javascript interpreter will pull the next event out of the event queue and run it (your timer callback will get called).

jfriend00
  • 683,504
  • 96
  • 985
  • 979
1

It's because your function does synchronous job.

The nature of the node event loop is asynchronous. As far as you function finishes execution or you will execute something asynchronous - nextTick will be called. And most probably the first item will be you setTimout function.

Here's example with intended asynchronous chunk, and it works as you expect:

function sleep(ms) {
  return new Promise( (resolve, reject) => {
    setTimeout(resolve, ms);
  });
}

async function recursiveFibonacci(n) {
  await sleep(10);
  if ( n === 0 ) {
      return 1;
  } else if ( n === 1 ) {
      return 1;
  } else {
      return await recursiveFibonacci(n-1) + await recursiveFibonacci(n-2);
  }
}

setTimeout( () => {console.log("one second");}, 1000);

recursiveFibonacci(25).then(console.log);
Lazyexpert
  • 3,106
  • 1
  • 19
  • 33