YIXUNFE / blog

文章区
151 stars 25 forks source link

利用 jQuery.when 简化代码 #56

Open YIXUNFE opened 8 years ago

YIXUNFE commented 8 years ago

利用 jQuery.when 简化代码

最近有一些项目,是从已有的功能上新增一些数据,比如个人账户中新增一些数据。后端并没有在原有接口上提供所需数据,而是希望我们调用另一个接口,这个接口仅仅提供新增的数据。

由于新接口和旧接口在数据上并没有依赖关系,所以我们可以等两个接口都请求完成后将数据填充到 DOM 结构中。在这个问题的解决过程中就出现了一些不太简洁的代码,比如:

var len = 2, data = {}
  cb = function () {...}

//一个请求
$.ajax({
  url: url1,
  ...
}).then(function (d) {
  len--
  $.extend(data, d)
  if (len === 0) {
    cb(data)
  }
})

//又一个请求
$.ajax({
  url: url2,
  ...
}).then(function (d) {
  len--
  $.extend(data, d)
  if (len === 0) {
    cb(data)
  }
})

这种解决方案虽然能处理当前项目中的实际问题,但是当这类无序的异步请求是 N 个的时候,这种方法就有点捉襟见肘了。而利用 jQuery.when 方法,即能很好的解决这些问题,而且能够使代码简洁优雅易于维护。

$.when($.ajax({
  url: url1,
  ...
}), $.ajax({
  url: url2,
  ...
})).done(function (data1, data2) {...}).fail(function () {...})

简洁明了了许多吧:smile:


jQuery.when 的参数

上面提到将多个异步请求作为参数传入 jQuery.when 方法中,这个描述并不正确,因为 jQuery.when 接受的是延迟对象(jQuery.Deferred),巧在 jQuery.ajax 方法返回的 jqXHR 对象(浏览器原生 XHR 对象的超集)就是一个延迟对象(jQuery 1.5+),所以看上去我们是将 jqXHR 对象作为了参数,这确实让人产生了一定的误解。

在处理普通的延迟问题的时候,我们就需要在异步方法中明确地返回延迟对象。

var dfd1 = new $.Deferred(),
  dfd2 = new $.Deferred()

function wait1 (dfd) {
  setTimeout(function () {
    dfd.resolve('wait1 done')
  }, 1000)
  return dfd.promise()
}

function wait2 (dfd) {
  setTimeout(function () {
    dfd.resolve('wait2 done')
  }, 2000)
  return dfd.promise()
}

$.when(wait1(dfd1), wait2(dfd2)).done(function (data1, data2) {
  console.log(data1)  // 'wait1 done'
  console.log(data2)  // 'wait2 done'
})


done、fail、then 方法

jQuery.when 方法返回的是一个 Promise 对象(延迟对象的一个子集),可以调用它的 donefailthen 方法执行回调函数。这里列一下这些方法中回调函数的执行条件。

另外需要注意的是,当 jQUery.when 方法被传入了一个以上的参数时,回调函数中的参数可能是一个数组。因为在传递给一个延迟对象的解决(resolved)事件为多个值的情况下,相应的参数将是这些值组成的数组。

var dfd1 = new $.Deferred(),
  dfd2 = new $.Deferred()

function wait1 (dfd) {
  setTimeout(function () {
    dfd.resolve('wait1', 'done')
  }, 1000)
  return dfd.promise()
}

function wait2 (dfd) {
  setTimeout(function () {
    dfd.resolve('wait2', 'done')
  }, 2000)
  return dfd.promise()
}

$.when(wait1(dfd1), wait2(dfd2)).done(function (data1, data2) {
  console.log(data1)  // ['wait1', 'done']
  console.log(data2)  // ['wait2', 'done']
})


参考资料

jQuery.ajax API 传送门 jQuery.when API 传送门


Thanks