Why are javascript promises asynchronous when calling only synchronous functions?

前端 未结 2 697
攒了一身酷
攒了一身酷 2020-12-01 09:44

In testing I\'ve found that JavaScript Promises are always asynchronous regardless of whether or not they contain any asynchronous functions in their chain.

相关标签:
2条回答
  • 2020-12-01 10:38

    Your code is fine is you want your promises to run independently and let them execute in their own way, no matter each one complete first. As soon as your code is async, you cannot predict which one will be completed first (due to the async nature of the event loop).

    However if you want to catch all your promises after they all completed, you should use Promise.all (which is the equivalent of $.when is jQuery). See:

    • https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
    • https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop
    0 讨论(0)
  • 2020-12-01 10:44

    The callback passed to a Promise constructor is always called synchronously, but the callbacks passed into then are always called asynchronously (you could use setTimeout with a delay of 0 in a userland implementation to achieve that).

    Simplifying your example (and giving the anonymous function's names so I can refer to them) to:

    Promise.resolve().then(function callbackA () {
      console.log("finish run 1");
    }).then(function callbackB () {
      console.log("surprisingly this happens after run 2 finishes");
    });
    
    Promise.resolve().then(function callbackC () {
      console.log("finish run 2");
    })
    

    Still gives the output in the same order:

    finish run 1
    finish run 2
    surprisingly this happens after run 2 finishes
    

    Events happen in this order:

    1. The first promise is resolved (synchronously)
    2. callbackA is added to the event loop's queue
    3. The second promise is resolved
    4. callbackC is added to the event loop's queue
    5. There is nothing left to do so the event loop is accessed, callbackA is first in the queue so it is executed, it doesn't return a promise so the intermediate promise for callbackB is immediately resolved synchronously, which appends callbackB to the event loop's queue.
    6. There is nothing left to do so the event loop is accessed, callbackC is first in the queue so it is executed.
    7. There is nothing left to do so the event loop is accessed, callbackB is first in the queue so it is executed.

    The easiest way I can think of to work around your problem is to use a library that has an Promise.prototype.isFulfilled function you can use to decide whether to call your second callback synchronously or not. For example:

    var Promise = require( 'bluebird' );                                                                                                                          
    
    Promise.prototype._SEPH_syncThen = function ( callback ) { 
        return (
          this.isPending()
            ? this.then( callback )
            : Promise.resolve( callback( this.value() ) ) 
        );  
    }
    
    Promise.resolve()._SEPH_syncThen(function callbackA () {
      console.log("finish run 1");
    })._SEPH_syncThen(function callbackB () {
      console.log("surprisingly this happens after run 2 finishes");
    });
    
    Promise.resolve()._SEPH_syncThen(function callbackC () {
      console.log("finish run 2");
    })
    

    This outputs:

    finish run 1
    surprisingly this happens after run 2 finishes
    finish run 2
    
    0 讨论(0)
提交回复
热议问题