У меня есть fetch-api POST запрос:

fetch(url, {
  method: 'POST',
  body: formData,
  credentials: 'include'
})

Я хочу знать, какой тайм-аут по умолчанию для этого? и как мы можем установить его на определенное значение, например 3 секунды или неопределенные секунды?

Akshay Lokur

Ответов: 11

Ответы (11)

Редактировать 1

Как указано в комментариях, код в исходном ответе продолжает запускать таймер даже после того, как обещание разрешено / отклонено.

Приведенный ниже код устраняет эту проблему.

таймаут функции (мс, обещание) {
  вернуть новое обещание ((разрешить, отклонить) => {
    const timer = setTimeout (() => {
      отклонить (новая ошибка ('TIMEOUT'))
    }, РС)

    обещать
      .then (значение => {
        clearTimeout (таймер)
        решить (значение)
      })
      .catch (причина => {
        clearTimeout (таймер)
        отклонить (причина)
      })
  })
}


Оригинальный ответ

У него нет указанного по умолчанию;в спецификации таймауты вообще не обсуждаются.

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

// Rough implementation. Untested.
function timeout(ms, promise) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      reject(new Error("timeout"))
    }, ms)
    promise.then(resolve, reject)
  })
}

timeout(1000, fetch('/hello')).then(function(response) {
  // process response
}).catch(function(error) {
  // might be a timeout error
})

Как описано в https://github.com/github/fetch/issues/175 Комментарий от https://github.com/mislav

Отредактируйте, если вам нравится еще более чистое решение, обрабатывающее все крайние случаи, выберите этот ответ: https://stackoverflow.com/a/57888548/1059828.

Мне очень нравится чистый подход из этого gist с использованием Promise.race

fetchWithTimeout.js

export default function (url, options, timeout = 7000) {
    return Promise.race([
        fetch(url, options),
        new Promise((_, reject) =>
            setTimeout(() => reject(new Error('timeout')), timeout)
        )
    ]);
}

main.js

import fetch from './fetchWithTimeout'

// call as usual or with timeout as 3rd argument

fetch('http://google.com', options, 5000) // throw after max 5 seconds timeout error
.then((result) => {
    // handle result
})
.catch((e) => {
    // handle errors and timeout error
})
  fetchTimeout (url,options,timeout=3000) {
    return new Promise( (resolve, reject) => {
      fetch(url, options)
      .then(resolve,reject)
      setTimeout(reject,timeout);
    })
  }

Основываясь на Endless 'отлично ответ, я создал полезную служебную функцию.

const fetchTimeout = (url, ms, {signal, ... options} = {}) => {
    const controller = новый AbortController ();
    const Promise = fetch (url, {signal: controller.signal, ... options});
    если (сигнал) signal.addEventListener ("прервать", () => controller.abort ());
    const timeout = setTimeout (() => controller.abort (), мс);
    вернуть обещание.finally (() => clearTimeout (тайм-аут));
};
  1. Если время ожидания истекло до того, как ресурс был извлечен, выборка прерывается.
  2. Если ресурс извлекается до истечения времени ожидания, время ожидания сбрасывается.
  3. Если входной сигнал прерван, выборка прерывается и тайм-аут очищается.
const controller = new AbortController ();

document.querySelector ("button.cancel"). addEventListener ("щелчок", () => controller.abort ());

fetchTimeout ("example.json", 5000, {сигнал: controller.signal})
    .then (ответ => response.json ())
    .then (console.log)
    .catch (error => {
        if (error.name === "AbortError") {
            // выборка прервана либо из-за тайм-аута, либо из-за того, что пользователь нажал кнопку отмены
        } еще {
            // сетевая ошибка или ошибка парсинга json
        }
    });

Надеюсь, что это поможет.

Использование решения для гонки обещаний приведет к тому, что запрос останется зависшим и по-прежнему будет потреблять полосу пропускания в фоновом режиме, а также снизит максимально разрешенный одновременный запрос, выполняемый, пока он еще обрабатывается.

Instead use the AbortController to actually abort the request, Here is an example

const controller = new AbortController()

// 5 second timeout:
const timeoutId = setTimeout(() => controller.abort(), 5000)

fetch(url, { signal: controller.signal }).then(response => {
  // completed request before timeout fired

  // If you only wanted to timeout the request, not the response, add:
  // clearTimeout(timeoutId)
})

AbortController может использоваться и для других целей, не только для выборки, но и для чтения / записи потоков. Более новые функции (особенно основанные на обещаниях) будут использовать это все больше и больше. NodeJS также внедрил AbortController в свои потоки / файловую систему. Я знаю, что веб-bluetooth тоже изучает это. Теперь его также можно использовать с опцией addEventListener, и он перестанет прослушивать, когда сигнал закончится

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

, например,

  function fetchWrapper(url, options, timeout) {
    return new Promise((resolve, reject) => {
      fetch(url, options).then(resolve, reject);

      if (timeout) {
        const e = new Error("Connection timed out");
        setTimeout(reject, timeout, e);
      }
    });
  }

Вот SSCCE с использованием NodeJS, время ожидания которого истекает через 1000 мс:

import fetch from 'node-fetch';

const controller = new AbortController();
const timeout = setTimeout(() => {
    controller.abort();
}, 1000); // will time out after 1000ms

fetch('https://www.yourexample.com', {
    signal: controller.signal,
    method: 'POST',
    body: formData,
    credentials: 'include'
}
)
.then(response => response.json())
.then(json => console.log(json))
.catch(err => {
    if(err.name === 'AbortError') {
        console.log('Timed out');
    }}
)
.finally( () => {
    clearTimeout(timeout);
});

Используя c-prom2 lib, отменяемая выборка с тайм-аутом может выглядеть так (Live jsfiddle demo):

import CPromise from "c-promise2"; // npm package

function fetchWithTimeout(url, {timeout, ...fetchOptions}= {}) {
    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(request=> console.log('done'));
    
// chain.cancel(); - to abort the request before the timeout

Этот код как пакет npm cp-fetch

Вы можете создать обертку timeoutPromise

function timeoutPromise(timeout, err, promise) {
  return new Promise(function(resolve,reject) {
    promise.then(resolve,reject);
    setTimeout(reject.bind(null,err), timeout);
  });
}

Затем вы можете обернуть любое обещание

timeoutPromise(100, new Error('Timed Out!'), fetch(...))
  .then(...)
  .catch(...)  

На самом деле это не отменяет базовое соединение, но позволит вам отсрочить выполнение обещания.
Ссылка

Если вы не настроили тайм-аут в своем коде, это будет тайм-аут запроса по умолчанию для вашего браузера.

1) Firefox - 90 секунд

Введите about: config в поле URL Firefox. Найдите значение, соответствующее ключу network.http.connection-timeout

2) Хром - 300 секунд

Источник

РЕДАКТИРОВАТЬ: запрос на выборку по-прежнему будет выполняться в фоновом режиме и, скорее всего, будет регистрировать ошибку в вашей консоли.

Действительно, подход Promise.race лучше.

См. Ссылку Promise.race ()

Race означает, что все обещания будут выполняться одновременно, и гонка остановится, как только одно из обещаний вернет значение. Следовательно, будет возвращено только одно значение. Вы также можете передать функцию для вызова, если время выборки истекло.

fetchWithTimeout (url, {
  метод: 'POST',
  тело: formData,
  учетные данные: "включить",
}, 5000, () => {/ * здесь что-нибудь делать * /});

Если это вас заинтересует, возможная реализация:

function fetchWithTimeout (url, options, delay, onTimeout) {
  const timer = new Promise ((resolve) => {
    setTimeout (разрешение, задержка, {
      тайм-аут: правда,
    });
  });
  return Promise.race ([
    выборка (URL, параметры),
    таймер
  ]). then (response => {
    if (response.timeout) {
      onTimeout ();
    }
    ответ на ответ;
  });
}

2022 WebDevInsider