首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
V2EX  ›  问与答

JavaScript 中这种写法如何用 Promise 实现?

  •  
  •   xhyzidane · 275 天前 · 637 次点击
    这是一个创建于 275 天前的主题,其中的信息可能已经有所发展或是发生改变。

    今天遇到一个需求:做请求接口的封装,接口请求失败时,如无特殊处理则执行默认方法 defaultFunction (如弹窗提示),如有特殊处理则不执行默认方法。 如果用 callback 的写法很容易实现:

    //封装方法
    var ajax = function(option) {
      $.ajax({
        url: option.url,
        fail: function() {
          if(!option.fail || typeof option.fail != 'function') {
            defaultFunction();
          }
        }
      })
    }
    //特殊处理,不执行默认     
    ajax({
      url: 'a.url',
      fail: function() {
        console.log('error');
      }
    })
    //无特殊处理,执行默认        
    ajax({
      url: 'a.url',
    })
    

    但是现在项目中的封装是基于 Promise 的,如下:

    //封装方法
    let p = (option) => {
      return new Promise((resolve, reject) => {
        $.ajax({
          url: option.url,
          fail: () => {
            reject();
            defaultFunction();
          }
        })
      });
    }
    //调用
    p({url: 'a.url'}).catch(() => {
      //...
    })
    

    求教:基于 Promise 是否可以实现类似上面「在调用处决定是否执行默认方法」的写法?

    10 回复  |  直到 2018-12-18 10:59:02 +08:00
        1
    yokyj   274 天前
    //封装方法
    let p = (option) => {
    return new Promise((resolve, reject) => {
    $.ajax({
    url: option.url,
    fail: () => {
    reject( option.fail || defaultFunction);

    }
    })
    });
    }
    //调用
    p({url: 'a.url'}).catch( cb => cb() )
        2
    xhyzidane   274 天前
    @yokyj #1 感谢,你这个方法很好。
    但是我已经采纳了另一种方案了:把是否执行默认行为作为一个标识在参数中传递。更适合我手上项目的实际需求。
    可能是我的示例代码不好,其实现有封装的意思是:参数中并不传任何回调函数,而是 fail 时 reject,在 catch 中捕获。
        3
    SoloCompany   274 天前
    我们的选择是做了一个 promise 扩展框架

    框架允许在 promise 上定义一个 uncaught 方法, 如果 promise 运行中的异常没有被捕获, 将会调用 uncaught 方法

    比如
    EPromise.reject(1)
    // 输出 Uncaught (in promise): 1

    EPromise.reject(1).catch(noop)
    // 不输出任何内容

    window.addEventListener(“ rejectionhandled ”) 可以有类似的作用, 但首先这个事件只有 chrome 支持, 其次只能是全局的, 不能对不同的 promise 实例使用不同的处理
        4
    hoyixi   252 天前
    $.ajax({...})本身就实现了 Promise 接口,可以直接 then:

    $.ajax({...}).then(...);

    $.ajax({...}).then(...).catch(...);
        5
    xhyzidane   251 天前
    @SoloCompany #3 你这个应该算是最符合场景的解决办法了,但是自己维护一套 promise 框架的代价太高了。我看了 bluebirdjs 里面有类似的实现,但好像也是全局的
        6
    SoloCompany   251 天前
    @xhyzidane 不是实现 promise 啊,只是对 promise 扩展包装一下, 两百行代码就够了
        7
    xhyzidane   250 天前
    @SoloCompany #6 求教具体如何实现,我查到了一个实现方式 [https://github.com/rtsao/browser-unhandled-rejection]( https://github.com/rtsao/browser-unhandled-rejection) ,不知道是不是类似的
        8
    SoloCompany   250 天前
    @xhyzidane #7 大致的方案是使用包装和继承

    对原始的 promise object 包装成一个新的 promise object, 并覆盖其 then 方法, 返回同样的经过包装的 promise object

    在包装初始的时候, 就注册一个默认的 catch 链条, 处理默认的 catch 事件, 同时保留一个状态变量
    如果被包装的 promise object 的 then 方法被调用, 就清理状态变量, 之前注册的默认 catch 方法不执行
        9
    SoloCompany   250 天前
    @xhyzidane #7 看了下 https://github.com/rtsao/browser-unhandled-rejection/blob/master/src/promise.js

    思想上应该是差不多的,只不过我做了更多的扩展,所以代码会多一些
    1. 兼容 ES3, 也就是说不使用 es6 的 class 扩展
    2. 封装, 不暴露内部状态 (_hasDownstreams)
    3. 扩展支持类似与 jquery 的 deferred 的用法支持多个参数
    比如 EPromise.resolve(1,2).then(console.log) 能够输出 1 2
    当然这种扩展有最大的局限性在于如果使用 async / await 则总是只能得到第一个结果
        10
    xhyzidane   249 天前
    @SoloCompany #9 感谢大佬,学到了
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   872 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 24ms · UTC 23:00 · PVG 07:00 · LAX 16:00 · JFK 19:00
    ♥ Do have faith in what you're doing.