js promise and jq defered

本文介绍如何使用Promise简化异步操作,提高代码可读性,并展示了如何利用Promise进行错误处理及链式调用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

You've probably heard the talk around the water cooler about how promises are the future. All of the cool kids are using them, but you don't see what makes them so special. Can't you just use a callback? What's the big deal? In this article, we'll look at what promises are and how you can use them to write better JavaScript.

Promises are Easier to Read

Let's say we want to grab some data from the HipsterJesus API and add it to our page. This API responds with data that looks like this:

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

Using a callback, we'd write something like this:

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

If you're experienced with jQuery, you'll recognize we're making aGETrequest and expecting JSON in the response body. We're also passing in a callback function that takes the response JSON and adds it to the document.

译者信息

你可能已经无意中听说过 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,以将数据添加到文档中。

Another way to write this is to use the promise object returned by thegetJSONmethod. You can attach a callback to this object directly.

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

Like the callback example, this appends the result of the API request to the document when the request is successful. But what happens if the request fails? We can also attach afailhandler to our 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>');
});

Most people remove thepromisevariable, which makes it a little easier to tell what the code does at a glance.

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

jQuery also includes analwaysevent handler that's called regardless if the request succeed or fails.

$.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>');
});

With promises, the order of the callbacks is respected. We're guaranteed to have ourdonecallback called first, then ourfailcallback, and finally ouralwayscallback.

译者信息

另外一种书写方法是使用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,回调的顺序是按预期的。我们能确保正常回调先被调用,然后是失败回调,最后是一直发生的回调。

Better APIs

Let's say we want to create a wrapper object for the HipsterJesus API. We'll add a method, html, to return the HTML data that comes down from the API. Rather than having this method take in a handler that's called when the request is resolved, we can just have the method return a promise object.

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

The cool thing about this is we can pass around our promise object without worrying about when or how it resolves its value.   Any code that needs the return value of the promise can just register a callback with done.

The then method allows us to modify the result of a promise and pass it to the next handler in the chain. This means we can now use our new API like this:

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

Until recently, one of the killer features of AngularJS was that templates could bind directly to promises. In an Angular controller, this looked like:

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

Then, it was as simple as writing {{ hipsterIpsum.text }} in a template. When the promise resolved, Angular would automatically update the view. Unfortunately, the Angular team has deprecated this feature. For now, it can be enabled by calling $parseProvider.unwrapPromises(true). I hope Angular and other frameworks include this feature going forward (I'm looking at you Ember).

译者信息

更好的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已经其他框架一直包含此特性(我会一直留意)。

Chaining

The best part about promises is you can chain them! Let's say we want to add a method to our API that returns an array of paragraphs.

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("");
    });
  }};

We've left our HTML method the same, and we're using it in the paragraphs method. Because the return value of a promise's callback is passed to the next callback in the chain, we're free to create small, functional methods that change the data as it's passed through them.

We can chain promises as many times as we want. Let's add a method for sentences.

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。

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(/. /);
      }));
    });
  }};

Multiple calls

Probably the most notable feature of promises is the ability to combine multiple API calls. When using callbacks, what happens if you need to make two API calls at once? You'll probably end up writing something like this:

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();
});

With promises, this becomes much easier:

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

Here, we're using the when method to attach a handler that's called when both requests are done.

Conclusion

That's it! Hopefully you have a sense of some of the awesome things you can accomplish with promises. What's your favorite way to use them? Let me know in the comments!

*Note: For simplicity, this article is using jQuery's deferred implementation. There are subtle differences between jQuery'sDeferred object and the Promises/A+ specification, which is a more canonical standard. For more information, check out Q'sComing from jQuery wiki.

译者信息

多个调用

可能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+的规范间有细微的差别,这个规范更标准。更多信息,查看jQuery维基上的问答。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值