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

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

  •  
  •   xhyzidane · 2018-11-22 23:55:52 +08:00 · 1490 次点击
    这是一个创建于 1953 天前的主题,其中的信息可能已经有所发展或是发生改变。

    今天遇到一个需求:做请求接口的封装,接口请求失败时,如无特殊处理则执行默认方法 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
    yokyj
        1
    yokyj  
       2018-11-23 14:48:23 +08:00
    //封装方法
    let p = (option) => {
    return new Promise((resolve, reject) => {
    $.ajax({
    url: option.url,
    fail: () => {
    reject( option.fail || defaultFunction);

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

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

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

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

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

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

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

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

    在包装初始的时候, 就注册一个默认的 catch 链条, 处理默认的 catch 事件, 同时保留一个状态变量
    如果被包装的 promise object 的 then 方法被调用, 就清理状态变量, 之前注册的默认 catch 方法不执行
    SoloCompany
        9
    SoloCompany  
       2018-12-17 20:45:33 +08:00
    @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 则总是只能得到第一个结果
    xhyzidane
        10
    xhyzidane  
    OP
       2018-12-18 10:59:02 +08:00
    @SoloCompany #9 感谢大佬,学到了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3594 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 10:29 · PVG 18:29 · LAX 03:29 · JFK 06:29
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.