javascript - Promise opakuje pokusy až do úspěchu/neúspěchu s Typescriptem
Moje mobilní aplikace nahrává na server několik souborů po sobě, často z odlehlých oblastí s pochybnou sílou připojení. Z tohoto důvodu chci provést několik pokusů o odeslání souboru. Také chci pokračovat a zkusit další soubor v případě chyby, přičemž všechny chybové zprávy se zobrazí na konci exportu (např. "10 souborů nahráno, 3 selhalo...")
Nicméně mám problém pochopit rekuzivní vzor opakování s promises. Toto je to, co mám zatím:
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)
}
Když na serveru vyvolám výjimku, uvidím chybovou zprávu „Nepodařilo se 3krát!!!“, ale volaná promise se nevyřeší, takže zbytek exportu nemůže pokračovat. Myslím, že je to proto, že vytvářím zanořené promisy (tj. vytvářím novou promise při každém pokusu). Jak mohu zajistit, aby se původní promise vyřešila po 3 pokusech?
Díky!
Odpověď č. 1
Tady je to, co nakonec fungovalo:
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)
}
}
Odpověď č. 2
Mohl byste implementovat obal pro Promise(), který automaticky propojuje opakování, což vám umožní refaktorovat váš kód s jakoukoli logikou, kterou potřebujete, a nemusíte se současně starat o zpracování logiky opakování. Vaše použití by mohlo vypadat nějak takto:
sendFile(params, retries = 3) {
return Promise.retry(retries, (resolve, reject) => {
this._sendFile(params).then(resolve, reject)
})
}
Níže je ukázka, jak byste mohli implementovat 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}`)
)Pokud se necítíte pohodlně při rozšiřování nativního objektu (byl by to správný způsob, jak to udělat, protože se jedná o konfigurovatelnou, nevyčíslitelnou a zapisovatelnou vlastnost), můžete ji jednoduše implementovat jako statickou funkci:
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)
)
}
