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

作者:青柠时间:2026-10-30 06:45:35浏览:494

本文深入解析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代码就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持本站。

作者最新文章