Delay function in a promise JavaScript code explanation

Viewed 62

I am not asking about how to write the delay function because that question has been answered, I just doesn't understand the code itself. I don't understand why we need to have a function returning another function? How do we get the data
I have annotated the code with my comments. If you run it in the console it should work. I am just looking for a newbie explanation for why do we need this currying syntax in here.

// a function
function delay(duration) {
  // why do we return here !! 
  // this args are the data.json() 
  // but how do we have access to it?? I don't see anywhere in the code that we are calling delay(data => data.json())
  // I know that .then calls the function for you with data.json() but that only if the function doesn't have a paramets for example doing just then(delay) but we are using a paramaeter here which is the 1000
  return function(...args){
    // so we return a new promise that resolves after sometime, this make sense.
    // but I don't understand all these returns.
    return new Promise(function(resolve, reject){
      setTimeout(function(){
        resolve(...args);
      }, duration)
    });
  };
}


const endpoint = 'https://pokeapi.co/api/v2/pokemon/ditto/'
const prom1 = fetch(endpoint)
              .then(data => data.json())
              .then(delay(2000))
              .then(console.log)


2 Answers

...why we need to have a function returning another function?

So that when you do

.then(delay(2000))

...it calls delay(2000), gets the function that will add that delay, and adds it to the promise chain. Later, when the chain settles, that function will get called with the argument then callbacks receive (the fulfillment value), which it receives as the only entry in its ...args rest parameter. It will then wait duration milliseconds before fulfilling its promise with that fulfillment value and allowing the chain to continue.

If delay returned its promise directly, that promise would go to then and the timeout would start prematurely (before the fulfillment got to that point in the chain). It would also "eat" the fulfillment value passing through the chain, since it wouldn't have that value to use when fulfilling its promise.

If you just had:

function delay(duration, ...args) {
  return new Promise(function(resolve, reject){
    setTimeout(function(){
      resolve(...args);
    }, duration)
  });
}

then you'd have to use it like this:

.then(delay.bind(null, 2000))

which is more awkward (and still creates and provides a function, since that's what bind does).


Side note: There's no reason for that delay implementation to use rest and spread. Only the first argument to resolve is used (any other ones are completely ignored), and then handlers only ever receive a single argument. So it could be:

function delay(duration) {
  return function(fulfillmentValue){
    return new Promise(function(resolve, reject){
      setTimeout(function(){
        resolve(fulfillmentValue);
      }, duration)
    });
  };
}

...although I'd probably use arrow functions for all three of functions delay creates.

You have to pass a function to then() (it will be called when everything that has to happen before then has happened).

So when you say then(delay(2000)) you have to ensure that delay(2000) returns a function.

Related