catch error in promise with settimeout

Нужно отловить ошибку из промиса с сеттаймаутом и при этом получить id сеттаймаута что бы можно было его остановить. Код с которым я это пытаюсь сделать

const globalTimeout = async () => {
  let timeoutId; 
  const timeoutPromise = new Promise(() => {
    timeoutId = setTimeout(() => {
      throw new Error('Request timed out');
    }, 4000);
  });
  return {
    timeoutPromise,
    timeoutId,
  };
};

async function loop() {
  const { timeoutPromise, timeoutId } = globalTimeout();
  await timeoutPromise.catch(err => { throw err;});
}

(async() => {
  try {
    await loop();
  } catch (error) {
    console.log('Error:', error);
  }
})();


Ответы (2 шт):

Автор решения: Vadim

Я обычно не использую try{}catch(){} в твоём кейсе, самостоятельно запрашивать еррор - ну такое ?

у setTimeout задаешь количество секундочек - отложенных на выполнение реквеста

С этим способом тебе не нужно будет получать ид таймаута и тд

// true  = data from the request
// false = timeout "error"
const fast_fetch = false;

const fetch = async()=>{
    // fetch placeholder
    return await new Promise((done)=>setTimeout(()=>done('da'), fast_fetch ? 100 : 300));
};

const loopa = async()=>{
    return await new Promise((done)=>{
        let tookLong = true;

        fetch('url').then((r)=>{
            tookLong = false;
            done({ok: true, data: r});
        }).catch((e)=>{
            done({ok: false, data: e});
        });

        setTimeout(()=>{
            if(!tookLong)return false;
            done({ok: false, data: 'Request timed out'});
        }, 200);
    });
};

(async()=>{
    const response = await loopa();
    // if(!response?.ok)throw new Error(response.data);
    console.log('done', response);
})();

→ Ссылка
Автор решения: yar85

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

Естественно, и синхронный код функции промиса всегда выполняется полностью (если не добавлять в него выход по условию какого-то внешнего состояния). В общем, невозможно "снаружи" прервать уже выполняющийся код промиса: мы можем лишь выбирать, нужен ли нам его результат. Здесь выбор делается в зависимости от времени выполнения.

Проще говоря, принцип такой: мы помещаем в асинхронную очередь два промиса (один с полезным кодом; другой с отклонением по таймауту), и try..catch срабатывает по-разному в зависимости от того какой из этих промисов исполнится раньше. Пример ниже - показывает как можно объединить это в один промис:

const globalTimeout = (callback, delay = 4e3) => {
  let timeoutId;
  const promise = new Promise(async (resolve, reject) => {
    timeoutId = setTimeout(() => {
      reject(new Error('Request timed out'));  // отклоняем, по таймауту, в этом случае `resolve` не имеет эффекта
    }, delay);
    const result = await callback();           // ожидаем выполнения асинхронного коллбэка...
    clearTimeout(timeoutId); 
    resolve(result);                           // ...и резолвим результат
  });
  return { promise, timeoutId };
};

function loop() {
  // какие-то действия... (код этой функции - имитация)
  return new Promise(resolve => {
    const isLongExecution = Math.random() < 0.5;
    setTimeout(
      () => resolve('Hello Stack Overflow!'),
      isLongExecution ? 5e3 : 0
    );
  });
}

(async () => {
  try {
    const { promise } = globalTimeout(loop);
    console.log(await promise);
  } catch (err) {
    console.log('Error:', err.message);
  }
})();

В примере сценарий выполнения loop выбирается случайным образом (иногда быстрое, а иногда долгое - во втором случае промис таймаута исполняется раньше).
Конечно же, при "внешней" отмене таймаута через clearTimeout, его промис останется незавершенным (навечно в состоянии pending).

// Не вижу тут причин возвращать значение timeoutId, т.к. логичнее сразу снимать таймаут если коллбэк "успел"... но на всякий случай оставил это значение в возвращаемом объекте.

→ Ссылка