如何利用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 相关文章推荐
基于jquery实现漂亮的动态信息提示效果
Aug 02 Javascript
js中的caller和callee属性介绍和例子
Jun 07 Javascript
使用JavaScript和C#中获得referer
Nov 14 Javascript
jQuery实现的指纹扫描效果实例(附演示与demo源码下载)
Jan 26 Javascript
JS动态的把左边列表添加到右边的实现代码(可上下移动)
Nov 17 Javascript
微信小程序 wx.uploadFile在安卓手机上面the same task is working问题解决
Dec 14 Javascript
原生JavaScript实现Tooltip浮动提示框特效
Mar 07 Javascript
JavaScript屏蔽Backspace键的实现代码
Nov 02 Javascript
mpvue将vue项目转换为小程序
Sep 30 Javascript
微信运维交互机器人的示例代码
Nov 12 Javascript
node中使用es6/7/8(支持性与性能)
Mar 28 Javascript
vue 如何使用递归组件
Oct 23 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 静态页面中显示动态内容
2009/08/14 PHP
详解php 使用Callable Closure强制指定回调类型
2017/10/26 PHP
PHP的图像处理实例小结【文字水印、图片水印、压缩图像等】
2019/12/20 PHP
laravel5.6 框架操作数据 Eloquent ORM用法示例
2020/01/26 PHP
jquery实现心算练习代码
2010/12/06 Javascript
幻灯片带网页设计中的20个奇妙应用示例小结
2012/05/27 Javascript
JavaScript中json使用自己总结
2013/08/13 Javascript
js鼠标及对象坐标控制属性详细解析
2013/12/14 Javascript
检查输入的是否是数字使用keyCode配合onkeypress事件
2014/01/23 Javascript
ANGULARJS中用NG-BIND指令实现单向绑定的例子
2014/12/08 Javascript
php常见的页面跳转方法汇总
2015/04/15 Javascript
js中flexible.js实现淘宝弹性布局方案
2020/06/23 Javascript
JavaScript判断表单为空及获取焦点的方法
2016/02/12 Javascript
JS组件Bootstrap Select2使用方法解析
2016/05/30 Javascript
Angular的$http的ajax的请求操作(推荐)
2017/01/10 Javascript
get  post jsonp三种数据交互形式实例详解
2017/08/25 Javascript
nodejs中安装ghost出错的原因及解决方法
2017/10/23 NodeJs
Vue通过URL传参如何控制全局console.log的开关详解
2017/12/07 Javascript
selenium+java中用js来完成日期的修改
2019/10/31 Javascript
微信小程序获取当前位置和城市名
2019/11/13 Javascript
bootstrap-table后端分页功能完整实例
2020/06/01 Javascript
vue scroll滚动判断的实现(是否滚动到底部、滚动方向、滚动节流、获取滚动区域dom元素)
2020/06/11 Javascript
Vue记住滚动条和实现下拉加载的完美方法
2020/07/31 Javascript
jquery+ajax实现异步上传文件显示进度条
2020/08/17 jQuery
记录一次websocket封装的过程
2020/11/23 Javascript
python中print()函数的“,”与java中System.out.print()函数中的“+”功能详解
2017/11/24 Python
Python语言实现将图片转化为html页面
2017/12/06 Python
Selenium 模拟浏览器动态加载页面的实现方法
2018/05/16 Python
Python实现Mysql数据统计及numpy统计函数
2019/07/15 Python
pycharm的python_stubs问题
2020/04/08 Python
html table呈现个人简历以及单元格宽度失效的问题解决
2021/01/22 HTML / CSS
伦敦一家非常流行的时尚精品店:Oxygen Boutique
2017/01/15 全球购物
优秀中专生推荐信
2013/11/17 职场文书
初中三年毕业生的自我评价分享
2014/02/14 职场文书
本科生求职信
2014/06/17 职场文书
校运会宣传稿大全
2015/07/23 职场文书