Объект Deferred и функция $.when()

Объект Deferred используется для выполнения отложенных или асинхронных вычислений, в частности при создании анимации (см. разд. 2.1) и выполнении AJAX-запросов (см. разд. 3.3). Создать объект позволяет функция $.Deferred():

<Deferred> = $.Deferred([<Функция>])

Пример создания объекта:

var myDeferred = $.Deferred();
myDeferred.done( function(d) {
   console.log('done() ' + d); // done() data
});
myDeferred.resolve('data');

В качестве параметра можно указать функцию обратного вызова, внутри которой через параметр, а также через указатель this, доступна ссылка на объект Deferred. Внутри функции можно назначать обработчики событий:

var myDeferred = $.Deferred( function(defer) {
   defer.done( function(d) {
      console.log('defer.done() ' + d);
   });
   this.done( function(d) {
      console.log('this.done() ' + d);
   });
});
myDeferred.resolve('data');

Объект Deferred может находиться в следующих состояниях, возвращаемых методом state():

  • pending — режим ожидания (начальное состояние);
  • resolved — успешное выполнение операции; вызван метод resolve() или resolveWith();
  • rejected — выполнение операции отклонено; вызван метод reject() или rejectWith().

Изменить статус позволяют следующие методы:

  • resolve([<Данные>]) — вызывает успешное выполнение операции и возвращает ссылку на объект Deferred. Значение, указанное в параметре <Данные>, доступно внутри обработчиков успешного выполнения операции через параметр. Можно передать сразу несколько параметров с данными:
var myDeferred = $.Deferred();
myDeferred.done( function(d, n) {
   console.log('done() ' + d + ' ' + n); // done() data 10
}).then( function(d, n) {
   console.log('then() ' + d + ' ' + n); // then() data 10
});
console.log( myDeferred.state() );       // pending
setTimeout( function() {
   myDeferred.resolve('data', 10);
   console.log( myDeferred.state() );    // resolved
}, 1000);
  • resolveWith(<Контекст>[, <Данные>]) — вызывает успешное выполнение операции и возвращает ссылку на объект Deferred. Значение, указанное в параметре <Контекст>, доступно внутри обработчиков успешного выполнения операции через указатель this. В параметре <Данные> можно передать массив с данными, которые будут доступны через параметры внутри обработчиков:
var obj = { a: 5 };
var myDeferred = $.Deferred();
myDeferred.done( function(d, n) {
   console.log('done() ' + d + ' ' + n); // done() data 10
   console.log(this.a);                  // 5
}).then( function(d, n) {
   console.log('then() ' + d + ' ' + n); // then() data 10
   console.log(this.a);                  // 5
});
setTimeout( function() {
   myDeferred.resolveWith(obj, ['data', 10]);
}, 1000);
  • reject([<Данные>]) — отклоняет выполнение операции и возвращает ссылку на объект Deferred. Значение, указанное в параметре <Данные>, доступно внутри обработчиков отклонения операции через параметр. Можно передать сразу несколько параметров с данными:
var myDeferred = $.Deferred();
myDeferred.fail( function(d, n) {
   console.log('fail() ' + d + ' ' + n);  // fail() data 10
}).catch(function(d, n) {
   console.log('catch() ' + d + ' ' + n); // catch() data 10
});
console.log( myDeferred.state() );        // pending
setTimeout( function() {
   myDeferred.reject('data', 10);
   console.log( myDeferred.state() );     // rejected
}, 1000);
  • rejectWith(<Контекст>[, <Данные>]) — отклоняет выполнение операции и возвращает ссылку на объект Deferred. Значение, указанное в параметре <Контекст>, доступно внутри обработчиков отклонения операции через указатель this. В параметре <Данные> можно передать массив с данными, которые будут доступны через параметры внутри обработчиков:
var obj = { a: 5 };
var myDeferred = $.Deferred();
myDeferred.fail( function(d, n) {
   console.log('fail() ' + d + ' ' + n);  // fail() data 10
   console.log(this.a);                   // 5
}).catch(function(d, n) {
   console.log('catch() ' + d + ' ' + n); // catch() data 10
   console.log(this.a);                   // 5
});
setTimeout( function() {
   myDeferred.rejectWith(obj, ['data', 10]);
}, 1000);
  • notify([<Данные>]) — отправляет информацию о ходе выполнения операции и возвращает ссылку на объект Deferred. Значение, указанное в параметре <Данные>, доступно внутри обработчиков через параметр. Можно передать сразу несколько параметров с данными:
var myDeferred = $.Deferred();
myDeferred.progress( function(d, n) {
   console.log('progress() ' + d + ' ' + n);
});
setTimeout( function() {
   myDeferred.notify('data', 10);
}, 1000);
  • notifyWith(<Контекст>[, <Данные>]) — отправляет информацию о ходе выполнения операции и возвращает ссылку на объект Deferred. Значение, указанное в параметре <Контекст>, доступно внутри обработчиков через указатель this. В параметре <Данные> можно передать массив с данными, которые будут доступны через параметры внутри обработчиков:
var obj = { a: 5 };
var myDeferred = $.Deferred();
myDeferred.progress( function(d, n) {
   console.log('progress() ' + d + ' ' + n);
   console.log(this.a);
});
setTimeout( function() {
   myDeferred.notifyWith(obj, ['data', 10]);
}, 1000);

Метод promise([<Объект>]) позволяет вернуть объект Deferred без методов, изменяющих статус операции: resolve(), resolveWith(), reject(), rejectWith(), notify() и notifyWith(). Через этот объект можно только назначить обработчики изменения статуса, что позволяет избежать изменения состояния кодом стороннего разработчика:

function func() {
   var myDeferred = $.Deferred();
   setTimeout( function() {
      myDeferred.resolve('data');
   }, 1000);
   return myDeferred.promise();
}
var obj = func();
obj.done( function(d) {
   console.log('done() ' + d); // done() data
});

Если методу promise() передать объект, то к этому объекту будут прикреплены методы для назначения обработчиков. Метод promise() вернет ссылку на этот объект, а не будет создавать новый объект:

var obj = {
   func: function() {
      console.log('func()');
   }
};
var myDeferred = $.Deferred();
setTimeout( function() {
   myDeferred.resolve('data');
}, 1000);
myDeferred.promise(obj);

obj.done( function(d) {
   console.log('done() ' + d); // done() data
}).func();                     // func()

Обработать изменение статуса позволяют следующие методы:

  • done(<Функция1>[, <Функция2>]) — назначает обработчики для успешного выполнения операции. Возвращает ссылку на объект Deferred. Можно указать несколько ссылок на функции через запятую или передать массив со ссылками на функции:
var myDeferred = $.Deferred();
myDeferred.done( function(d, n) {
   console.log('done() ' + d + ' ' + n);    // done() data 10
}).done([
   function(d, n) {
      console.log('done() ' + d + ' ' + n); // done() data 10
   },
   function(d, n) {
      console.log('done() ' + d + ' ' + n); // done() data 10
   }
]);
setTimeout( function() {
   myDeferred.resolve('data', 10);
}, 1000);
  • fail(<Функция1>[, <Функция2>]) — назначает обработчики для отклонения операции. Возвращает ссылку на объект Deferred. Можно указать несколько ссылок на функции через запятую или передать массив со ссылками на функции:
var myDeferred = $.Deferred();
myDeferred.fail( function(d, n) {
   console.log('fail() ' + d + ' ' + n);    // fail() data 10
}).fail([
   function(d, n) {
      console.log('fail() ' + d + ' ' + n); // fail() data 10
   },
   function(d, n) {
      console.log('fail() ' + d + ' ' + n); // fail() data 10
   }
]);
setTimeout( function() {
   myDeferred.reject('data', 10);
}, 1000);
  • always(<Функция1>[, <Функция2>]) — назначает обработчики, которые будут вызваны независимо от успешности выполнения операции. Возвращает ссылку на объект Deferred. Можно указать несколько ссылок на функции через запятую или передать массив со ссылками на функции:
var myDeferred = $.Deferred();
myDeferred.always( function() {
   console.log('always()');                 // always()
}).always([
   function() {
      console.log('always()');              // always()
   },
   function() {
      console.log('always()');              // always()
   }
]);
setTimeout( function() {
   myDeferred.resolve();
   // myDeferred.reject();
}, 1000);
  • progress(<Функция1>[, <Функция2>]) — назначает обработчики, которые будут вызваны при отправке сообщений методами notify() и notifyWith(). Возвращает ссылку на объект Deferred. Можно указать несколько ссылок на функции через запятую или передать массив со ссылками на функции:
var myDeferred = $.Deferred();
myDeferred.progress( function(data) {
   console.log('progress: ' + data);     // progress: 5
}).progress([
   function(data) {
      console.log('progress: ' + data);  // progress: 5
   },
   function(data) {
      console.log('progress: ' + data);  // progress: 5
   }
]);
setTimeout( function() {
   myDeferred.notify(5);
}, 1000);
  • then(<done>[, <fail>[, <progress>]]) — в первом параметре указывается ссылка на функцию, которая будет вызвана при успешном выполнении операции. Во втором параметре указывается ссылка на функцию, которая будет вызвана при отклонении операции. В третьем параметре указывается ссылка на функцию, которая будет вызвана при отправке сообщений методами notify() и notifyWith(). Вместо ссылки на функцию можно передать значение null. Метод возвращает отфильтрованный объект Deferred, содержащий результат, возвращаемый функцией <done>. Этот результат доступен через параметр функции <done> при вызове следующего метода then() или done():
var myDeferred = $.Deferred();
var myDeferred2 = myDeferred.then(
   function(data) {
      console.log('done_1: ' + data);
      return data + 3;
   },
   function(data) {
      console.log('fail: ' + data);
   },
   function(data) {
      console.log('progress: ' + data);
});
myDeferred2.then( function(d) {
   console.log('done_2: ' + d);
});
myDeferred.notify(10);
setTimeout( function() {
   myDeferred.resolve(5);
   //myDeferred.reject(5);
}, 1000);

При вызове метода resolve() в консоли получим следующие сообщения:

progress: 10
done_1: 5
done_2: 8

При вызове метода reject() результат будет таким:

progress: 10
fail: 5
done_2: undefined
  • catch(<fail>) — в качестве параметра указывается ссылка на функцию, которая будет вызвана, если операция отклонена. Метод возвращает отфильтрованный объект Deferred. Пример:
var myDeferred = $.Deferred();
myDeferred.catch(
   function(data) {
      console.log('fail: ' + data); // fail: 5
   });
setTimeout( function() {
   myDeferred.reject(5);
}, 1000);

Для получения объекта Deferred можно также воспользоваться функцией $.when([<Параметры>]). Если параметры не указаны, то функция возвращает объект Deferred с успешным статусом выполнения операции:

$.when().done( function() {
   console.log('done()');     // done()
});

В качестве параметра можно передать объект Deferred:

var myDeferred = $.Deferred();
$.when(myDeferred).done( function(d) {
   console.log('done() ' + d);
}).fail( function(d) {
   console.log('fail() ' + d);
});
setTimeout( function() {
   myDeferred.resolve(5);          // done() 5
   // myDeferred.reject(10);       // fail() 10
}, 1000);

Если указать несколько объектов Deferred через запятую, то функция будет ожидать завершения всех объектов. Внутри обработчика через параметры доступны данные, переданные при завершении операции. Первый параметр содержит данные первого объекта, второй — второго объекта и т. д. Если данные не передавались, то параметр будет иметь значение undefined. Если передавалось одно значение, то оно будет доступно через параметр. Если передавались сразу несколько значений, то они будут доступны через параметр в виде массива:

var myDeferred  = $.Deferred();
var myDeferred2 = $.Deferred();
var myDeferred3 = $.Deferred();
$.when(myDeferred, myDeferred2, myDeferred3).done(
   function(v1, v2, v3) {
      console.log(v1); // undefined
      console.log(v2); // 5
      console.log(v3); // Array [ 1, 2, 3 ]
   });
setTimeout( function() {
   myDeferred.resolve();
   myDeferred2.resolve(5);
}, 1000);
setTimeout( function() {
   myDeferred3.resolve(1, 2, 3);
}, 1500);

Если один из объектов отклонен, то сразу вызываются обработчики отклонения операции, не дожидаясь завершения операций в остальных объектах. Параметры внутри обработчика будут соответствовать параметрам при вызове метода reject() или rejectWith():

var myDeferred  = $.Deferred();
var myDeferred2 = $.Deferred();
var myDeferred3 = $.Deferred();
$.when(myDeferred, myDeferred2, myDeferred3).fail(
   function(v1, v2) {
      console.log(v1); // 1
      console.log(v2); // 2
   });
setTimeout( function() {
   myDeferred.resolve();
   myDeferred2.resolve(5);
}, 1000);
setTimeout( function() {
   myDeferred3.reject(1, 2);
}, 500);

В качестве параметра можно также указать объекты Promise:

var myPromise = new Promise( function(resolve, reject) {
   setTimeout( function() {
      resolve('test');
   }, 1000);
});
$.when(myPromise).done( function(data) {
   console.log(data); // test
});

Если передать другие данные или объекты, то функция вернет объект Deferred с успешным статусом выполнения операции:

$.when(10).done( function(data) {
   console.log(data);   // 10
});
$.when( { a: 5} ).done( function(data) {
   console.log(data.a); // 5
});

Учебник по jQuery и AJAX
Учебник по jQuery и AJAX в формате PDF

Помощь сайту

ЮMoney (Yandex-деньги): 410011140483022

ПАО Сбербанк:
Счет: 40817810855006152256
Реквизиты банка:
Наименование: СЕВЕРО-ЗАПАДНЫЙ БАНК ПАО СБЕРБАНК
Корреспондентский счет: 30101810500000000653
БИК: 044030653
КПП: 784243001
ОКПО: 09171401
ОКОНХ: 96130
Скриншот реквизитов