自定义超时的http请求

306 阅读1分钟
  • 需要封装XMLHttpRequest

  • 超时后执行 xhr.abort()

  • 利用Promise.race得到http请求或者超时先回来

  • http请求先返回,那就终止setTimeout

  • 超时先回来,那就终止http请求

// 带有超时的http请求
const isObject = v => v && typeof v == 'object'
const assert = (condition, msg) => {
  if (!condition) throw new Error(msg)
}

function ajax(constroller, { method, url, param, data }) {
  assert(url, 'url must not be empty!')
  function createUrl() {
    if (isObject(param)) {
      param = Object.keys(param)
        .map(key => {
          return `${key}=${param[key]}`
        })
        .join('&')
    }
    if (!param) {
      return url
    }
    url = url.toString().replace(/(\?+)$/g, '')
    return param ? `${url}?${param}` : url
  }
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    url = createUrl()
    xhr.open(method, url, true)
    xhr.onload = function () {
      resolve(xhr.response)
    }
    constroller.abortHttp = () => xhr.abort()
    xhr.onerror = function (error) {
      reject(error)
    }
    xhr.send(data)
  })
}

function timeout(constroller, timestamp = 0) {
  if (timestamp <= 0) return
  return new Promise((_, reject) => {
    const timer = setTimeout(() => {
      reject('timeout')
    }, timestamp)
    constroller.abortTime = () => {
      clearTimeout(timer)
    }
  })
}

function AbortController() {
  return {
    abortHttp: null,
    abortTime: null
  }
}

function timeoutHttp(config) {
  const constroller = new AbortController()
  return Promise.race([
    ajax(constroller, config),
    timeout(constroller, config.timeout)
  ]).then(
    function (res) {
      constroller.abortTime()
      return res
    },
    function (error) {
      if (error == 'timeout') {
        constroller.abortHttp()
      }
      return error
    }
  )
}

// 导出
export { timeoutHttp }