Search code examples
javascriptasynchronouspromisees6-promise

Passing a function to Promise.then results in a different behaviour than passing a nested function


The following code

function doSomething(msg){ 
    return new Promise((resolve, reject) => {
      setTimeout(
        () => {
          console.log(msg);
          resolve();
        }, 
        2000);
    }) 
}

let start = Date.now();  
let end;  
doSomething("1st Call")
  .then(()=>doSomething("2nd Call"))
  .then(()=>doSomething("3rd Call"))
  .then(()=>{
    end = Date.now();
    console.log('start',start);
    console.log('end',end);
    console.log('elapsed time',end-start);
  }) 

prints 1st Call, 2nd and 3rd, with 2 seconds between each console.log statement, as expected

enter image description here

However, if I remove the arrow functions from the then blocks, the behaviour is totally different, i.e

doSomething("1st Call")
  .then(doSomething("2nd Call"))
  .then(doSomething("3rd Call"))
  .then(()=>{
    end = Date.now();
    console.log('start',start);
    console.log('end',end);
    console.log('elapsed time',end-start);
  }) 

With this code, all the console.log statements get printed at the same time, and the elapsed time is just 2 seconds, as opposed to 2 seconds per function (6 seconds total as in the first example)

enter image description here

In other words, for the code to work correctly, the then function needs to take a function (the arrow one in this case), and from inside that function, then I can do further function calls.

Why can't I just pass the function directly, why does it need to be nested in another function?


Solution

  • .then() expects a callback function which will be called when the Promise on which .then() method is called - fulfills.

    When you do this

    .then(doSomething("2nd Call"))
    

    instead of registering a callback function which will be invoked at some later time, doSomething() is called immediately.

    You could pass a reference to a function to the .then() method and .then() will call that function when Promise fulfills.

    .then(doSomething)
    

    but you won't be able to pass any argument to doSomething function.

    .then(doSomething("2nd Call")) will only work if doSomething returns a function. In that case, returned function will be registered as a callback and will be called when Promise fulfills.

    You could use .bind() to get a function which will be used as a callback to .then() method

    function doSomething(msg){ 
        return new Promise((resolve, reject) => {
          setTimeout(() => {
              console.log(msg);
              resolve();
            }, 2000);
        }); 
    }
    
    let start = Date.now();  
    let end;  
    
    doSomething("1st Call")
      .then(doSomething.bind(null, '2nd Call'))
      .then(doSomething.bind(null, '3rd Call'))
      .then(()=>{
        end = Date.now();
        console.log('start',start);
        console.log('end',end);
        console.log('elapsed time',end-start);
      })