收藏
回答

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



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

9 个回答

  • 杨赟
    杨赟
    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
    回复
  • 禾店科技
    禾店科技
    2021-05-07

    因为是异步函数,获取服务器数据时会出现几秒至几十秒的延迟,

    在渲染页面的时候如果要用到服务器数据的可能获取不到,

    一个常用的解决方式是所有涉及到服务器数据的变量都要进行初始化,并且页面加载的时候用loading提示


    2021-05-07
    有用 1
    回复
  • 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
      回复
登录 后发表内容