Ab und zu stehe ich vor dem Problem, in JavaScript mehrere Promises in einer festgelegten Reihenfolge ausführen zu müssen.
Anders als bei parallel auszuführendenden Promises (via Promise.all()
) gibt es meines Wissens nach dafür keine Methode am Promise
-Objekt. Diese Funktionalität muss man sich also selbst zusammenbauen.
So habe ich dieses Problem für mich gelöst:
// An example of long running funtion returning a promise.
// for demonstration purposes we just wrap a promise around setTimeout.
function longRunningFunctionReturningAPromise(parameter) {
const taskDuration = Math.floor(Math.random() * 110);
return new Promise((resolve, reject) => {
const calculationResult = Math.sin(parameter * parameter) * 2;
if (parameter > 3) {
// Example for an error.
reject(new Error(`Parameters greater than 3 are not allowed.`));
} else {
setTimeout(() => {
resolve(calculationResult);
}, taskDuration);
}
});
}
// Create a promise sequence from an array of tasks.
function createSequence(tasks) {
let sequenceResults = [];
// The initial sequence Promise object:
// Promise.resolve() returns a Promise. It also is "Thenable" so we
// can append promises at the end of it by using sequence.then(...)
let sequence = Promise.resolve();
// Append all tasks to the promise
// and execute them only after the previous task has finished.
tasks.forEach((task, taskIndex) => {
sequence = sequence
.then(() => task())
.then(taskResult => {
// add the result of the task to the results array
sequenceResults[taskIndex] = taskResult;
});
});
// At the end of the sequence, return the sequence resutlts.
return sequence.then(() => sequenceResults);
}
// Create a list of tasks. Note: we wrap each task in a separate anonymous function.
// This is important so we can invoke the tasks in order at a later point in time
// (instead of them being invoked immediately on creation)
const tasks = [
() => longRunningFunctionReturningAPromise(1),
() => longRunningFunctionReturningAPromise(2),
() => longRunningFunctionReturningAPromise(3),
];
// Start a sequence that completes
createSequence(tasks).then(
sequenceResults => console.log('Sequence completed:', sequenceResults),
error => console.log('Sequence failed:', error)
);
const tasksThatWillFail = [
() => longRunningFunctionReturningAPromise(2),
() => longRunningFunctionReturningAPromise(3),
() => longRunningFunctionReturningAPromise(4),
];
// Start a sequence that will throw an error
createSequence(tasksThatWillFail).then(
sequenceResults => console.log('Sequence completed:', sequenceResults),
error => console.log('Sequence failed:', error)
);
Folgendes war mit bei dieser Lösung wichtig:
- Alle
Promise
s werden in eine anonyme Funktion verpackt, so dass sie erst erst ausgeführt werden, wenn das vorhergehende Promise aufgelöst wurde. - Sobald ein Fehler auftritt und ein
Promise
nicht aufgelöst (resolved
) werden kann, wird die Sequenz angehalten, keine weiterenPromise
s ausgeführt und die aufgetretenen Fehler propagiert.
Hinterlasse einen Kommentar
Auf der eigenen Website geantwortet? Sende eine Webmention!