@CoolCmd

Отмена выполнения цепочки обещаний (promises). Можно проще?

Есть цепочка обещаний, которая зациклена. Нужно в любой момент извне цепочки прервать её выполнение (вызовом функции "остановить"). Сделал на AbortController:
function создатьОшибкуДляОтменыОбещания()
{
	const e = new Error('The operation was aborted');
	e.name = 'AbortError';
	return e;
}

function проверитьОтменуОбещания(signal)
{
	if (signal.aborted)
	{
		throw создатьОшибкуДляОтменыОбещания();
	}
}

let отменаОбещания = null;

function запустить()
{
	console.assert(отменаОбещания === null);
	отменаОбещания = new AbortController();
	повторить(отменаОбещания.signal);
}

function остановить()
{
	if (отменаОбещания)
	{
		отменаОбещания.abort();
		отменаОбещания = null;
	}
}

function повторить(signal)
{
	fetch('url1', {signal})
	.then(response => response.json())
	.then(
		data =>
		{
			проверитьОтменуОбещания(signal);
			//...
		},
		e =>
		{
			проверитьОтменуОбещания(signal);
			//...
		}
	)
	.then(() => асинхроннаяОперация(signal, 1000))
	.then(() =>
	{
		проверитьОтменуОбещания(signal);
		повторить(signal);
	})
	.catch(e =>
	{
		if (!signal.aborted)
		{
			console.error();
		}
	});
}

function асинхроннаяОперация(signal, ms)
{
	if (signal.aborted)
	{
		return Promise.reject(создатьОшибкуДляОтменыОбещания());
	}
	return new Promise((resolve, reject) =>
	{
		signal.addEventListener('abort', onAbort);
		const idTimeout = setTimeout(() =>
		{
			signal.removeEventListener('abort', onAbort);
			resolve();
		}, ms);
		function onAbort()
		{
			clearTimeout(idTimeout);
			signal.removeEventListener('abort', onAbort);
			reject(создатьОшибкуДляОтменыОбещания());
		}
	});
}

запустить();
остановить();
запустить();

Смущает, что везде нужно добавлять проверитьОтменуОбещания(). Можно как-то проще?

Я видел пару решений, в которых наследовали новый класс от Promise, но там вместо AbortController свой способ уведомления. Кроме того обещания, которые возвращают fetch(), play() и т.д. придется заворачивать в этот самопальный класс, что тоже не радует.
  • Вопрос задан
  • 533 просмотра
Пригласить эксперта
Ответы на вопрос 2
IvanBlacky
@IvanBlacky
back-end разработчик
Можно объявить внешнюю переменную, которая будет флагом, указывающим на то, должна ли цепочка продолжаться (true/false). В цепочке промисов последним звеном вызывать функцию, которая будет вызывать или не будет вызывать цепочку заново, основываясь на значении переменной
Ответ написан
DIITHiTech
@DIITHiTech
Fullstack javascript developer
Использовать внешнюю библиотеку, с которой можно закрывать цепочки обещаний в любом месте.
Простой пример
Усложненный мониторингом прогресса
import CPromise from "c-promise2";

// прерываемый fetch с таймаутом
function fetchWithTimeout(url, options) {
    const {timeout, ...fetchOptions}= options;
    return new CPromise((resolve, reject, {signal}) => {
        fetch(url, {...fetchOptions, signal}).then(resolve, reject)
    }, timeout)
}
        
const chain = fetchWithTimeout("https://run.mocky.io/v3/753aa609-65ae-4109-8f83-9cfe365290f0?mocky-delay=10s", {timeout: 5000})
.then(response => response.json())
.then(response => console.log('Done:', response ));
    
// chain.cancel(); - прервать цепочку промисов и отменить запрос в любое время
// вернет false, если прервать не удалось (в случае, если цепочка была уже выполнена)
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы
26 апр. 2024, в 07:47
2000 руб./за проект
26 апр. 2024, в 06:46
1000 руб./в час
26 апр. 2024, в 05:31
1000 руб./за проект