Javascript Promises in Reihe ausführen

Veröffentlicht von am

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 Promises 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 weiteren Promises ausgeführt und die aufgetretenen Fehler propagiert.

Hinterlasse einen Kommentar

Verfügbare Formatierungen

Benutze Markdown-Befehle oder ihre HTML-Äquivalente, um deinen Kommentar zu formatieren:

Textauszeichnungen
*kursiv*, **fett**, ~~durchgestrichen~~, `Code` und <mark>markierter Text</mark>.
Listen
- Listenpunkt 1
- Listenpunkt 1
1. Nummerierte Liste 1
2. Nummerierte Liste 2
Zitate
> Zitierter Text
Code-Blöcke
```
// Ein einfacher Code-Block
```
```php
// Etwas PHP-Code
phpinfo();
```
Verlinkungen
[Link-Text](https://example.com)
Vollständige URLs werden automatisch in Links umgewandelt.

Auf der eigenen Website geantwortet? Sende eine Webmention!