利用Promises编写更优雅的JavaScript代码
本文深入解析JavaScript中Promises的使用,对比传统回调函数,展示如何利用Promises提升代码可读性与维护性。通过jQuery Deferred对象示例,讲解链式调用、API封装及多请求并行处理技巧,帮助开发者编写更优雅的异步代码。
引言:为什么选择 Promises
你可能已经听说过 Promises,虽然很多人都在讨论和使用它,但或许你还不太清楚它为何如此特别。难道传统的回调函数不够用吗?Promises 到底有什么独特之处?本文将带你了解 Promises 的概念,并展示如何利用它编写出更简洁、优雅的 JavaScript 代码。
Promises 的可读性优势
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
构建更友好的 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) 来启用它。我希望其他框架也能包含此特性(我会持续关注)。
链式调用技巧
链式调用
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(/. /);
}));
});
}};
并行调用多个 API
并行调用多个 API
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 方法,将其绑定到一个处理器上,该处理器在两个请求都完成时被调用。
总结
结论
以上就是对 Promises 的介绍。希望你已经想到了一些可以用 Promises 实现的精彩应用。你最喜欢如何使用它们?欢迎在评论区分享!
*注:为简化说明,本文使用了 jQuery 的 Deferred 对象。jQuery 的 Deferred 对象与 Promises/A+ 规范之间存在细微差异,后者是更通用的标准。
以上这篇如何利用Promises编写更优雅的JavaScript代码就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持本站。
ES6模块化中js文件怎么用import和export交互
2026-11-05 17:00:48
JS读取本地文件时报错或无响应的常见原因
2026-11-05 16:32:31
let、const与var的区别:JavaScript变量声明最佳实践
2026-11-05 14:16:38
JS中直接定义静态二维数组的字面量写法
2026-11-05 14:16:08
JS 怎么查找字符串中所有匹配项的位置
2026-11-05 14:15:53
