繁体   English   中英

为什么仅调用同步函数时javascript promises异步?

[英]Why are javascript promises asynchronous when calling only synchronous functions?

在测试中,我发现JavaScript Promises 始终是异步的,无论它们的链中是否包含任何异步函数。

这是一些代码,显示了控制台中的操作顺序。 如果运行它,即使每个函数都是同步的,输出aPromise()显示两个aPromise()调用正在并行运行,并且"surprisingly this happens after run 2 finishes"在运行2完成之前没有发生。

 function aPromise() { return new Promise(function(resolve, reject) { console.log("making promise A") resolve(bPromise()); console.log("promise A resolved") }); } function bPromise() { return new Promise(function(resolve, reject) { console.log("making and resolving promise B") resolve(); }); } aPromise().then(function() { console.log("finish run 1"); }).then(function() { console.log("surprisingly this happens after run 2 finishes"); }); aPromise().then(function() { console.log("finish run 2"); }) 

输出到控制台:

making promise A
making and resolving promise B
promise A resolved
making promise A
making and resolving promise B
promise A resolved
finish run 1
finish run 2
surprisingly this happens after run 2 finishes

那么, 为什么JavaScript仅在调用同步函数时才承诺异步? 导致这种行为的幕后发生了什么?


PS为了更好地理解这一点,我实现了自己的Promise系统,发现使同步功能按预期顺序执行很容易,但是使它们并行执行是我只能通过将setTimeout()设置为几毫秒来实现的功能每一个解决方案(我的猜测是香草诺言并没有发生这种情况,它们实际上是多线程的)。

对于我的一个程序来说,这是一个小问题,我遍历一棵树,将一个函数数组应用于每个节点,如果该节点已在运行异步函数,则将该函数放入队列。 大多数功能都是同步的,因此很少使用队列,但是从回调(地狱)切换到Promises时,我一直遇到一个问题,即由于Promises从未同步运行,因此几乎总是使用队列。 这不是一个很大的问题,但它有点像调试的噩梦。

1年后编辑

我最终写了一些代码来解决这个问题。 这不是很彻底,但是我已经成功地使用它来解决我遇到的问题。

var SyncPromise = function(fn) {
    var syncable = this;
    syncable.state = "pending";
    syncable.value;

    var wrappedFn = function(resolve, reject) {
        var fakeResolve = function(val) {
            syncable.value = val;
            syncable.state = "fulfilled";
            resolve(val);
        }

        fn(fakeResolve, reject);
    }

    var out = new Promise(wrappedFn);
    out.syncable = syncable;
    return out;
}

SyncPromise.resolved = function(result) {
    return new SyncPromise(function(resolve) { resolve(result); });
}

SyncPromise.all = function(promises) {
    for(var i = 0; i < promises.length; i++) {
        if(promises[i].syncable && promises[i].syncable.state == "fulfilled") {
            promises.splice(i, 1);
            i--;
        }
        // else console.log("syncable not fulfilled" + promises[i].syncable.state)
    }

    if(promises.length == 0)
        return SyncPromise.resolved();

    else
        return new SyncPromise(function(resolve) { Promise.all(promises).then(resolve); });
}

Promise.prototype.syncThen = function (nextFn) {
    if(this.syncable && this.syncable.state == "fulfilled") {
            //
        if(nextFn instanceof Promise) {
            return nextFn;
        }
        else if(typeof nextFn == "function") {
            var val = this.syncable.value;
            var out = nextFn(val);
            return new SyncPromise(function(resolve) { resolve(out); });
        }
        else {
            PINE.err("nextFn is not a function or promise", nextFn);
        }
    }

    else {
        // console.log("default promise");
        return this.then(nextFn);
    }
}

传递给无极构造函数的回调总是同步调用,但传递到回调then总是异步调用(你可以使用setTimeout与延迟0在用户空间实现来实现这一点)。

简化您的示例(并提供匿名函数的名称,以便我可以引用它们):

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");
})

仍然以相同的顺序给出输出:

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

事件按以下顺序发生:

  1. 第一个承诺得到解决(同步)
  2. callbackA被添加到事件循环的队列中
  3. 第二个承诺解决了
  4. callbackC被添加到事件循环的队列中
  5. 没有什么可做的,因此无法访问事件循环,回调A首先在队列中执行,因此它不会返回承诺,因此回调B的中间承诺会立即被同步解析,这会将回调B附加到事件循环的队列中。
  6. 没有什么可做的,因此可以访问事件循环,callbackC在队列中位于第一个位置,因此可以执行。
  7. 没什么可做的,因此可以访问事件循环,回调B在队列中位于第一个位置,因此可以执行。

解决该问题的最简单方法是使用具有Promise.prototype.isFulfilled函数的库,该函数可用于决定是否同步调用第二个回调。 例如:

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");
})

输出:

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

您的代码很好,因为您希望自己的诺言能够独立运行,并让它们以自己的方式执行,而不管每个先完成。 一旦代码异步,就无法预测哪个代码将首先完成(由于event loop的异步性质)。

但是,如果您希望在所有诺言完成后兑现诺言,则应使用Promise.all (相当于jQuery的$.when Promise.all )。 看到:

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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