簡體   English   中英

處理承諾鏈外的承諾拒絕

[英]Handling promise rejections outside the promise chain

我熟悉承諾的工作方式以及未處理的承諾拒絕是什么,但是我有一個案例,我很難准確地確定如何捕獲這個特定的未處理的承諾拒絕錯誤。

我的目標是創建一個速率受限的重試處理程序,它可以與一系列順序承諾函數內聯。

我正在使用一個實現轉換流的限制器類。 進入流的消息僅以速率受限的速率發出,該速率用於強制執行重試率。

我的重試處理程序實現了一個函數sendMessage ,該函數返回一個帶有結果的承諾。 如果消息發送失敗,重試處理程序應重試發送消息,直至達到指定的最大重試次數。 它還應該將傳出消息限制為指定的速率。

重試處理程序實際上並不發出 API 請求本身,這是由注冊的 API 處理程序函數(它是抽象實際 API 調用的第 3 方庫)完成的。 API 可能會以以下兩種方式之一失敗:

  1. 調用直接失敗,來自處理程序的承諾被拒絕並被sendMessage函數的 .catch 捕獲

  1. API 處理程序不會失敗,但處理程序返回的結果為空,在這種情況下, errorEmitter模塊稍后會發出一個事件( errorEmitter模塊擴展EventEmitter )。
class RetryMessageHandler extends Readable {

  constructor(msg, limit, interval, max, handler, errorEmitter) {
    super({objectMode: true});
    this._limiter = new RateLimiter(limit, interval);
    this._retryCount = 0;
    this._maxRetries = max;
    this._msg = msg;
    this._handler = handler;
    this._errorEmitter = errorEmitter;

    // The retry handler is intended as single use. The promise is
    // created and stored to deal with the 2 different rejection 
    // scenarios

    this._promise = new Promise((resolve, reject) => { 
      this._resolve = resolve; 
      this._reject = reject;
    });

    this.retryTrigger = this.retryTrigger.bind(this);
    this.sendMessage = this.sendMessage.bind(this);

    // catch the messages as they exit the rate limiter
    this._limiter.on('data', this.sendOrder);

    // add the listener for the message failed event
    this._errorEmitter.prependOnceListener('Sending Message Failed', this.retryTrigger);

    // allows send() to push messages into the rate limiter
    this.pipe(this._limiter);
  }

  // after instantiation of the retry handler this method is
  // called to send the message with retries
  sendMessage() {
    this._retryCount++;

    // attempt to send message via API message handler
    this._handler(this._msg)

      .then((result) => {

        // check if the result received was null
        if (result) {
          
          // remove the errorEmitter module listener
          this._errorEmitter.removeListener('Sending Message Failed', this.retryTrigger);

          // resolve the retry handler promise and return the 
          // result to the retry handler caller.
          this._resolve(result);
        }
      })
      .catch((err) => {
        
        // scenario 1: Message sending failed directly
        // Need to remove the errorEmitter to avoid triggering twice
        this._errorEmitter.removeListener('Sending Message Failed', this.retryTrigger);

        // Trigger the retry method
        this.send();
      })

    return this._promise;
  }

  // required function due to extending Readable
  _read(size: number) {
    /* no op */
  }

  // Scenario 2: Message sending failed indirectly.
  // This method that is called whenever the errorEmitter module
  // emits a 'Sending Message Failed' event
  retryTrigger(err) {
    // Trigger the retry method
    this.send();
  }

  // Handles the retry sending the message
  send() {

    // Check if we've already exceed the maximum number of retries
    if (this._retryCount >= this._maxRetries) {
      this._errorEmitter.removeListener('Sending Message Failed', this.retryTrigger);


      // THIS IS WHERE THE PROBLEM OCCURS
      // We need to throw an error because we've exceeded the max
      // number of retries. This error causes the unhandled promise rejection error
      throw new ExceededRetryCountError('Exceeded maximum number of retries', this._msg);
    }

    // if can retry we need to reset the errorEmitter listener
    this._errorEmitter.prependOnceListener('Sending Message Failed', this.retryTrigger);

    // Finally push the message into the rate limiter.
    // The message will come out the other side and call the
    // sendMessage() method and the whole thing starts over again
    this.push(this._msg);
  } 
}

最初,我沒有拋出錯誤,而是使用this._reject(new ExceededRetryCountError('Exceeded maximum number of retries', this._msg)); 但這仍然有同樣的問題。

我發現了這個相關的問題( How can you retry after an exception in Javascript when using promises? )但這僅處理在承諾鏈中發生故障時的重試情況。

我想我可能已經發現導致未處理異常的問題。 當返回承諾的函數拋出錯誤時,catch 語句在同一個滴答上執行。 如果在函數執行的同一個滴答上拋出錯誤,則必須已經分配了 catch 處理程序。 這通常是通過調用function().then( … ).catch(err) ,但是對於 promises 通常也可以通過let promise = function(); promise.then( ... ).catch(err)直接處理返回的 promise let promise = function(); promise.then( ... ).catch(err) let promise = function(); promise.then( ... ).catch(err) 在第二種情況下,雖然這將導致未處理的異常,因為在拋出錯誤時,catch 語句尚未分配給承諾。 附加 catch 語句后,它實際上會正確捕獲錯誤,但未處理的拒絕警告已經觸發。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM