承诺使用Typescript重试成功/失败

时间:2017-08-01 17:49:50

标签: javascript typescript ionic-framework es6-promise ionic3

我的移动应用程序连续将多个文件上传到服务器,通常来自连接强度可疑的偏远地区。出于这个原因,我想尝试发送文件。我还想继续并在发生故障时尝试下一个,并在导出结束时显示所有错误消息(即“上传10个文件,3个失败......”)

然而,我在使用promises计算递归重试模式时遇到了麻烦。这是我到目前为止所做的:

sendFile(params, retries = 3){
    console.log("SENDING FILE: ", retries, "attempts remaining", params)

    return new Promise((resolve, reject)=>{
      if(retries > 0){
        this._sendFile(params)
        .then(()=>{
          // Upload Success
          console.log("Upload Success!")
          resolve()
        })
        .catch((err)=>{
          console.log("Upload Fail", err)
          // Retry
          this.sendFile(params, --retries)
        })
      }else{
        console.log("Failed 3 times!!!")
        //Record error
        this.exportStatus.errors.push({
          message:"A file failed to upload after 3 attempts.",
          params: params
        })

        //Resolve and move on to next export
        resolve()

      }
    })

  }

  _sendFile(params){

      // Mobile - File Transfer

        let options = {
          fileKey: "file_transfer",
          fileName: params.fileName,
          httpMethod: "PUT",
          chunkedMode: false,
          headers: {
            "x-user-email":this.settings.user.email,
            "x-user-token":this.settings.user.authentication_token,
          }
        }

        let fileTransfer = this.transfer.create()
        let url = encodeURI(this.settings.api_endpoint + params.url)

        return fileTransfer.upload(params.file, url, options, true)


  }

当我在服务器上引发异常时,我会看到“失败3次!!!”错误消息,但调用的promise不会解析其余的导出以继续。我相信这是因为我创建了嵌套的promises(即每次重试都会创建一个新的promise)。如何在3次重试后解决原始承诺?

谢谢!

4 个答案:

答案 0 :(得分:3)

您可以为Promise()实现一个自动链接重试的包装器,允许您使用您需要的任何逻辑重构代码,而不必担心同时处理重试逻辑。您的用法可能如下所示:

sendFile(params, retries = 3) {
  return Promise.retry(retries, (resolve, reject) => {
    this._sendFile(params).then(resolve, reject)
  })
}

以下是如何实施Promise.retry()



Object.defineProperty(Promise, 'retry', {
  configurable: true,
  writable: true,
  value: function retry (retries, executor) {
    console.log(`${retries} retries left!`)

    if (typeof retries !== 'number') {
      throw new TypeError('retries is not a number')
    }

    return new Promise(executor).catch(error => retries > 0
      ? Promise.retry(retries - 1, executor)
      : Promise.reject(error)
    )
  }
})

Promise.retry(100, (resolve, reject) => {
  // your sendFile core logic with proper
  // calls to resolve and reject goes here
  const rand = Math.random()

  console.log(rand)

  if (rand < 0.1) resolve(rand)
  else reject(rand)
}).then(
  value => console.log(`resolved: ${value}`),
  error => console.log(`rejected: ${error}`)
)
&#13;
&#13;
&#13;

如果您对扩展本机对象感到不舒服(这是正确的方法,因为它是可配置的,不可枚举和可写的属性),您可以将其实现为静态函数:< / p>

function retry (retries, executor) {
  console.log(`${retries} retries left!`)

  if (typeof retries !== 'number') {
    throw new TypeError('retries is not a number')
  }

  return new Promise(executor).catch(error => retries > 0
    ? Promise.retry(retries - 1, executor)
    : Promise.reject(error)
  )
}

答案 1 :(得分:1)

以下是最终工作的内容:

  sendFile(params, retries = 3, promise = null){
    console.log("SENDING FILE: ", retries, "attempts remaining", params)

    if(retries > 0){
      return this._sendFile(params)
      .then(()=>{
        // Upload Success
        console.log("Upload Success!")
        return Promise.resolve(true)
      })
      .catch((err)=>{
        console.log("Upload Fail", err)

        this.exportStatus.retries++

        return this.sendFile(params, --retries) // <-- The important part
      })
    }else{
      console.log("Failed 3 times!!!")

      this.exportStatus.errors.push({
        message:"A file failed to upload after 3 attempts.",
        params: params
      })

      return Promise.resolve(false)

    }

  }

答案 2 :(得分:0)

帕特里克·罗伯茨(Patrick Roberts)发布的内容的后续活动, 如何在打字稿中实现它的示例:

type promiseExecutor<T> = (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void;
class RetryablePromise<T> extends Promise<T> {
  static retry<T>(retries: number, executor: promiseExecutor<T>): Promise<T> {
    return new RetryablePromise(executor).catch(error =>
      retries > 0 ? RetryablePromise.retry(retries - 1, executor) : RetryablePromise.reject(error)
    );
  }
}

,用法如下:

RetryablePromise.retry(4, (resolve, reject) => console.log('run'));

答案 3 :(得分:0)

为了完整起见,这是一个简单的版本:

export async function promiseRetry<T>(fn: () => Promise<T>, retries = 5, err?: any): Promise<T> {
  await new Promise(resolve => setTimeout(resolve, (5 - retries) * 1000));

  return !retries ? Promise.reject(err) : fn().catch(error => promiseRetry(fn, (retries - 1), error));
}

内置重试逻辑,不需要的可以注释掉。

用法:

const myVal = await promiseRetry(() => myPromiseFn())