如何实现 BluebirdJS 同步检查扩展原生 Promise

Posted

技术标签:

【中文标题】如何实现 BluebirdJS 同步检查扩展原生 Promise【英文标题】:How to implement BluebirdJS synchronous inspection extending a native Promise 【发布时间】:2022-01-24 03:24:19 【问题描述】:

我正在更新一些使用 Bluebird 承诺的旧代码(最初不是我的)。我宁愿使用原生 ES6 Promises 代替,但旧代码使用 Promise 没有的函数,用于检查承诺是否已解决。

这与一个类似的问题(Is there a way to tell if an ES6 promise is fulfilled/rejected/resolved?)有关,但那里给出的解决方案非常不同,所以我想知道以下代码是否是解决问题的合理方法。

export class QueryablePromise<T> extends Promise<T> 
  private _isRejected = false;
  private _isResolved = false;
  private _isSettled = false;

  then<TResult1 = T, TResult2 = never>(
    onResolved?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
    onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
  ): Promise<TResult1 | TResult2> 
    const newResolved = onResolved && ((value: T): TResult1 | PromiseLike<TResult1> => 
      this._isResolved = true;
      this._isSettled = true;
      return onResolved(value);
    );
    const newRejected = onRejected && ((reason: any): TResult2 | PromiseLike<TResult2> => 
      this._isRejected = true;
      this._isSettled = true;
      return onRejected(reason);
    );

    return super.then(newResolved, newRejected);
  

  catch<TResult = never>(
    onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null
  ): Promise<T | TResult> 
    const newRejected = onRejected && ((reason: any): TResult | PromiseLike<TResult> => 
      this._isRejected = true;
      this._isSettled = true;
      return onRejected(reason);
    );

    return super.catch(newRejected);
  

  finally(
    onFinally?: (() => void) | undefined | null
  ): Promise<T> 
    const newFinally = onFinally && ((): void => 
      this._isSettled = true;
      return onFinally();
    );

    return super.finally(newFinally);
  

  get isRejected(): boolean  return this._isRejected; 
  get isResolved(): boolean  return this._isResolved; 
  get isSettled(): boolean  return this._isSettled; 

基本上,我将传递给thencatchfinally 的每个回调包装在另一个函数中,该函数设置适当的标志,然后调用原始回调。以这种方式可以轻松地多次重复设置标志,但我认为这不是什么大问题。

我试图想出一种方法来使用我的 promise 子类的构造函数来解决这个问题,并以某种方式在原始 executor 参数周围放置一个包装器以拦截 resolvereject 的调用,但不能完全放我想到了一种实现这种解决方案的方法。

我还尝试在该子类的构造函数中简单地添加我自己单独的 thencatchfinally 回调,每个回调除了设置我的状态标志外无事可做,但奇怪的是导致了堆栈溢出,构造函数被递归调用,直到它崩溃。

【问题讨论】:

嗨,尝试同步评估 Promise 的状态一次又一次。至少在我寻找解决方案时,是因为我围绕 Promises 的整个设计都是错误的。有关更多信息,请参阅此帖子:softwareengineering.stackexchange.com/questions/309511/… 我没有发现任何需要测试我在自己的代码中创建的 Promise 的状态,显然有很多方法可以避免这样做,但在这种情况下我正在尝试更接近我正在修改的原始代码的方法。 "如果以下代码是解决问题的合理方法。" - 否。正确的解决方案是重写使用承诺不同步检查其状态的代码。为什么你认为你需要这样做? "我也尝试在这个子类的构造函数中简单地添加我自己单独的 then 回调,但奇怪的是导致堆栈溢出" - 这是因为 @ 987654335@构造同一个子类的新promise 【参考方案1】:

此代码看起来并不可靠。你需要记住 Promises 在创建时会自动解析。

使用上面的代码,这个简单的测试会失败:

const a = new QueryablePromise((res, rej) => 
    setTimeout(() =>  console.log('resolved'); res(), 100)
);

setTimeout(() => 
console.log(a.isSettled)
, 500);

会输出

// resolved
// false

需要考虑所有情况,不仅是在调用thencatchfinally 时。我可能会重写构造函数并像这样包装resolvereject 函数:

class QueryablePromise<T> extends Promise<T> 
  private _isRejected: boolean | undefined;
  private _isResolved: boolean | undefined;
  private _isSettled: boolean | undefined;

  constructor (fn: (res: (value: T | PromiseLike<T>) => void, rej: (reason?: any) => void) => void) 
    super((resolve, reject) => fn(
      value => 
        resolve(value)
        if (value instanceof Promise || typeof value === 'object' && typeof (value as any).then === 'function') 
          (value as any).then(() => 
            this._isResolved = true;
            this._isSettled = true;    
          );
         else 
          this._isResolved = true;
          this._isSettled = true;
        
      ,
      reason => 
        reject(reason)
        this._isRejected = true;
        this._isSettled = true;
      ,
    ));

    this._isRejected = false;
    this._isResolved = false;
    this._isSettled = false;
  

  get isRejected(): boolean  return this._isRejected!; 
  get isResolved(): boolean  return this._isResolved!; 
  get isSettled(): boolean  return this._isSettled!; 


【讨论】:

谢谢!正如您所描述的承诺自动解决,我没有看到任何问题,所以我认为我的原始代码没有错误,但您的这种方法是我最初希望做的。在 super(...) 调用中引用 this 时,我不断收到错误消息。然而,你在这里做的方式解决了我遇到的问题。好东西! 奇怪的是,如果不调用 then,您就看不到 isSettled 为假。但我很高兴代码对你有用。 :) 这个解决方案还是错误的。 QueryablePromise.resolve(new Promise()).isSettled 将返回 true 尽管承诺仍在等待中。 @Bergi 在解析 Promise 时,我更新了该类以涵盖 thenable 值。可能需要进行一些调整以涵盖特定场景,但是,如果需要涵盖所有场景的整体解决方案,则最好坚持使用 BluebirdJS。

以上是关于如何实现 BluebirdJS 同步检查扩展原生 Promise的主要内容,如果未能解决你的问题,请参考以下文章

Google Chrome 同步检查是不是通过 API/扩展启用?

puma gem - 无法构建 gem 原生扩展

原生 Android BLE 实现本质上是同步的吗?

同步加载和异步加载的原生态实现

云原生之使用docker部署NTP时间服务器

一个对页面节点的拖踹用原生js该如何实现