如何利用Promises编写更优雅的JavaScript代码


Posted in Javascript onMay 17, 2016

你可能已经无意中听说过 Promises,很多人都在讨论它,使用它,但你不知道为什么它们如此特别。难道你不能使用回调么?有什么了特别的?在本文中,我们一起来看看 Promises 是什么以及如何使用它们写出更优雅的 JavaScript 代码。

Promises 易于阅读

比如说我们想从 HipsterJesus 的API中抓取一些数据并将这些数据添加到我们的页面中。这些 API 的响应数据形式如下:

{ 
 "text": "<p>Lorem ipsum...</p>", 
 "params": { 
 "paras": 4, 
 "type": "hipster-latin" 
}}

要使用回调的话,我们通常要写如下形式的东西:

$.getJSON('http://hipsterjesus.com/api/', function(data) { 
 $('body').append(data.text); 
});

如果你有 jQuery 的使用经历,你会认出我们创建了一个 GET 请求并且希望响应内容是 JSON。我们还传递了一个回调函数来接受响应的 JSON,以将数据添加到文档中。

另外一种书写方法是使用 getJSON 方法返回的 promise 对象。你可以直接在这个返回对象上绑定一个回调。

var promise = $.getJSON('http://hipsterjesus.com/api/');promise.done(function(data) { 
 $('body').append(data.text); 
});

在上面的回调例子中,当响应成功时它将 API 请求的结果添加到文档中。但当响应失败是会发生什么呢?我们可以在我们的 promise 上绑定一个失败处理器。

var promise = $.getJSON('http://hipsterjesus.com/api/');promise.done(function(data) { 
 $('body').append(data.text);});promise.fail(function() { 
 $('body').append('<p>Oh no, something went wrong!</p>'); 
});

大多数人删掉了 promise 变量,这样更简洁,一眼就能看出代码的作用。

$.getJSON('http://hipsterjesus.com/api/').done(function(data) { 
 $('body').append(data.text);}).fail(function() { 
 $('body').append('<p>Oh no, something went wrong!</p>'); 
});

jQuery 也包含一个一直发生的事件处理器,不论请求成功失败都会被调用。

$.getJSON('http://hipsterjesus.com/api/').done(function(data) { 
 $('body').append(data.text);}).fail(function() { 
 $('body').append('<p>Oh no, something went wrong!</p>');}).always(function() { 
 $('body').append('<p>I promise this will always be added!.</p>'); 
});

通过使用promise,回调的顺序是按预期的。我们能确保正常回调先被调用,然后是失败回调,最后是一直发生的回调。

更好的 API

比如说我们想创造一个 HipsterJesus API 的封装对象。我们会添加一个方法——html,它将来自 API 的 HTML 数据返回。与之前设置一个回调处理器来解析请求不同,我们可以让方法返回一个 promise 对象。

var hipsterJesus = { 
 html: function() { 
  return $.getJSON('http://hipsterjesus.com/api/').then(function(data) { 
   return data.text; 
  }); 
}};

这个做法很酷,这样我们可以绕过 promise 对象而不必担心何时或如何解析它的值。任何需要 promise 返回值的代码只需注册一个成功响应回调即可。

then方法允许我们修改promise的结果并将其传递给链中的下一个处理器。这意味现在我们可以这样使用新的API:

hipsterJesus.html().done(function(html) { 
 $("body").append(html); 
});

直到最近,AngularJS 出现了一个杀手级特性,模板可以直接绑定到promise。在Angular的控制器中,像这样:

$scope.hipsterIpsum = $http.get('http://hipsterjesus.com/api/');

这样,在模板中写 {{ hipsterIpsum.text }} 就很简单了。当 promise 解析后,Angular 不需要自动更新视图。不幸的是 Angular 团队已经放弃了这一特性。现在,它可以通过调用 $parseProvider.unwrapPromises(true) 来启用。我希望Angular已经其他框架一直包含此特性(我会一直留意)。

链式调用

Promise 最出彩的部分是你可以将它们串联起来。比如说我们想添加一个方法到一个返回一段数组的 API。

var hipsterJesus = { 
 
 html: function() { 
  return $.getJSON('http://hipsterjesus.com/api/').then(function(data) { 
   return data.text; 
  }); 
 }, 
 
 paragraphs: function() { 
  return this.html().then(function(html) { 
   return html.replace(/<[^>]+>/g, "").split(""); 
  }); 
 }};

我们以上面的方式这种 HTML 方法,我们用它在 paragraphs 方法中。因为promise回调函数的返回值将传递给链中的下一个回调,我们能够在通过它们时自由地创建小的、功能性的方法来改变数据。

我们可以按需求任意次串联promise。让我们添加一个。

var hipsterJesus = { 
 
 html: function() { 
  return $.getJSON('http://hipsterjesus.com/api/').then(function(data) { 
   return data.text; 
  }); 
 }, 
 
 paragraphs: function() { 
  return this.html().then(function(html) { 
   return html.replace(/<[^>]+>/g, "").split(""); 
  }); 
 }, 
 
 sentences: function() { 
  return this.paragraphs().then(function(paragraphs) { 
   return [].concat.apply([], paragraphs.map(function(paragraph) { 
    return paragraph.split(/. /); 
   })); 
  }); 
 }};  

多个调用

可能 promise 最显著的特点是调用多个 API 的能力。当使用回调时,如果你需要同时创建两个API调用时会发生什么呢?你可能会这样写:

var firstData = null;var secondData = null;var responseCallback = function() { 
 
 if (!firstData || !secondData) 
  return; 
 
 // do something}$.get("http://example.com/first", function(data) { 
 firstData = data; 
 responseCallback();});$.get("http://example.com/second", function(data) { 
 secondData = data; 
 responseCallback(); 
});

使用 promise 的话,这就简单多了:

var firstPromise = $.get("http://example.com/first"); 
var secondPromise = $.get("http://example.com/second"); 
$.when(firstPromise, secondPromise).done(function(firstData, secondData) { 
 // do something 
});

这里我们使用 when 方法,将其绑定到一个供两个请求都完成时调用的处理器上。

结论

这就是 Promise。希望你马上就想到一些可以用 Promise 实现的的可怕的事情。你最喜欢使用它们的方式是什么?在评论中告诉我吧!

*注:为简单起见,本文使用了jQuery的延期执行。jQuery 的 Deferred对象 和 Promises/A+的规范 间有细微的差别,这个规范更标准。

以上这篇如何利用Promises编写更优雅的JavaScript代码就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript 多级checkbox选择效果
Aug 20 Javascript
js中访问html中iframe的文档对象的代码[IE6,IE7,IE8,FF]
Jan 08 Javascript
不到30行JS代码实现Excel表格的方法
Nov 15 Javascript
使用jQuery获得内容以及内容的属性
Feb 26 Javascript
JS实现的自定义右键菜单实例二则
Sep 01 Javascript
微信小程序 页面跳转传参详解
Oct 28 Javascript
Angularjs中三种数据的绑定策略(“@”,“=”,“&amp;”)
Dec 23 Javascript
vue2.0移除或更改的一些东西(移除index key)
Aug 28 Javascript
Vue.js中 v-model 指令的修饰符详解
Dec 03 Javascript
JavaScript中callee和caller的区别与用法实例分析
Jun 28 Javascript
js编写简易的计算器
Jul 29 Javascript
JavaScript实现多球运动效果
Sep 07 Javascript
JavaScript实现设计模式中的单例模式的一些技巧总结
May 17 #Javascript
使用Promise解决多层异步调用的简单学习心得
May 17 #Javascript
js字符串截取函数slice、substring和substr的比较
May 17 #Javascript
javascript Promise简单学习使用方法小结
May 17 #Javascript
关于安卓手机微信浏览器中使用XMLHttpRequest 2上传图片显示字节数为0的解决办法
May 17 #Javascript
Web前端新人笔记之jquery入门心得(新手必看)
May 17 #Javascript
Angularjs中的事件广播 —全面解析$broadcast,$emit,$on
May 17 #Javascript
You might like
PHP如何编写易读的代码
2007/07/10 PHP
PHP file_get_contents 函数超时的几种解决方法
2009/07/30 PHP
PHP常用的文件操作函数经典收藏
2013/04/02 PHP
PHP页面中文乱码分析
2013/10/29 PHP
PHP传值到不同页面的三种常见方式及php和html之间传值问题
2015/11/19 PHP
Zend Framework实现留言本分页功能(附demo源码下载)
2016/03/22 PHP
PHP动态地创建属性和方法, 对象的复制, 对象的比较,加载指定的文件,自动加载类文件,命名空间
2016/05/06 PHP
laravel中短信发送验证码的实现方法
2018/04/25 PHP
javascript 一个自定义长度的文本自动换行的函数
2007/08/19 Javascript
Javascript学习笔记9 prototype封装继承
2010/01/11 Javascript
jQuery与ExtJS之选择实例分析
2010/08/19 Javascript
JavaScript中的索引数组、关联数组和静态数组、动态数组讲解
2014/11/08 Javascript
全面解析Bootstrap手风琴效果
2020/04/17 Javascript
Extjs4.0 ComboBox如何实现三级联动
2016/05/11 Javascript
JavaScript知识点总结(五)之Javascript中两个等于号(==)和三个等于号(===)的区别
2016/05/31 Javascript
Vue组件之自定义事件的功能图解
2018/02/01 Javascript
iview同时验证多个表单问题总结
2018/09/29 Javascript
Vue中使用方法、计算属性或观察者的方法实例详解
2018/10/31 Javascript
Mint UI实现A-Z字母排序的城市选择列表
2018/12/28 Javascript
Vue商品控件与购物车联动效果的实例代码
2019/07/21 Javascript
Vue element-ui父组件控制子组件的表单校验操作
2020/07/17 Javascript
JavaScript实现烟花绽放动画效果
2020/08/04 Javascript
[01:45]IMBATV TI4前线报道-选手到达
2014/07/07 DOTA
[00:47]DOTA2荣耀之路6:玩不了啦!
2018/05/30 DOTA
Python对列表排序的方法实例分析
2015/05/16 Python
Python实现股市信息下载的方法
2015/06/15 Python
selenium在执行phantomjs的API并获取执行结果的方法
2018/12/17 Python
Python基于matplotlib画箱体图检验异常值操作示例【附xls数据文件下载】
2019/01/07 Python
利用Django模版生成树状结构实例代码
2019/05/19 Python
Python在OpenCV里实现极坐标变换功能
2019/09/02 Python
Django生成数据库及添加用户报错解决方案
2020/10/09 Python
matplotlib grid()设置网格线外观的实现
2021/02/22 Python
为什么在使用动态 SQL 语句时必须为低层数据库对象授予权限
2012/12/13 面试题
2016年“5.12”国际护士节活动总结
2016/04/06 职场文书
如何用python反转图片,视频
2021/04/24 Python
CSS几步实现赛博朋克2077风格视觉效果
2021/06/16 HTML / CSS