umi-request:期待增加取消请求的功能

Created on 17 Aug 2019  ·  4Comments  ·  Source: umijs/umi

Background

取消请求 在某些项目中还是有必要的

Proposal

目前我能想到的关于fetch的取消请求的方法有两种:

  1. cancelIdleCallback兼容性差得很
  2. 借助 Promise.race,将abrot_promise函数(reject)挂在请求实例上,然后手动调用abrot即reject

看看方法2的具体实现:(我目前能想到但不优雅的方法)

/**
 * fetch 封装,timeout,cancel;
    ========================
    const p = $get(
      "https://api.apiopen.top/getJoke",
      {
        page: 1,
        count: 2,
        type: "video"
      },
      {
        credentials: "omit",
        headers: {
          "Content-Type": "text/plain"
        }
      }
    );
    console.log(p);
    p.then(res => {
      console.log("res:::", res);
    }).catch(err => {
      console.log("err:::", err);
    });
    // 手动取消请求
    p.cancel();
 */
import { qsStringify } from "./util";

// 默认超时:30s
const defaultTimeout = 30 * 1000;
// 默认url
const defaultURL = "";
// 默认fetch配置
const defaultConfig = {
  credentials: "include",
  mode: "cors",
  cache: "default",
  headers: {
    "Content-Type": "application/json"
  }
};
// 校验status状态码
const checkStatus = (reject, response) => {
  const response2json = response.json();
  if (response.status >= 200 && response.status < 300) {
    console.log("response2json: ", response2json);
    return response2json;
  } else {
    reject(response2json);
  }
};
export const _fetch = (fetch => (
  url,
  { timeout = defaultTimeout, ...rest }
) => {
  console.log(url, { timeout, ...rest });
  // 定义终止函数
  let Abort = null;
  // 可被终止(reject)的promise
  const abort_promise = new Promise((resolve, reject) => {
    Abort = (msg = "canceled.") => {
      reject(msg);
    };
  });
  // 调用超时
  if (timeout && typeof abort === "function") {
    setTimeout(() => {
      Abort(`timeout:${timeout}ms`);
    }, timeout);
  }
  // 业务API的promise
  const fetch_promise = new Promise((resolve, reject) => {
    if (!url.startsWith("http")) {
      url = `${defaultURL}${url}`;
    }
    fetch(url, rest)
      .then(response => checkStatus(reject, response))
      .then(data => {
        // 这里setTimeout用于测试
        setTimeout(() => {
          resolve(data);
        }, 5 * 1000);
        // resolve(data);
      })
      .catch(error => {
        console.log("request fail url:", url);
        console.log("request fail reason:", error);
        reject(error);
      });
  });
  // race:返回最快的结果(resolve/reject)
  const promise = Promise.race([fetch_promise, abort_promise]);
  // console.log(promise)
  // 将终止函数作为结果返回,达到可取手动取消请求的目的
  promise.cancel = Abort;
  return promise;
})(fetch);

export const generateConfig = (method, params, config) => {
  const finalConfig = { ...defaultConfig };
  if (method === "GET") {
    Object.assign(finalConfig, { method }, config);
  } else if (method === "POST" || method === "PUT" || method === "DELETE") {
    const body = JSON.stringify(params);
    Object.assign(finalConfig, { method }, { body }, config);
  } else if (method === "UPLOAD") {
    const formData = new FormData();
    if (!params.length) {
      return { success: false, message: "未选择文件" };
    }
    for (const i of params) {
      const path = i.path;
      const arr = path.split("/");
      const file = {
        uri: path,
        type: "multipart/form-data",
        name: escape(arr[arr.length - 1]),
        fileType: i.mime
      };
      formData.append("file", file);
    }
    Object.assign(
      finalConfig,
      { method: "POST" },
      { headers: { "Content-Type": "multipart/form-data" } },
      { body: formData },
      config
    );
  }
  return finalConfig;
};

export const $get = (url, params = {}, config = {}) => {
  url = `${url}?${qsStringify(params)}`;
  const finalConfig = generateConfig("GET", params, config);
  return _fetch(url, finalConfig);
};

export const $post = (url, params = {}, config = {}) => {
  const finalConfig = generateConfig("POST", params, config);
  return _fetch(url, finalConfig);
};
export const $put = (url, params = {}, config = {}) => {
  const finalConfig = generateConfig("PUT", params, config);
  return _fetch(url, finalConfig);
};
export const $delete = (url, params = {}, config = {}) => {
  const finalConfig = generateConfig("DELETE", params, config);
  return _fetch(url, finalConfig);
};
export const $upload = (url, fileArr = [], config = {}) => {
  const finalConfig = generateConfig("UPLOAD", fileArr, config);
  return _fetch(url, finalConfig);
};

在线测试&查看代码:codesandbox

Additional context

我简单看了一下umi-request源码,我这种方法,不太好加入进去(功力不够,提pr心有余力不足),希望大佬们能增加取消请求的功能🙏。

Most helpful comment

取消请求的能力目前正在 beta 测试中,刚好是你提 issue 的前一天,欢迎提前尝鲜:https://www.npmjs.com/package/umi-request/v/1.2.3-beta.1

All 4 comments

取消请求的能力目前正在 beta 测试中,刚好是你提 issue 的前一天,欢迎提前尝鲜:https://www.npmjs.com/package/umi-request/v/1.2.3-beta.1

取消请求的能力目前正在 beta 测试中,刚好是你提 issue 的前一天,欢迎提前尝鲜:https://www.npmjs.com/package/umi-request/v/1.2.3-beta.1

看了一下和axios,用的是一样的

@chenjsh36 Timeout 异常抛出后 http request 根本没有cancel 还是发送并且成功了

fetch 返回 promise,但是 js 并没有 “中止” promise 的概念
fetch本身没有abrot方法,即使有一个AbortController兼容太差,

不像axios,web端用的XML,node端用的http的request,他们都有abrot方法

Was this page helpful?
0 / 5 - 0 ratings