Website-Suche

javascript - Promise wiederholt Versuche bis Erfolg/Misserfolg mit Typescript


Meine mobile App lädt mehrere Dateien nacheinander auf den Server hoch, oft aus entfernten Gebieten mit fragwürdiger Verbindungsstärke. Aus diesem Grund möchte ich einige Versuche unternehmen, die Datei zu senden. Ich möchte auch fortfahren und versuchen, die nächste Datei im Falle eines Fehlers zu senden, wobei alle Fehlermeldungen am Ende des Exports angezeigt werden (z. B. "10 Dateien hochgeladen, 3 fehlgeschlagen...").

Ich habe jedoch Schwierigkeiten, das rekursive Wiederholungsmuster mit Promises zu verstehen. Hier ist, was ich bisher habe:

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)


  }

Wenn ich auf dem Server eine Ausnahme auslöse, sehe ich die Fehlermeldung „3 Mal fehlgeschlagen!!!“, aber das aufrufende Promise wird nicht aufgelöst, damit der Rest des Exports fortfahren kann. Ich glaube, das liegt daran, dass ich verschachtelte Promises erstellt habe (d. h. ich erstelle bei jedem erneuten Versuch ein neues Promise). Wie kann ich das ursprüngliche Promise nach 3 Versuchen auflösen lassen?

Danke!

Antwort #1

Hier ist, was letztendlich funktioniert hat:

  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)

    }

  }

Antwort #2

Sie könnten einen Wrapper für Promise() implementieren, der automatisch Wiederholungen für Sie verknüpft, sodass Sie Ihren Code mit beliebiger Logik umstrukturieren können und sich nicht gleichzeitig um die Behandlung der Wiederholungslogik kümmern müssen. Ihre Verwendung könnte ungefähr so aussehen:

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

Unten wird gezeigt, wie Sie Promise.retry() implementieren könnten:

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}`)
)

Wenn Sie sich unwohl dabei fühlen, ein natives Objekt zu erweitern (dies wäre der korrekte Weg, dies zu tun, da es sich um eine konfigurierbare, nicht aufzählbare und beschreibbare Eigenschaft handelt), können Sie es einfach als statische Funktion implementieren:

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
    ? retry(retries - 1, executor)
    : Promise.reject(error)
  )
}

Verwandte Artikel