为什么javascript承诺在只调用同步函数时是异步的? [英] Why are javascript promises asynchronous when calling only synchronous functions?

查看:36
本文介绍了为什么javascript承诺在只调用同步函数时是异步的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

这是一些显示控制台中操作顺序的代码.如果你运行它,你会看到即使每个函数都是同步的,输出显示两个 aPromise() 调用并行运行,并且 令人惊讶的是,这发生在运行 2 完成后" 不会在运行 2 完成之前发生.

Here is some code that shows the order of operations in console. If you run it you will see that even though every function is synchronous the output shows both of the aPromise() calls being run in parallel, and "surprisingly this happens after run 2 finishes" not happening before run 2 finishes.

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 承诺在只调用同步函数时是异步的?导致这种行为的幕后发生了什么?

附言为了更好地理解这一点,我实现了我自己的 Promise 系统,我发现让同步函数按照预期的顺序发生很容易,但让它们并行发生是我只能通过在每一个地方设置一个几毫秒的 setTimeout() 来完成的解决(我的猜测是,这不是普通 Promise 所发生的情况,它们实际上是多线程的).

P.S. In order to better understand this I implemented my own Promise system and I found that making synchronous functions happen in the expected order was easy but making them happen in parallel was something I could only accomplish by putting a setTimeout() of a few milliseconds at every resolve (My guess is that this is not what's happening with vanilla promises and that they are actually being multi threaded).

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

This has been a small problem for one of my programs where I'm traversing a tree applying an array of functions to each node and putting the functions in queue if that node has an asynchronous function already running. Most of the functions are synchronous so the queue is rarely used but upon switching over from callbacks (hell) to Promises I've been having an issue where the queues get used almost always as a result of Promises never running synchronously. It's not a huge problem but it is a bit of a debugging nightmare.

1 年后编辑

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

I ended up writing some code to deal with this. It's not amazingly thorough, but I've used it with success to solve the issue I was having.

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 的回调总是被异步调用(你可以使用setTimeout 在用户态实现中延迟 0 以实现这一点).

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

事件按以下顺序发生:

  1. 第一个承诺被解决(同步)
  2. callbackA 被添加到事件循环的队列中
  3. 第二个promise解决了
  4. callbackC 被添加到事件循环的队列中
  5. 没有什么可做的,所以事件循环被访问,callbackA 是队列中的第一个,所以它被执行,它不返回一个承诺,所以 callbackB 的中间承诺立即同步解析,这将 callbackB 附加到事件循环的队列.
  6. 没有什么可做的,因此事件循环被访问,callbackC 在队列中排在第一个,因此它被执行.
  7. 没有什么可做的,因此事件循环被访问,callbackB 在队列中排在第一个,因此它被执行.

我能想到的解决您的问题的最简单方法是使用具有 的库Promise.prototype.isFulfilled 函数可以用来决定是否同步调用你的第二个回调.例如:

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

输出:

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

这篇关于为什么javascript承诺在只调用同步函数时是异步的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆