我可以在不使用 deferr 的情况下在变量更改时解决承诺吗?

Can I resolve a promise when a variable change without using deferr?

我正在将一些基于 Q-promise 的打字稿代码转换为 ES6-promises。

在某个时候,我使用了 Q.defer 并且在我的迁移中我只是将 defer 重写为 ES6 promise,就像这条评论中解释的那样:

如果可能的话,我试图摆脱这种延迟方法,并且正在寻找替代解决方案。

我提问的原因是:

  1. 延迟承诺通常被认为是一种反模式
  2. 想知道这种情况是否是少数真正只有延迟的情况之一

这是我的场景:

// app start, this is my low level API
init() {
    commService.subscribe("myRecordId", {
        onRecordAdd: this.onAdd
    });
}

...

private _myRecord: MyRecordObj | null = null;
private _recordReceivedDef = newDeferred(); // was Q.defer()

// callback will be called when record is received
private readonly onAdd = (recordObj: MyRecordObj) => {
    this._myRecord = recordObj;
    this._recordReceivedDef.resolve(recordObj);
}

...

// here's my async code that requires the deferred
public readonly doStuff = async () => {
    // ...
    const myRec = await this._recordReceivedDef.promise;
    // use myRef
}

我的问题是:有什么办法可以消除这种延迟? 我正在考虑在 _myRecord 更改时解决的问题,但不知道该怎么做。

旁注: 我在应用程序的其他部分使用了 MobX,因此

await when(() => this._myRecord); // of course _myRecord must be @observable

会很方便,但不幸的是我不能在这段特定的代码中使用 MobX。

非常感谢任何帮助。

非常感谢!

假设 initdoStuff 之前调用,正确的方法是

init() {
    this._myRecordPromise = new Promise((resolve, reject) => {
        commService.subscribe("myRecordId", {
            onRecordAdd: (recordObj: MyRecordObj) => {
                // callback will be called when record is received
                resolve(this._myRecord = recordObj);
            }
        });
    });
}

…

private _myRecord: MyRecordObj | null = null;
private _myRecordPromise: Promise<MyRecordObj>;

…

public readonly doStuff = async () => {
    …
    const myRec = await this._myRecordPromise;
    // use myRef
}

您甚至可以完全删除 _myRecord,只保留 _myRecordPromise

但是,您可能要考虑在收到记录之前根本不构建您的实例,请参阅 Is it bad practice to have a constructor function return a Promise?

如果在任意时间调用 init,您将需要某种延迟模式,但不需要 newDeferred()。随便写

init() {
    commService.subscribe("myRecordId", {
        onRecordAdd: this.onAdd
    });
}

…

private _myRecordPromise: Promise<MyRecordObj> = new Promise(resolve => {
    this.onAdd = resolve;
});
private readonly onAdd: (recordObj: MyRecordObj) => void;

对于可能感兴趣的人,还有另一种使用事件发射器方法的解决方案。 假设您有一个 class EventEmitter 实现了以下接口:

// pseudo code
type UnsubscribeFn = () => void;
interface IEventEmitter<T> {
    /** Fires an event */
    emit(args: T): void;
    /** Subscribes to emissions of this event and provides an unsubscribe function */
    subscribe((args: T) => void): UnsubscribeFn;
}

您可以为您的客户提供如下所示的 whenAdded 功能

// pseudo code
class Subscriber {
    readonly onAddEmitter = new EventEmitter<MyRecordObj>();

    // app start
    init() {
        commService.subscribe("myRecordId", {
            onRecordAdd: this.onAdd
        });
    }
    
    onAdd = (rec: MyRecordObj) => {
           // do some stuff
           onAddEmitter.emit(rec);
    }
    
    whenAdded(): Promise<MyRecordObj> {
        return new Promise((res) => {
            const unsub = onAddEmitter.subscribe((record: MyRecordObj) => {
                unsub();
               res(record); 
            });
        });
    }
}

实现的目标是:

  • 摆脱 Q promise,转而使用 ES6
  • 使 commService 事件 API 适应基于承诺的事件