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

查看:103
本文介绍了为什么当只调用同步函数时,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只在调用同步函数时才会异步?导致此行为的幕后发生了什么?

P.S。为了更好地理解这一点,我实现了自己的Promise系统,我发现使同步函数以预期的顺序发生很容易,但是让它们并行发生是我只能通过在每个时间放置几毫秒的setTimeout()来实现的。解决(我的猜测是,这不是香草承诺发生的事情,而且它们实际上是多线程的。)

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).

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

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构造函数的回调总是被同步调用,但是传递到然后的回调总是异步调用(你可以使用 setTimeout 在userland实现中延迟 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. 第二个承诺已解决

  4. callbackC被添加到事件循环的队列中

  5. 没有任何事情要做,所以访问事件循环,callbackA首先在队列中执行,它不会返回一个promise,所以callbackB的中间承诺会立即同步解析,这会追加callbackB到事件循环的队列。

  6. 没有任何事情可以访问事件循环,callbackC首先在队列中,所以它是exec uted。

  7. 没有任何事情可以访问事件循环,callbackB是队列中的第一个,所以它被执行。

  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.

我能想到解决问题的最简单方法是使用一个具有 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天全站免登陆