Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

jQuery Deferred 与 ES6 Promise 的设计差异

  前不久我有写过一篇「Promise 与异步分支流程」,里面纠结了业务分支流程在 ES6 原生的 Promise 对象上不同的两种做法。ES6 的 Promise 自带了异常处理的功能,导致了 reject 方向的执行可能是意料之外的。其实我有时候觉得 jQuery 的 Deferred 更好用。
  jQuery 的 Deferred(之后简称 Deferred)是不带异常处理的,它不能算是缺陷,也不能算是优点。我觉得这应该算是一种特性吧?这么做至少它可以确保 resolve 和 reject 两个方向的执行平等,不会产生意料之外异常的处理方向偏重问题。所以将 Deferred 直接用于业务代码的分支逻辑中也毫无违和感。
  Deferred 不仅有 then,还有 done 和 fail。then 的效果其实和 ES6 的 Promise(之后简称 Promise)几乎一样,它会返回一个新的 Deferred/Promise 节点,并且这个节点可以接收 then 处理函数中的返回值。但是此处有略微的差异,如果 then 中的处理函数直接返回一个值,那么 then 返回的 Deferred 节点的执行方向将保持与父 Deferred 节点一致。而 Promise 节点的执行方向则总是 resolve。下面是这个差异的重现: <script src="http://code.jquery.com/jquery-2.1.3.min.js"></script>
<script>
new $.Deferred(function(deferred){
  deferred.reject(0);
}).then(function(e){
  console.log("deferred resolve",e);
  return 1;
},function(e){
  console.log("deferred reject",e);
  return 2;
}).then(function(e){
  console.log("deferred resolve",e);
},function(e){
  console.log("deferred reject",e);
});

new Promise(function(resolve,reject){
  reject(0);
}).then(function(e){
  console.log("promise resolve",e);
  return 1;
},function(e){
  console.log("promise reject",e);
  return 2;
}).then(function(e){
  console.log("promise resolve",e);
},function(e){
  console.log("promise reject",e);
});
</script>

  无论是 Deferred 还是 Promise,它们对此的处理方式至少在我的逻辑中都是合理的。Promise 将异常丢给了 reject 方向,所以 resolve 方向也尽量处理非异常来保持这个平衡。Deferred 没有自带异常处理,所以继承父 Deferred 节点的状态才能保持 resolve 和 reject 方向的平衡。
  除了 then 方法之外,Deferred 实例上还具备了 Promise 没有的 done 和 fail 方法。它们并不会创建新的 Deferred 节点,只是将成功或失败的处理函数注册到当前 Deferred 节点上而已。下面代码是在 Promise 上实现 Deferred 的 done 和 fail,需要使用一个中间变量。 <script src="http://code.jquery.com/jquery-2.1.3.min.js"></script>
<script>
new $.Deferred(function(deferred){
  deferred.resolve(123);
}).done(function(e){
  console.log("deferred resolve",e);
}).done(function(e){
  console.log("deferred resolve",e);
});

var promise=new Promise(function(resolve,reject){
  resolve(123);
});
promise.then(function(e){
  console.log("promise resolve",e);
});
promise.then(function(e){
  console.log("promise resolve",e);
});
</script>

  我觉得 done 和 fail 这两个方法非常实用,至少我经常用到。也许是因为 Promise 中有个 catch,所以没有设计 done 和 fail 吧?反正 catch 也是个坑。最大的坑是低版本 IE 不支持 catch 作为方法名,所以要以字符串形式来调用。不过这怨不得 Promise,因为 ES6 的设计当然是直接无视浏览器的。但实际上 catch 只是 then 省略了第一个参数而已,同样会创建新的 Promise 节点,有时候感觉它完全是多余的。
  总结一下吧。Deferred 没有自带错误处理机制,resolve 和 reject 方向的比重是相等的,它可以作为业务分支逻辑使用。而 Promise 的设计理念更倾向于一套完整的异步工作方式,它自带了错误处理机制,并且将运行时的错误也放入 reject 方向处理,而将 then 的默认方向定义为 resolve 方向。我个人真觉得 Deferred 比 Promise 好用,但是异常处理机制有时候确实是需要的。我的看法是如果将 Promise 的异常处理单独抽取出来,让 Promise 节点树变成三叉树的话也许会更容易使用。
网名:
54.144.24.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^