decorator/compatible.js

/**
 * 修饰器,实现各种兼容用法
 * @module compatible
 */

/**
 * 提供微信api形式的回调
 * 主要适用场景:将微信api改写为promise形式后,兼容旧代码
 * 被修饰函数应该返回一个promise,成功时resolve,失败时reject,或返回{succeeded: true/false, ...}格式,通过succeeded字段标识成功失败
 * 修饰后的函数会支持arguments[0]中传入success、fail、complete属性,并根据promise结果进行回调
 * @param target
 * @param funcName
 * @param descriptor
 * 
 * @example
 * class Demo {
 *   \@supportWXCallback //自动支持success、fail、complete回调
 *   async getSystemInfo(){
 *     let sysInfo = {};//自定义getSystemInfo实现,比如加入一些缓存策略,添加一些额外字段等
 *     return {
 *       succeeded: true, //标示应该触发success回调还是fail回调
 *       ...sysInfo, //返回成功/失败对应数据
 *     }
 *   }
 *   
 *   test(){
 *     this.getSystemInfo().then(sysInfo=>{
 *       //正常以Promise形式使用
 *     });
 *     
 *     this.getSystemInfo({
 *       success(sysInfo){
 *         //同时,自动兼容回调形式使用
 *       }
 *     })
 *   }
 * }
 */
export function supportWXCallback(target, funcName, descriptor) {
  let oriFunc = descriptor.value;
  descriptor.value = function (...args) {
    //获取回调函数
    let options = args[0] || {};
    let {success, fail, complete} = options;
    
    //清除回调,避免函数体和修饰器重复处理
    delete options.success;
    delete options.fail;
    delete options.complete;
    
    //获取执行结果
    let fetchRes = oriFunc.apply(this, args);

    //格式检查
    if (!(fetchRes instanceof Promise)) {
      console.error('[supportWXCallback] 被修饰函数返回结果应为Promise,函数:', funcName, '返回值:', fetchRes);
      return fetchRes;
    }

    //触发回调
    fetchRes.then((...results)=>{
      //恢复回调配置,尽量减小对入参原始对象的影响
      options.success = success;
      options.fail = fail;
      options.complete = complete;
      
      //判断应该按成功回调还是按失败回调
      let succeeded = results[0] && typeof results[0].succeeded === "boolean" ? results[0].succeeded : true;
      
      //回调
      if (succeeded) {
        success && success(...results);
      } else {
        fail && fail(...results);
      }
      
      complete && complete(...results);
    });
    fetchRes.catch((e)=>{
      //恢复回调配置,尽量减小对入参原始对象的影响
      options.success = success;
      options.fail = fail;
      options.complete = complete;
      
      //回调
      let res = {
        errMsg: (e instanceof Error) ? e.message : 
                (e && e.errMsg) ? e.errMsg : 
                (typeof e ==="string") ? e : 
                'fail'
      };
      fail && fail(res);
      complete && complete(res);
    });

    //保留promise调用形式
    return fetchRes;
  }
}