使用 Promises 编写更优雅的 JavaScript 代码
你可能已经无意中听说过 Promises,很多人都在讨论它,使用它,但你不知道为什么它们如此特别。难道你不能使用回调么?有什么了特别的?在本文中,我们一起来看看 Promises 是什么以及如何使用它们写出更优雅的 JavaScript 代码。
Promises 易于阅读
比如说我们想从 HipsterJesus 的API中抓取一些数据并将这些数据添加到我们的页面中。这些 API 的响应数据形式如下:
1 2 3 4 5 6 | { "text" : "<p>Lorem ipsum...</p>" , "params" : { "paras" : 4, "type" : "hipster-latin" }} |
要使用回调的话,我们通常要写如下形式的东西:
1 2 3 | $.getJSON( 'http://hipsterjesus.com/api/' , function (data) { $( 'body' ).append(data.text); }); |
如果你有 jQuery 的使用经历,你会认出我们创建了一个 GET 请求并且希望响应内容是 JSON。我们还传递了一个回调函数来接受响应的 JSON,以将数据添加到文档中。
另外一种书写方法是使用 getJSON 方法返回的 promise 对象。你可以直接在这个返回对象上绑定一个回调。
1 2 3 | var promise = $.getJSON( 'http://hipsterjesus.com/api/' );promise.done( function (data) { $( 'body' ).append(data.text); }); |
在上面的回调例子中,当响应成功时它将 API 请求的结果添加到文档中。但当响应失败是会发生什么呢?我们可以在我们的 promise 上绑定一个失败处理器。
1 2 3 4 | 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 变量,这样更简洁,一眼就能看出代码的作用。
1 2 3 4 | $.getJSON( 'http://hipsterjesus.com/api/' ).done( function (data) { $( 'body' ).append(data.text);}).fail( function () { $( 'body' ).append( '<p>Oh no, something went wrong!</p>' ); }); |
jQuery 也包含一个一直发生的事件处理器,不论请求成功失败都会被调用。
1 2 3 4 5 | $.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 对象。
1 2 3 4 5 6 | var hipsterJesus = { html: function () { return $.getJSON( 'http://hipsterjesus.com/api/' ).then( function (data) { return data.text; }); }}; |
这个做法很酷,这样我们可以绕过 promise 对象而不必担心何时或如何解析它的值。任何需要 promise 返回值的代码只需注册一个成功响应回调即可。
then方法允许我们修改promise的结果并将其传递给链中的下一个处理器。这意味现在我们可以这样使用新的API:
1 2 3 | hipsterJesus.html().done( function (html) { $( "body" ).append(html); }); |
直到最近,AngularJS 出现了一个杀手级特性,模板可以直接绑定到promise。在Angular的控制器中,像这样:
1 | $scope.hipsterIpsum = $http.get( 'http://hipsterjesus.com/api/' ); |
这样,在模板中写 {{ hipsterIpsum.text }} 就很简单了。当 promise 解析后,Angular 不需要自动更新视图。不幸的是 Angular 团队已经放弃了这一特性。现在,它可以通过调用 $parseProvider.unwrapPromises(true) 来启用。我希望Angular已经其他框架一直包含此特性(我会一直留意)。
链式调用
Promise 最出彩的部分是你可以将它们串联起来。比如说我们想添加一个方法到一个返回一段数组的 API。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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。让我们添加一个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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调用时会发生什么呢?你可能会这样写:
1 2 3 4 5 6 7 8 9 10 11 | 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 的话,这就简单多了:
1 2 3 4 5 | 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+的规范 间有细微的差别,这个规范更标准。更多信息,查看 jQuery维基 上的问答。
您可能感兴趣的相关文章
译文链接:使用 Promises 模式编写更好的 JavaScript 代码
编译来源:梦想天空 ◆ 关注前端开发技术 ◆ 分享网页设计资源
本文来自【梦想天空(http://www.cnblogs.com/lhb25/)】
作者:山边小溪
主站:yyyweb.com 记住啦:)
欢迎任何形式的转载,但请务必注明出处。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
2013-04-29 一组免费的简约风格表情图标 – Simple Smileys
2011-04-29 最新30个漂亮的个人作品集网页设计案例
2011-04-29 Web设计师应该收藏的11个网站
2011-04-29 分享22个制作3D模型的应用程序