简体   繁体   中英

Understanding setTimeout and its implementation in JavaScript Promises

I have the following code:

function MyPromise(configFunction) {
  let nextSuccessCallBack = undefined;
  let nextResolve = undefined;
  configFunction(function(message){
    setTimeout(function(){
      if(nextSuccessCallBack) {
        var result = nextSuccessCallBack(message);
        if(result && result.then) {
          result.then(nextResolve);
        } else {
          nextResolve && nextResolve(result);
        }
      }
    })
  });

  return {
    then: function(successCallback) {
      nextSuccessCallBack = successCallback;
      return new MyPromise(function(resolve) {
        nextResolve = resolve;
      })
    }
  }
}

new MyPromise(function(resolve, reject) {
  resolve('new message');
}).then(function(message) {
  console.log(message);
  return 'another message'
}).then(function(message) {
  console.log(message)
  console.log('here')
})

In this example, it seems that the nextSuccessCallBack is set to be the callback functions within .then , then its value within the setTimeout function is now populated. However, this confuses me. I thought that when we reach the return statement in the constructor, we return the object and effectively stop the function? If that the case then how does the program even get to the setTimeout ?

This is not a correct Promise implementation. It clearly has no capabilities for rejections, but also for the implemented fulfilment feature it does not comply with the Promises/A+ specification . Just to give two examples:

  1. It does not comply with rule 2.1.2.2

    When fulfilled, a promise must have a value, which must not change.

    ....nor with rule 2.2.2.3:

    If onFulfilled is a function it must not be called more than once.

    In your implementation, if you would add a second call to resolve :

     new MyPromise(function(resolve, reject) { resolve('new message'); resolve('new message2'); }).then((function(message) { console.log(message); //... etc })

    ...then both calls to resolve would fire the then callback, and show that the promised value was modified after the first time it was set. This is in complete violation of the principle of promises: promises can only resolve once.

  2. It does not comply with rule 2.2.6:

    then may be called multiple times on the same promise.

    • If/when promise is fulfilled, all respective onFulfilled callbacks must execute in the order of their originating calls to then .

    This does not happen if we use the following code:

     let p = new MyPromise(resolve => resolve("value")); p.then(console.log); // This callback is called p.then(console.log); // This callback is not called -> violation!

These are basic shortcomings, and this is just the tip of the iceberg.

If you are interested in how it could be implemented in compliance with Promise/A+, then have a look at one I did a few years ago with a step-by-step explanation.

As to your question

how does the program even get to the setTimeout ?

When your main code executes this:

new MyPromise(function(resolve, reject) {
  resolve('new message');
})

...then the parameter variable configFunction is initialised with that callback function. The MyPromise constructor calls it, passing it the following callback as first argument:

  function(message){
    setTimeout(function(){
      if(nextSuccessCallBack) {
        var result = nextSuccessCallBack(message);
        if(result && result.then) {
          result.then(nextResolve);
        } else {
          nextResolve && nextResolve(result);
        }
      }
    })
  }

So that means that resolve in your main code's callback function is initialised with the above callback function. Then your main code's callback function calls resolve with a string argument. Since resolve is the above callback function, we can see it gets executed with message initialised to "new message". This function executes setTimeout .

The code in the snippet does solve the purpose but, it is confusing as is a bad implementation of a Promise according to the way MDN Uses Promises in JavaScript for better code efficiency and understanding.

The above code can also be written this way:

const examplePromise = new Promise((resolve, reject) => {
    resolve("Another Message");
});

console.log("New Message");
examplePromise.then((message) => {
    console.log(message);
});

Now, for better understading of the concept of Promises in JavaScript, I'll take another example:

Let's say I want to create a program which says hello and then after 2 seconds says How Are You?

There are two ways of solving this

  • Just using the setTimeout() function which executes a function after certain period of time
  • Using promises with setTimeout()

Case I (Using setTimeout() ):

console.log("hello"); //Saying hello for the first time

setTimeout(function () {
    console.log("How Are You?");
}, 2000); //2000 milliseconds is 2 seconds

Case II (Using Promises )

console.log("hello");

const example = new Promise((resolve) => {
    setTimeout(() => {
        resolve("How Are You?");
    }, 2000);
});

example.then((message) => {
    console.log(message);
});

Now, for simple cases like logging a message after 2 seconds do not require Promises . You may use them (No problem) but actually, Promises are used in cases where, you wait for a function to execute a Database Query or you need to wait for a server to give you some HTML Response or while using WebAPIs, etc.

PS: setTimeout() is just an example. It is not a rule that you always have to use setTimeout() while writing a Promise . If you are fetching data using a WebAPI, you usually write $.ajax(...).then(...) or fetch(...).then(...) instead of setTimeout()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM