为什么我的连锁承诺被阻止?

j0h*_*4r5 3 javascript promise

我有一个类方法,该方法将类中的其他方法链接在一起,并在类的另一个实例上调用一个方法:

class Thing {
    doSomething(nextThing) {
        return new Promise((resolve) =>
            this.initialize()
                .then(() => this.doA())
                .then(() => {
                    nextThing.initialize(); // call initialize() on another instance
                    return this.doB();
                })
                .then(() => this.doC())
                .then(resolve)
        );
    }

    initialize() {
        return new Promise((resolve) => {
            // take a long time to do something
            // ...
            // ...
            resolve();
        });
    }

    doA() { return new Promise((resolve) => resolve()); }

    doB() { return new Promise((resolve) => resolve()); }

    doC() { return new Promise((resolve) => resolve()); }
}

const thing1 = new Thing();
const thing2 = new Thing();

thing1.doSomething(thing2);
Run Code Online (Sandbox Code Playgroud)

但是,在另一个类实例上调用该函数会锁定链的流程。this.doB()并且nextThing.initialize()会同时运行(根据需要),但this.doC()要等到nextThing.initialize()解决后才能运行。

什么是正确的方式来确保此流程按预期进行,nextThing.initialize()this.doB()同时开始,并this.doC()this.doB()解决后立即开始?这没关系,如果nextThing.initialize()结算后this.doC()

jfr*_*d00 5

当您执行此结构时:

return new Promise(resolve => {
    // run some long synchronous piece of code
    resolve(...);
});
Run Code Online (Sandbox Code Playgroud)

这是发生了什么。

  1. 创建一个新的Promise对象
  2. 作为promise构造函数执行的一部分,promise executor回调被同步调用
  3. 您长时间运行的同步代码称为executor回调
  4. 您打电话resolve(...)来解决以前创建的承诺
  5. 您从诺言执行者返回
  6. Promise构造函数返回
  7. 您从主机函数返回,此函数调用后的代码行将开始运行
  8. 稍后的某个时间(在当前的Java脚本将控制权返回给系统之后),将.then()根据先前的promise调用处理程序。

因此,promise会同步调用执行程序回调。它不允许您“在后台运行任何内容”。Javascript仍然是单线程的。

您不能使用诺言将同步代码转换为异步代码。您可以使用一些promise技术来更改代码运行的时间安排,但是Javascript中的同步代码仍然是同步的,而Javascript中的阻塞代码无论何时运行。

Promise纯粹是一个通知系统,用于在其他操作告诉诺言现在已解决或被拒绝时通知您。它们不会神奇地将同步代码转换为异步代码。

因此,最重要的是,您不能使用promise采取同步的,长时间运行的initialize()功能,并以某种方式使其成为非阻塞或异步的。

用nextThing.initialize()和this.doB()同时启动的方式来确保按预期方式进行流动的正确方法是,

如果nextThing.initialize()是同步且阻塞的,则它不能与任何事物同时运行。node.js运行您的Javascript单线程。一次运行一个Javascript。承诺不能改变这一点。

并在this.doB()解析后立即启动this.doC()

由于this.doB()并且this.doC()两者都返回了Promise,因此您将Promise与链接的.then()处理程序链接在一起以对这些操作进行排序。您的代码似乎已经做到了。

有关将当前运行的同步代码卸载到当前node.js单个Javascript线程之外的选项的信息,请参见以下其他答案:

使时间密集型功能异步


仅供参考,也许这只是伪代码,但是从来没有理由这样做:

return new Promise((resolve) => resolve());
Run Code Online (Sandbox Code Playgroud)

您可以改为:

return Promise.resolve();.
Run Code Online (Sandbox Code Playgroud)