Есть цепочка обещаний, которая зациклена. Нужно в любой момент извне цепочки прервать её выполнение (вызовом функции "остановить"). Сделал на 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() и т.д. придется заворачивать в этот самопальный класс, что тоже не радует.