jQuery Deferred 对象

jQuery 异步编程方案 Deferred 的用法

问题

是否用过 jQuery 的 Deferred?它是什么,如何使用?

解答

jQuery Deferred 是 jQuery 1.5 引入的异步编程方案,是 Promise 的前身。它提供了一种管理异步操作的方式。

基本用法

// 创建 Deferred 对象
var deferred = $.Deferred();

// 注册成功回调
deferred.done(function(result) {
  console.log('成功:', result);
});

// 注册失败回调
deferred.fail(function(error) {
  console.log('失败:', error);
});

// 无论成功失败都执行
deferred.always(function() {
  console.log('完成');
});

// 触发成功
deferred.resolve('数据');

// 或触发失败
// deferred.reject('错误信息');

封装异步操作

function asyncTask() {
  var deferred = $.Deferred();
  
  setTimeout(function() {
    var success = Math.random() > 0.5;
    if (success) {
      deferred.resolve('操作成功');
    } else {
      deferred.reject('操作失败');
    }
  }, 1000);
  
  // 返回 promise,防止外部调用 resolve/reject
  return deferred.promise();
}

// 使用
asyncTask()
  .done(function(msg) { console.log(msg); })
  .fail(function(err) { console.log(err); });

then 方法(链式调用)

function getData() {
  var deferred = $.Deferred();
  setTimeout(function() {
    deferred.resolve(10);
  }, 500);
  return deferred.promise();
}

getData()
  .then(function(value) {
    console.log(value); // 10
    return value * 2;
  })
  .then(function(value) {
    console.log(value); // 20
  });

$.when(并行执行)

function task1() {
  var d = $.Deferred();
  setTimeout(function() { d.resolve('任务1完成'); }, 1000);
  return d.promise();
}

function task2() {
  var d = $.Deferred();
  setTimeout(function() { d.resolve('任务2完成'); }, 500);
  return d.promise();
}

// 等待所有任务完成
$.when(task1(), task2()).done(function(r1, r2) {
  console.log(r1, r2); // '任务1完成' '任务2完成'
});

与 Ajax 结合

// jQuery Ajax 返回的就是 Deferred 对象
$.ajax({ url: '/api/data' })
  .done(function(data) {
    console.log('请求成功', data);
  })
  .fail(function(xhr) {
    console.log('请求失败', xhr.status);
  })
  .always(function() {
    console.log('请求完成');
  });

关键点

  • $.Deferred() 创建对象,resolve/reject 改变状态
  • done/fail/always 注册回调,then 支持链式调用
  • promise() 返回只读对象,防止外部改变状态
  • $.when() 处理多个并行异步操作
  • Deferred 是 ES6 Promise 的前身,现代项目建议用原生 Promise