收藏
回答

wx.request 经 Promise 封装后,如何拿到requestTask


大家会用 promise 将 wx.request 包装一层。但经过这么一层包装后,就拿到不到 requestTask,从而调用不了 abort 方法。大家都是如何解决的? 代码来自:https://www.kancloud.cn/xiaoyulive/wechat/526990

class Request {
  constructor (parms) {
    this.withBaseURL = parms.withBaseURL
    this.baseURL = parms.baseURL
  }
  get (url, data) {
    return this.request('GET', url, data)
  }
  post (url, data) {
    return this.request('POST', url, data)
  }
  put (url, data) {
    return this.request('PUT', url, data)
  }
  request (method, url, data) {
    const vm = this
    return new Promise((resolve, reject) => {
      wx.request({
        url: vm.withBaseURL ? vm.baseURL + url : url,
        data,
        method,
        success (res) {
          resolve(res)
        },
        fail () {
          reject({
            msg: '请求失败',
            url: vm.withBaseURL ? vm.baseURL + url : url,
            method,
            data
          })
        }
      })
    })
  }
}
 
const request = new Request({
  baseURL: 'http://test',
  withBaseURL: true
})
 
module.exports = request



回答关注问题邀请回答
收藏

8 个回答

  • 杨赟
    杨赟
    2020-09-11
    function request(opt) {
      return new Promise((success, fail) => {
        const requestTask = wx.request({
          ...opt,
          success,
          fail,
        });
        opt.signal && (opt.signal.onabort = () => requestTask.abort())
      })
    }
    
    // usage
    const signal = {};
    request({
      url: "localhost",
      signal: signal
    }).then(response => console.debug(response)).catch(error => console.error(error));
    signal.onabort();
    

    设计思路来源于 fetch AbortSignal

    由于小程序没有事件系统🙄,使用onabort粗暴处理了。可根据需要自己编写事件系统使程序更加贴合web规范。

    2020-09-11
    有用 6
    回复
  • 晓初
    晓初
    2019-12-02

    function r() {

      let task = {}
      const promise = new Promise((resolve) => {
          let timer
          task.abort = function () {
            clearTimeout(timer)
          }
     
          timer = setTimeout(() => {
            resolve()
          }, 1000)
        })
     
     
      promise.task = task
      return promise
    }
     
     
    function a() {
      return r()
    }
     
    var promise = a()
     
    promise.then(res => {
      console.log(1)
    })
     
    promise.task.abort()
     
     
    a().then(res => {
      console.log(2)
    })

    demo实现思路,挂载在返回的promise上,但会有问题,你可以看到在调用的时候需要手动声明一个promise变量,不能直接a().then() 因为then返回的是一个新的Promise,上面没有挂载的task。

    今天遇到了这个问题,在不影响从前的代码的情况下想出来的。更好的思路暂时没有。这里就抛砖引玉了

    2019-12-02
    有用 2
    回复
  • DL
    DL
    2019-12-10
    class Request {
    constructor(parms) {
    this.withBaseURL = parms.withBaseURL;
    this.baseURL = parms.baseURL;
    this.requestTask = null;
    }

    get(url, data) {
    return this.request('GET', url, data);
    }

    post(url, data) {
    return this.request('POST', url, data);
    }

    put(url, data) {
    return this.request('PUT', url, data);
    }

    request(method, url, data) {
    const vm = this;
    return new Promise((resolve, reject) => {
    this.requestTask = wx.request({
    url: vm.withBaseURL ? vm.baseURL + url : url,
    data,
    method,
    success(res) {
    resolve(res);
    },
    fail() {
    reject({
    msg: '求失',
    url: vm.withBaseURL ? vm.baseURL + url : url,
    method,
    data
    });
    }
    });
    });
    }

    abort() {
    if (this.requestTask) {
    this.requestTask.abort();
    }
    }
    }

    const request = new Request({
    baseURL: 'http://test',
    withBaseURL: true
    });

    module.exports = request;

    --------

    调用:

    request.get(xxx, xxx).then((res) => {
      request.abort();
     })

    2019-12-10
    有用 1
    回复 2
    • ice breaker
      ice breaker
      2019-12-16
      这个封装有一些问题,导出是单例意味着api共享一个Request实例,出现并发时,requestTask 会被多次覆盖,正常使用必须要new多个Request实例,不然会导致abort不知道取消哪一个task.另外一个问题则是没有考虑小程序请求的并发量,需要接入并发队列来管理,这时候task的处理方式。
      2019-12-16
      5
      回复
    • 深蓝
      深蓝
      2020-03-11
      直接把Request类导出就好了,用的时候就去new一个新的对象,自己管理自己的task。
      2020-03-11
      回复
  • Thomas Trainset 🤪
    Thomas Trainset 🤪
    2021-07-23

    https://github.com/xdoer/PreQuest/blob/main/packages/miniprogram/src/adapter.ts

    可以看看这个源码,采用了 axios 的 cancel-token 的思想。

    function request(url, opt){
      const { getNativeRequestInstance } = opt
      return new Promise(() => {
          let resolvePromise: any
          let promise = new Promise(resolve => (resolvePromise = resolve))
          getNativeRequestInstance(promise)
    
          const instance = wx.request({
             // ... 参数
          })
    
          resolvePromise(instance)
      })
    }
    


    用法如下:

    const req = await request('/api', {
       getNativeRequestInstance(promise) {
         promise.then(instance => {
           instance.abort()
         })
       }
    })
    
    2021-07-23
    有用
    回复 1
    • Lichimedes
      Lichimedes
      2021-09-10
      你好,请你把调用过程补充详细一点,如,如果终止请求,如何处理wx.request成功或失败的数据。
      2021-09-10
      回复
  • 邵小平
    邵小平
    2021-07-08

    挖坟看到这了,有一个思路是返回两个值。 如:

    let(asyncTask,asyncController) = request(...);

    asyncTask.then(res=>...);

    asyncController.abort();

    ===

    代码开源在这,有增强的朋友可以在增强

    https://git.weixin.qq.com/jiji-opensouce/wx-xcx-empty-template/blob/master/utils/restapi.js

    function request(params{
      const { url, method, header, data } = params
      let taskController;
      let task = new Promise((resolve, reject) => {
        taskController = wx.request({
          url,
          header,
          data,
          method,
          success(res) {
            resolve(res)
          },
          fail(err) {
            reject({
              msg'请求失败',
              url,
              method,
              data,
              header,
              err,
            })
          }
        })
      });
      return { task, taskController };
    }
    
    // 使用 示例
     request({ url"http://www.qq.com"method"GET" }).task.then(res=> console.log(res));
    
    request({ url"http://www.qq.com"method"GET" }).taskController.abort();
    
    2021-07-08
    有用
    回复 2
    • Thomas Trainset 🤪
      Thomas Trainset 🤪
      2021-07-23
      task 是异步的, taskController 是同步的,所以你这个封装,用下面的例子会报错
      2021-07-23
      回复
    • 邵小平
      邵小平
      2021-09-08回复Thomas Trainset 🤪
      不明白问题是啥。 就是要用taskcontroller去提早结束异步的任务啊
      2021-09-08
      回复
  • 蓝星
    蓝星
    2021-01-08
    /**
     * 微信wx.Request 请求接口的附加值
     */
    export interface PromiseWxResponse<T> extends Promise<T> {
      /**
       * Response对象
       */
      wxResult?: WechatMiniprogram.RequestSuccessCallbackResult,
      /**
       * 微信wx.Request 的实例
       */
      wxRequestTask?:WechatMiniprogram.RequestTask
    }
    


    /**
     * 统一的HTTP请求入口
     * @param options {string | RequestOption} url 接口地址 或者 RequestOption 对象
     * @param needLoginCallback 登录拦截的callback方法。当接口数据返回成功、且code是401时,将执行needLoginCallback
     * @returns {PromiseWxResponse<*>
     */
    export const request = (options: RequestWxOption): PromiseWxResponse<any> => {
    
    
    
      const promise: PromiseWxResponse<any> = new Promise((resolve, reject) => {
    
        const requestTask:WechatMiniprogram.RequestTask = wx.request(<WechatMiniprogram.RequestOption>{
          url: options.url,
          method: options.method,
          data: options.data,
          header: options.header,
          success: (result: WechatMiniprogram.RequestSuccessCallbackResult) => {
    
            promise.wxRequestTask = requestTask;
            promise.wxResult = result;
    
            resolve(data);e {
              
            }
          },
          fail: (result: WechatMiniprogram.GeneralCallbackResult) => {
            promise.wxRequestTask = requestTask;
            reject(result);
          }
        });
      });
      return promise;
    };
    


    2021-01-08
    有用
    回复
  • 潘家全
    潘家全
    2020-12-28

    个人感觉最好不要对返回的Promise做修改不然下游的流程处理都会变得繁琐起来

    可以借助web规范里的AbortSignal,或着更简单方法是在入口参数加个配置项:

    onCancel?: (cancel: () => void) => void
    

    

    2020-12-28
    有用
    回复 1
    • Lichimedes
      Lichimedes
      2021-09-10
      AbortSignal是一个实验中的功能,小程序支持吗?能不能写一个具体的示例呢?
      2021-09-10
      回复
  • 小黑
    小黑
    2019-12-10

    ts源码:


     
    export namespace Hz.Http {
     
      const ES6Promise = require('es6-promise');
     
      export interface DatasObject {
        [data: string]: number | string | string[] | undefined;
      }
     
      export interface HeadersObject {
        [header: string]: number | string | string[] | undefined;
      }
     
      export interface RequestParams {
        url: string;
        method?: string;
        dataType?: string;
        data?: string | DatasObject | ArrayBuffer;
        header?: HeadersObject;
        responseType?: string;
      }
     
      export class HttpError extends Error {
     
        public statusCode: number;
        public header: HeadersObject;
        public body: string | object | ArrayBuffer;
     
        constructor(statusCode: number, header: HeadersObject, body: string | object | ArrayBuffer) {
          super(`response status code: ${statusCode}`);
          this.statusCode = statusCode;
          this.header = header;
          this.body = body;
        }
      }
     
      export class ResponsePromise<T> extends ES6Promise<T> {
     
        public statusCode: number;
        public header: HeadersObject;
        public body: string | object | ArrayBuffer;
     
        public requestTask: WechatMiniprogram.RequestTask;
     
        constructor(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void) {
          super(executor);
        }
     
        public onHeadersReceived(callback: WechatMiniprogram.RequestTaskOnHeadersReceivedCallback) {
          this.requestTask.onHeadersReceived(callback);
        }
     
        public offHeadersReceived(callback: WechatMiniprogram.RequestTaskOffHeadersReceivedCallback) {
          this.requestTask.offHeadersReceived(callback);
        }
     
        public abort(): void {
          this.requestTask.abort();
        }
      }
     
      export class Request {
     
        public static ExecAsync<T>(pars: RequestParams): ResponsePromise<T> {
     
          var requestTask: WechatMiniprogram.RequestTask;
     
          var p = new ResponsePromise<T>((resolve, reject) => {
     
            try {
              requestTask = wx.request({
                url: pars.url,
                data: pars.data,
                header: pars.header,
                method: pars.method,
                dataType: pars.dataType,
                responseType: pars.responseType,
                success(res) {
     
                  if (res.statusCode >= 200 && res.statusCode < 300) {
     
                    p.statusCode = res.statusCode;
                    p.header = res.header;
                    p.body = res.data;
     
                    resolve(res.data);
                  }
                  else {
                    reject(new HttpError(res.statusCode, res.header, res.data));
                  }
     
                },
                fail(err) {
                  reject(new Error(err.errMsg));
                }
              });
            }
            catch (e) {
              reject(e);
            }
     
          });
     
          p.requestTask = requestTask;
          return p;
        }
     
        public static getAsync<T>(url: string, data?: string | DatasObject | ArrayBuffer, header?: HeadersObject): ResponsePromise<T> {
          return Request.ExecAsync<T>({ url: url, data: data, header: header, method: "GET" });
        }
     
        public static postAsync<T>(url: string, data?: string | DatasObject | ArrayBuffer, header?: HeadersObject): ResponsePromise<T> {
          return Request.ExecAsync<T>({ url: url, data: data, header: header, method: "POST" });
        }
     
        public static deleteAsync<T>(url: string, data?: string | DatasObject | ArrayBuffer, header?: HeadersObject): ResponsePromise<T> {
          return Request.ExecAsync<T>({ url: url, data: data, header: header, method: "DELETE" });
        }
      }
    }


    2019-12-10
    有用
    回复 3
    • 小黑
      小黑
      2019-12-10
      2019-12-10
      回复
    • 小黑
      小黑
      2019-12-10
      小补丁,修复 .then().catch() 返回对象丢失信息问题。
      2019-12-10
      回复
    • 伟
      2020-01-17
      大佬,我试了不行啊,不加await 调用得到的是Promise {}
      2020-01-17
      回复
登录 后发表内容