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

瑟夫·里德(Seph Reed)

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

这是一些代码,显示了控制台中的操作顺序。如果您运行它,您将看到即使每个函数都是同步的,输出也会显示两个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);
    }
}
保罗

传递给Promise构造函数的回调总是被同步调用,但是传递给回调的回调then总是被异步调用(您可以在userland实现中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. 没有什么可做的,因此无法访问事件循环,callbackA在队列中首先执行,因此它不会执行promise,因此不会立即同步解决callbackB的中间promise,从而将callbackB附加到事件循环的队列中。
  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

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

Promises 中的异步函数

来自分类Dev

我在异步函数中使用Promises是否正确?

来自分类Dev

异步函数JS问题。我可以使用Promises吗?

来自分类Dev

如何使用返回Promises的NAPI创建异步函数

来自分类Dev

异步函数与 Promises 一起工作很奇怪

来自分类Dev

同步调用异步函数时被调用者的线程会发生什么

来自分类Dev

将异步和同步工作与javascript中的async / await和/或promises混合

来自分类Dev

CommonJS是同步的,但是如果调用异步函数会发生什么

来自分类Dev

JavaScript函数是同步还是异步?

来自分类Dev

JavaScript函数是同步的还是异步的?

来自分类Dev

从同步函数中调用异步函数

来自分类Dev

lua同步调用C异步函数

来自分类Dev

lua同步调用C异步函数

来自分类Dev

为什么异步函数被调用两次?

来自分类Dev

为什么异步函数被调用两次?

来自分类Dev

同步调用异步方法时出错

来自分类Dev

从JavaScript编译为Wasm的异步Rust函数时,为什么字符串参数为空?

来自分类Dev

Tulip / asyncIO:为什么不是所有调用都异步并指定什么时候应该同步?

来自分类Dev

为什么画布绘制是异步的,而 API 是同步的?

来自分类Dev

通过Promises排队异步操作

来自分类Dev

同步函数调用的行为与异步函数调用的“等待”行为

来自分类Dev

伪造的异步函数。为什么?

来自分类Dev

为什么使用 Python 异步从文件读取和调用 API 比同步慢?

来自分类Dev

仅当来自服务的异步调用完成时,如何调用函数?

来自分类Dev

调用异步时JavaScript循环

来自分类Dev

使异步函数同步的后果

来自分类Dev

使mongoskin函数是异步同步的

来自分类Dev

同步迭代异步函数

来自分类Dev

JavaScript解释器如何知道遇到的函数调用应该被同步还是异步处理?

Related 相关文章

  1. 1

    Promises 中的异步函数

  2. 2

    我在异步函数中使用Promises是否正确?

  3. 3

    异步函数JS问题。我可以使用Promises吗?

  4. 4

    如何使用返回Promises的NAPI创建异步函数

  5. 5

    异步函数与 Promises 一起工作很奇怪

  6. 6

    同步调用异步函数时被调用者的线程会发生什么

  7. 7

    将异步和同步工作与javascript中的async / await和/或promises混合

  8. 8

    CommonJS是同步的,但是如果调用异步函数会发生什么

  9. 9

    JavaScript函数是同步还是异步?

  10. 10

    JavaScript函数是同步的还是异步的?

  11. 11

    从同步函数中调用异步函数

  12. 12

    lua同步调用C异步函数

  13. 13

    lua同步调用C异步函数

  14. 14

    为什么异步函数被调用两次?

  15. 15

    为什么异步函数被调用两次?

  16. 16

    同步调用异步方法时出错

  17. 17

    从JavaScript编译为Wasm的异步Rust函数时,为什么字符串参数为空?

  18. 18

    Tulip / asyncIO:为什么不是所有调用都异步并指定什么时候应该同步?

  19. 19

    为什么画布绘制是异步的,而 API 是同步的?

  20. 20

    通过Promises排队异步操作

  21. 21

    同步函数调用的行为与异步函数调用的“等待”行为

  22. 22

    伪造的异步函数。为什么?

  23. 23

    为什么使用 Python 异步从文件读取和调用 API 比同步慢?

  24. 24

    仅当来自服务的异步调用完成时,如何调用函数?

  25. 25

    调用异步时JavaScript循环

  26. 26

    使异步函数同步的后果

  27. 27

    使mongoskin函数是异步同步的

  28. 28

    同步迭代异步函数

  29. 29

    JavaScript解释器如何知道遇到的函数调用应该被同步还是异步处理?

热门标签

归档