javascript - Bryt löfteskedjan och kalla en funktion baserat på det steg i kedjan där den bryts (avslås)
Uppdatering :
För att hjälpa framtida tittare av detta inlägg skapade jag denna demo av Plumas svar.
Fråga:
Mitt mål verkar ganska enkelt.
step(1)
.then(function() {
return step(2);
}, function() {
stepError(1);
return $q.reject();
})
.then(function() {
}, function() {
stepError(2);
});
function step(n) {
var deferred = $q.defer();
//fail on step 1
(n === 1) ? deferred.reject() : deferred.resolve();
return deferred.promise;
}
function stepError(n) {
console.log(n);
}
Problemet här är att om jag misslyckas på steg 1, så utlöses både stepError(1) OCH stepError(2). Om jag inte return $q.reject så kommer inte stepError(2) att utlösas, men step(2) kommer att göra det, vilket jag förstår. Jag har åstadkommit allt utom det jag försöker göra.
Hur skriver jag löften så att jag kan anropa en funktion vid avslag, utan att anropa alla funktioner i felkedjan? Eller finns det ett annat sätt att åstadkomma detta?
Här är en live-demo så att du har något att arbeta med.
Uppdatering :
Jag har på sätt och vis löst det. Här fångar jag felet i slutet av kedjan och skickar datan till reject(data) så att jag ska veta vilket problem som ska hanteras i error-funktionen. Detta uppfyller faktiskt inte mina krav eftersom jag inte vill vara beroende av datan. Det skulle vara lite tråkigt, men i mitt fall vore det renare att skicka en felåteranropsfunktion till funktionen istället för att vara beroende av den returnerade datan för att avgöra vad som ska göras.
Live-demo här (klicka).
step(1)
.then(function() {
return step(2);
})
.then(function() {
return step(3);
})
.then(false,
function(x) {
stepError(x);
}
);
function step(n) {
console.log('Step '+n);
var deferred = $q.defer();
(n === 1) ? deferred.reject(n) : deferred.resolve(n);
return deferred.promise;
}
function stepError(n) {
console.log('Error '+n);
}
Svar #1
Anledningen till att din kod inte fungerar som förväntat är att den faktiskt gör något annat än vad du tror att den gör.
Låt oss säga att du har något som följande:
stepOne()
.then(stepTwo, handleErrorOne)
.then(stepThree, handleErrorTwo)
.then(null, handleErrorThree);
För att bättre förstå vad som händer, låt oss låtsas att detta är synkront kod med try/catch-block:
try {
try {
try {
var a = stepOne();
} catch(e1) {
a = handleErrorOne(e1);
}
var b = stepTwo(a);
} catch(e2) {
b = handleErrorTwo(e2);
}
var c = stepThree(b);
} catch(e3) {
c = handleErrorThree(e3);
}
Hantera onRejected (det andra argumentet till then) är i princip en felkorrigeringsmekanism (som ett catch-block). Om ett fel kastas i handleErrorOne, kommer det att fångas av nästa catch-block (catch(e2)), och så vidare.
Detta är uppenbarligen inte vad du avsåg.
Låt oss säga att vi vill att hela upplösningskedjan ska misslyckas oavsett vad som går fel:
stepOne()
.then(function(a) {
return stepTwo(a).then(null, handleErrorTwo);
}, handleErrorOne)
.then(function(b) {
return stepThree(b).then(null, handleErrorThree);
});
Obs: Vi kan lämna handleErrorOne där den är, eftersom den bara kommer att anropas om stepOne avvisar (det är den första funktionen i kedjan, så vi vet att om kedjan avvisas vid denna punkt, kan det bara bero på den funktionen löfte).
Den viktiga förändringen är att felhanterarna för de andra funktionerna inte är en del av den huvudsakliga promise-kedjan. Istället har varje steg sin egen "sub-kedja" med en onRejected som bara anropas om steget avvisades (men kan inte nås direkt av huvudkedjan).
Anledningen till att detta fungerar är att både onFulfilled och onRejected är valfria argument till then-metoden. Om ett löfte uppfylls (d.v.s. löses) och nästa then i kedjan inte har en onFulfilled-hanterare, kommer kedjan att fortsätta tills det finns en med en sådan hanterare.
Detta betyder att de följande två raderna är ekvivalenta:
stepOne().then(stepTwo, handleErrorOne)
stepOne().then(null, handleErrorOne).then(stepTwo)
Men följande rad är inte ekvivalent med de två ovan:
stepOne().then(stepTwo).then(null, handleErrorOne)
Angulars löftebibliotek $q är baserat på kriskowals Q-bibliotek (som har ett rikare API, men innehåller allt du kan hitta i $q). Q:s API-dokumentation på GitHub kan vara användbar. Q implementerar Promises/A+-specifikationen, som går in på detaljer om hur then och löftets upplösningsbeteende fungerar exakt.
REDIGERA :
Kom också ihåg att om du vill bryta kedjan i din felhanterare, måste den returnera ett avvisat löfte eller kasta ett fel (vilket kommer att fångas och omslutas i ett avvisat löfte automatiskt). Om du inte returnerar ett löfte, omsluter then returvärdet i ett löfte som löses åt dig.
Detta innebär att om du inte returnerar något, returnerar du i praktiken ett löst löfte för värdet undefined.
Svar #2
Lite sen till festen men denna enkla lösning fungerade för mig:
function chainError(err) {
return Promise.reject(err)
};
stepOne()
.then(stepTwo, chainError)
.then(stepThreee, chainError);
Detta låter dig bryta dig ur kedjan.
Svar #3
Det du behöver är en upprepande .then()-kedja med ett specialfall för att starta och ett specialfall för att avsluta.
Tricket är att få stegnummret för felläget att sprida sig till en slutlig felhanterare.
- Start: kalla
step(1)utan villkor. Upprepande mönster: kedja en
.then()med följande callback-funktioner:- framgång: anropa steg(n+1)
- misslyckande: kasta värdet som det tidigare uppskjutna blev avvisat med eller kasta om felet.
.then() utan framgångshanterare och med en slutlig felhanterare.Du kan skriva ut hela saken för hand men det är lättare att demonstrera mönstret med namngivna, generaliserade funktioner :
function nextStep(n) {
return step(n + 1);
}
function step(n) {
console.log('step ' + n);
var deferred = $q.defer();
(n === 3) ? deferred.reject(n) : deferred.resolve(n);
return deferred.promise;
}
function stepError(n) {
throw(n);
}
function finalError(n) {
console.log('finalError ' + n);
}
step(1)
.then(nextStep, stepError)
.then(nextStep, stepError)
.then(nextStep, stepError)
.then(nextStep, stepError)
.then(nextStep, stepError)
.then(null, finalError);});
se demo
Observera hur i step() avvisas eller löses den deferred med n, vilket gör det värdet tillgängligt för callback-funktionerna i nästa .then() i kedjan. När stepError anropas, kastas felet om och om igen tills det hanteras av finalError.
