francecil / leetcode

solutions about leetcode
MIT License
9 stars 1 forks source link

给 fetch 增加超时功能 #31

Closed francecil closed 4 years ago

francecil commented 4 years ago
function mFetch(url,options,timeout){
  //返回一个 promise
}

到达超时时间 fetch 还没有返回响应,则中断请求并 reject promise

提示:需要用到 Promise.raceAbortController

francecil commented 4 years ago

超时设置

将超时任务 setTimeout 包装成一个 promise , fetch 自身也是一个 promise

利用 Promise.race 的特性 -- "迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝" 解决

function mFetch (url, options, timeout) {
  let genTimeoutPromise = (timeout) => new Promise((resolve, reject) => {
    setTimeout(reject, timeout)
  })
  return Promise.race([genTimeoutPromise(timeout), fetch(url, options)])
}

在控制台中测试一下, network 调至 Slow 3G

注意有跨域限制,需要在 baidu 页面的控制台输入测试

mFetch("https://www.baidu.com/", {}, 1000).then((res) => {
console.log(res)
}).catch(e => {
console.log("e:", e)
})

可以看到结果输出了 e: undefined

对错误做个包装

reject(new Response("timeout", { status: 504, statusText: "timeout " }))

中断请求

上述做法,超时后不会处理响应,但是请求还是会继续完成的。

这里需要加个中断功能。

利用 FetchController 实现

function mFetch (url, options, timeout) {
  let controller = new AbortController();
  let signal = controller.signal;
  let genTimeoutPromise = (timeout) => new Promise((resolve, reject) => {
    setTimeout(() => {
      controller.abort();
      reject(new Response("timeout", { status: 504, statusText: "timeout " }))
    }, timeout)
  })
  return Promise.race([genTimeoutPromise(timeout), fetch(url, { ...options, signal })])
}
mFetch("https://www.baidu.com/", {}, 1000).then((res) => {
  console.log(res)
}).catch(e => {
  console.log("e:", e)
})

通过 network 面板可以看到超时时请求 cancel 了