Retour au Lab

Délai Artificiel

Une meilleure expérience en simulant des délais supplémentaires lors des intéractions.
Durée de l'action : 100ms
Avec délai

Durée réelle: ...

...

Sans délai

Durée réelle: ...

...

Durée de l'action : 400ms
Avec délai

Durée réelle: ...

...

Sans délai

Durée réelle: ...

...

Durée de l'action : 3000ms
Avec délai

Durée réelle: ...

...

Sans délai

Durée réelle: ...

...

Délai artificiel: 800ms

Contexte

On cherche à améliorer l'expérience utilisateur en simulant des délais supplémentaires lors des intéractions.

Solution

On peut utiliser une fonction artificialDelay qui prend en paramètre une promesse et un délai en millisecondes. Voici la version minimale de la fonction qui permet de résoudre la promesse en attendant au minimum le délai spécifié.

const DEFAULT_DELAY = 800;

async function artificialDelay<T>(promise: Promise<T>, ms = DEFAULT_DELAY) {
	const delay = new Promise((resolve) => setTimeout(resolve, ms));
	const [p] = await Promise.all([promise, delay]);

	return p;
}

Exemple d'utilisation

import { artificialDelay } from '@/utils';

async function fetchUser() {
	return fetch('/api/user');
}

async function main() {
	// ...
	const user = await artificialDelay(fetchUser(), 1000);
	// ...
}

Amélioration, Cas spécifique

On peut améliorer la fonction pour retourner un objet contenant le résultat de la promesse et un flag indiquant si la promesse a été retardée ou non. Cette version n'est pas forcément utile, mais pour des besoins de traçabilité, elle peut l'être. (comme le playground au-dessus par exemple)

const DEFAULT_DELAY = 800;

async function enhancedArtificialDelay<T>(
	promise: Promise<T>,
	ms: number = DEFAULT_DELAY,
) {
	let hasBeenDelayed = false;

	const delayPromise = new Promise((resolve) => {
		setTimeout(() => {
			hasBeenDelayed = true;
			resolve(null);
		}, ms);
	});

	const result = await Promise.race([promise, delayPromise]);

	// `hasBeenDelayed: true` veut dire que la promesse n'est pas encore résolue alors => on l'attend

	if (hasBeenDelayed) {
		return { delayed: false, result: await promise };
	} else {
		// `hasBeenDelayed: false` veut dire que la promesse est déjà résolue => on attend la fin du délai
		// On retourne le résultat de la course car c'est la promesse qui a été résolue en premier
		await delayPromise;
		return { delayed: true, result };
	}
}

Exemple d'utilisation

import { enhancedArtificialDelay } from '@/utils';

async function fetchUser() {
	return fetch('/api/user');
}

async function main() {
	// ...
	const { delayed, result } = await enhancedArtificialDelay(fetchUser(), 1000);
	// ...
}