Как упорядочить пользовательские события, которые вызывают некоторую тяжелую асинхронную обработку - PullRequest
0 голосов
/ 20 марта 2019

Я ищу шаблон для решения типичной проблемы, когда несколько (пользовательских) событий запускаются вскоре после друг друга, и каждое событие должно заботиться о некоторой большой нагрузке, которая должна быть обработана до конца перед новым событием.может быть принят.

Типичным вариантом использования может быть, например, список элементов, по которым пользователь может щелкнуть, и для каждого элемента требуется некоторая асинхронная загрузка и обработка.Пользователь нажимает на элемент, который запускает асинхронную обработку.Когда событие обрабатывается, новая обработка не должна начаться, когда выбран другой элемент, и в лучшем случае (производительность) должна обрабатываться только последнее событие.

Следующий фрагмент кода имитирует сценарий использования ивывод консоли показывает, как событие возникает, а затем обрабатывается.Функция run моделирует (пользовательские) события, а функция heavyLifting моделирует некоторую асинхронную тяжелую атлетику.Обрабатывается больше событий, чем необходимо, и, убедившись, что функция heavyLifting вызывается только последовательно с использованием семафора currentlyLifting, также больше не гарантируется, что по крайней мере самое последнее событие всегда обрабатывается.

Используйте JSFiddle для запуска отсканированного кода

const getRandom = (min, max) => Math.floor(Math.random() * (max - min) + min);

const heavyLifting = id => {
return new Promise(resolve => {
	const cost = getRandom(500, 750)
	console.log(`heavy lifting "${id}" with cost "${cost}" started...`);
	setTimeout(() => {
		console.log(`heavy lifting "${id}" completed.`);
		resolve();
	}, cost);
});
};

let currentlyLifting;
window.addEventListener('heavyLift', async (e) => {
const id = e.detail.id;
if (currentlyLifting !== undefined) {
	console.log(`cannot start new heavy lifting of "${id}" while still running "${currentlyLifting}"`);
} else {
	currentlyLifting = id;
	await heavyLifting(id);
	currentlyLifting = undefined;
}
});

const run = () => {
let id = 1;
const timerCallback = () => {
	console.log(`dispatchEvent "${id}"`);
	window.dispatchEvent(new CustomEvent('heavyLift', {detail: {id}}));
	if (id++ < 10) {
		setTimeout(timerCallback, 100);
	}
};
timerCallback();
};

run();
<!DOCTYPE html>
<html>
	<body>
		<script src="index.js"></script>
	</body>
</html>

1 Ответ

0 голосов
/ 20 марта 2019

Вы можете дождаться предыдущего обещания обработки перед выполнением следующей обработки:

 const oneAtATime = () => {
   let previous = Promise.resolve();
   return action => previous = previous.then(action);
 };

const lifting = oneAtATime();

lifting(async function() {
 //...
});

 lifting(async function() {
  //...
 });

Или дождаться выполнения всех текущих действий, просто

 await lifting(); // don't do that in a lifting task itself, that will cause a livelock
...