我有一个fetch-api POST请求:

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

我想知道这个的默认超时时间是多少?我们如何将它设置为特定的值,比如3秒或不定秒?


当前回答

编辑:取回请求仍然在后台运行,很可能会在控制台中记录一个错误。

的确是应许。种族方法更好。

参见此链接以获取参考。

Race意味着所有promise将同时运行,一旦其中一个promise返回值,竞赛就会停止。 因此,只返回一个值。 如果取回超时,您也可以传递一个函数来调用。

fetchWithTimeout(url, {
  method: 'POST',
  body: formData,
  credentials: 'include',
}, 5000, () => { /* do stuff here */ });

如果这引起了你的兴趣,一个可能的实现将是:

function fetchWithTimeout(url, options, delay, onTimeout) {
  const timer = new Promise((resolve) => {
    setTimeout(resolve, delay, {
      timeout: true,
    });
  });
  return Promise.race([
    fetch(url, options),
    timer
  ]).then(response => {
    if (response.timeout) {
      onTimeout();
    }
    return response;
  });
}

其他回答

更新,因为我最初的答案有点过时,我建议使用像这里实现的中止控制器:https://stackoverflow.com/a/57888548/1059828或看看这个非常好的帖子解释中止控制器与fetch:我如何取消HTTP fetch()请求?

过时的原答案:

我非常喜欢使用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

// throw after max 5 seconds timeout error
fetch('http://google.com', options, 5000) 
.then((result) => {
    // handle result
})
.catch((e) => {
    // handle errors and timeout error
})

下面是一个使用NodeJS的SSCCE,它将在1000ms后超时:

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);
});

编辑1

正如注释中所指出的,原始答案中的代码即使在承诺被解析/拒绝之后也会继续运行计时器。

下面的代码修复了这个问题。

function timeout(ms, promise) {
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => {
      reject(new Error('TIMEOUT'))
    }, ms)

    promise
      .then(value => {
        clearTimeout(timer)
        resolve(value)
      })
      .catch(reason => {
        clearTimeout(timer)
        reject(reason)
      })
  })
}


原来的答案

它没有指定的默认值;该规范根本没有讨论超时。

你可以为promise实现自己的超时包装器:

// 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

您可以创建一个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(...)  

它实际上不会取消底层连接,但允许您超时承诺。 参考

使用c-promise2库,可取消的获取超时可能像这样(Live jsfiddle演示):

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