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

2018-11-22 23:55:52 +08:00
 xhyzidane

今天遇到一个需求:做请求接口的封装,接口请求失败时,如无特殊处理则执行默认方法 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 是否可以实现类似上面「在调用处决定是否执行默认方法」的写法?

1518 次点击
所在节点    问与答
10 条回复
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
2018-11-23 15:01:02 +08:00
@yokyj #1 感谢,你这个方法很好。
但是我已经采纳了另一种方案了:把是否执行默认行为作为一个标识在参数中传递。更适合我手上项目的实际需求。
可能是我的示例代码不好,其实现有封装的意思是:参数中并不传任何回调函数,而是 fail 时 reject,在 catch 中捕获。
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
2018-12-15 23:32:00 +08:00
$.ajax({...})本身就实现了 Promise 接口,可以直接 then:

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

$.ajax({...}).then(...).catch(...);
xhyzidane
2018-12-16 23:46:26 +08:00
@SoloCompany #3 你这个应该算是最符合场景的解决办法了,但是自己维护一套 promise 框架的代价太高了。我看了 bluebirdjs 里面有类似的实现,但好像也是全局的
SoloCompany
2018-12-16 23:49:58 +08:00
@xhyzidane 不是实现 promise 啊,只是对 promise 扩展包装一下, 两百行代码就够了
xhyzidane
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
2018-12-17 20:38:18 +08:00
@xhyzidane #7 大致的方案是使用包装和继承

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

在包装初始的时候, 就注册一个默认的 catch 链条, 处理默认的 catch 事件, 同时保留一个状态变量
如果被包装的 promise object 的 then 方法被调用, 就清理状态变量, 之前注册的默认 catch 方法不执行
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
2018-12-18 10:59:02 +08:00
@SoloCompany #9 感谢大佬,学到了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/510584

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX