머리말
jQuery 1.5 이전에는 여러 Ajax 작업이 필요한 경우 일반적으로 다음 두 가지 방법을 사용했습니다.
1).Ajax 직렬 호출
$.ajax({ success: function() { $.ajax({ success: function() { $.ajax({ //callbacks... }); }); });
이 방법의 코드는 읽기가 어렵고 비효율적이며 모호하고 디버그 및 문제 해결이 복잡합니다.
2) Ajax를 병렬로 호출
var promises = []; $.ajax({ success: function() { promises.push('resolved'); check(); } }); $.ajax({ success: function() { promises.push('resolved'); check(); } }); $.ajax({ success: function() { promises.push('resolved'); check(); } }); var check = function() { //checks for all 3 values in the promises array }
이 방법은 이미 콜백 함수 호출에 매우 적합하며 병렬로 데이터를 가져오고 가독성이 좋습니다. 단점은 코드가 길고 확장성이 낮으며 디버깅 및 문제 해결이 매우 복잡하다는 것입니다.
jQuery 1.5 이후에는 deferred 객체가 추가되었습니다. 따라서 위와 동일한 요구사항은 다음과 같은 방법으로 달성할 수 있습니다.
1) 약속
var address = $.ajax({}); var tweets = $.ajax({}); var facebook = $.ajax({}); render_side_bar = function(address, tweets, facebook){ //render sidebar } render_no_side_bar = function () { } $.when(address, tweets, facebook).then(render_side_bar, render_no_side_bar)
코드의 가독성이 좋고 확장성이 높으며 디버깅 및 문제 해결의 복잡성이 크게 줄어드는 것을 볼 수 있습니다.
그럼 질문은, Promise와 Deferred Object란 정확히 무엇인가요?
2.자세한 설명
2. 지연 객체란 무엇인가요?
deferred 객체는 jQuery 버전 1.5에서 도입된 콜백 함수 솔루션으로, 완료해야 할 특정 작업을 나타내며 사용자가 이를 사용할 수 있도록 몇 가지 메서드를 제공합니다.
지연된 객체는 Promises 인터페이스의 구현입니다. jQuery 1.5 이상의 모든 Ajax 버전에서 반환되는 jqXHR 객체는 지연된 객체입니다.
2. 지연 객체의 여러 이점
2.1. 동일한 작업에 대해 여러 콜백 함수 지정
지연된 객체의 이점 중 하나는 기존 Ajax에서는 불가능한 여러 콜백 함수를 작업에 추가할 수 있다는 것입니다.
$.ajax("test.html") .done(function(){ alert("first success callback!");} ) .fail(function(){ alert("there is an error!"); } ) .done(function(){ alert("second success callback!");} );
2.2. 여러 작업에 동일한 콜백 함수 지정
지연된 객체의 두 번째 이점은 여러 작업에 대해 동일한 콜백 함수를 지정할 수 있다는 것인데, 이는 기존 Ajax에서도 달성할 수 없습니다.
$.when($.ajax({}), $.ajax({})) .done(function(){ alert("success!"); }) .fail(function(){ alert("error!"); });
2.3. Ajax 이외의 작업을 위한 콜백 함수
지연된 개체의 세 번째 이점은 더 이상 ajax 작업으로 제한되지 않는다는 것입니다. 모든 작업(ajax 작업, 로컬 작업/비동기 작업 또는 동기 작업)에서 지연된 개체를 사용하고 콜백 함수를 지정할 수 있습니다.
매우 시간이 많이 걸리는 작업
var dfd = $.Deferred(); // create a deferred object var wait = function(dtd){ var tasks = function(){ alert("over!"); dtd.resolve(); // change the state of the deferred object from pending to resolved }; setTimeout(tasks,50000); return dtd; };
$.when(wait(dtd)) .done(function(){ alert("success!"); }) .fail(function(){ alert("error!"); });
2.4.체인콜
jQuery의 전통적인 Ajax 작업은 다음과 같습니다.
$.ajax({ url: "", success: function(){ alert("success!"); }, error:function(){ alert("error!"); } });
Success는 ajax 작업이 성공한 후의 콜백 함수를 지정하고, error는 ajax 작업이 실패한 후의 콜백 함수를 지정합니다. jQuery 1.5 이전에는 Ajax 작업이 XMLHTTPRequest 객체를 반환했으며 체인 작업을 지원하지 않았습니다. 버전 1.5부터 ajax 작업은 지연된 개체인 jqXHR 개체를 반환합니다. 지연된 개체의 중요한 이점은 지연된 개체의 모든 메서드가 지연된 개체를 반환하기 때문에 체인 작업을 수행할 수 있다는 것입니다.
현재 Ajax 작업은 다음과 같이 작성됩니다.
$.ajax({}) .done(function(){ alert("success!"); }) .fail(function(){ alert("fail!"); });
두 가지 작성 방법을 비교하면 done()은 전통적인 ajax 작업의 성공 방법과 동일하고, 실패()는 전통적인 ajax 작업의 실패 방법과 동일하다는 것을 분명히 알 수 있습니다. 기존 작성 방법에 비해 코드 가독성이 향상되었습니다.
3.지연 객체 메서드
3.1 기본 사용법
(1).지연 객체 생성
var dfd = $.Deferred() //지연 객체 생성
(2).지연 객체 상태
지연된 객체에는 세 가지 상태가 있습니다
pending: 은 작업이 완료되지 않은 상태이고 지연된(지연된) 객체가 보류 상태에서 시작됨을 나타냅니다.
해결됨: 은 작업이 성공했음을 나타냅니다.
거부됨: 은 작업이 실패했음을 나타냅니다.
state() 메소드는 지연된 객체의 현재 상태를 반환합니다.
$.Deferred().state(); // 'pending' $.Deferred().resolve().state(); // 'resolved' $.Deferred().reject().state(); // 'rejected'
(3).지연 객체의 상태 변경
Deferred.resolve() 또는 deferred.resolveWith()를 호출하여 Deferred를 해결 상태로 변환하고 설정에서 doneCallback을 즉시 실행합니다.
var callbackFunc = function(){console.log(arguments[0]);} var dfd = $.Deferred(); dfd.done(callbackFunc); dfd.resolve("hello"); //'hello'
Deferred.reject() 또는 deferred.rejectWith()를 호출하여 Deferred를 거부된 상태로 변환하고 설정에서 모든 실패 콜백을 즉시 실행합니다.
var callbackFunc = function(){console.log(arguments[0]);} var dfd = $.Deferred(); dfd.fail(callbackFunc); dfd.reject("fail"); //'fail'
(4).바인드 콜백 함수
지연된 객체의 상태가 변경되면 콜백 함수가 트리거됩니다. deferred.then(), deferred.always(), deferred.done() 또는 deferred.fail()을 사용하여 이 객체에 추가된 모든 콜백은 실행을 위해 대기열에 추가됩니다.
pending-->resolved, 설정에서 doneCallbacks(done()으로 지정됨)을 실행하면 매개변수가 해결됨에 따라 doneCallbacks로 전달됩니다.
보류 중-->거부된 경우 설정에서 모든 실패 콜백(fail()으로 지정됨)을 실행하고 매개변수는 해결을 통해 실패 콜백으로 전달됩니다.
보류 중-->해결/거부됨, Always()에 지정된 콜백을 실행하고 매개변수가 해결된 콜백에 전달됩니다.
var f1 = function(){console.log("done");}, f2 = function(){console.log("fail");}, f3 = function(){console.log("always");}; var dfd = $.Deferred(); dfd.done(f1).fail(f2).always(f3); //if dfd.resolve(); //'done' 'always' //if dfd.reject(); //'fail' 'always'
如果在状态更改后附件一个callback则会立即执行callback,因此不必担心deferred对象何时被resolved或者rejected,因为无论何时,参数都会正确地传递给callbacks。
var fun1 = function(){console.log(arguments[0]);}, fun1 = function(){console.log(arguments[0]);}; var dfd = $.Deferred(); dfd.done(fun1); dfd.resolve("hello"); //'hello' dfd.done(fun2); //'hello'
3.2.deferred对象的方法
(1)$.Deferred([beforeStart]) -- 创建一个deferred对象,参数类型为Function,是一个在构造函数之前调用的函数。
var func = function(){console.log("start");} var dfd = $.Deferred(func); //'start' create a deferred object
(2)deferred.done(doneCallbacks [,doneCallbacks]) -- 当deferred(延迟)对象解决时,调用添加处理程序。
args:接受一个或者多个参数,所有的参数都可以是一个单一的函数或者函数数组,当deferred(延迟)对象解决时,doneCallbacks被调用。回调是依照他们添加的顺序执行的。
var func1 = function(){console.log("1");}, func2 = function(){console.log("2");}, func3 = function(){console.log("3");}; var dfd = $.Deferred(); dfd.done([func1,func2],func3,[func2,func1]); dfd.resolve(); // "1 2 3 2 1"
(3)deferred.fail(failCallbacks [,failCallbacks]) -- 当deferred(延迟)对象拒绝时,调用添加处理程序。
args:接受一个或者多个参数,所有的参数都可以是一个单一的函数或者函数数组,当deferred(延迟)对象拒绝时,failCallbacks被调用。回调是依照他们添加的顺序执行的。
var func1 = function(){console.log("1");}, func2 = function(){console.log("2");}, func3 = function(){console.log("3");}; var dfd = $.Deferred(); dfd.fail([func1,func2],func3,[func2,func1]); dfd.reject(); // "1 2 3 2 1"
(4)deferred.resolve(args) and deferred.resolveWith(context [,args]) -- 解决Deferred(延迟)对象,并根据给定的args参数(resolveWith给定context)调用任何doneCallbacks。
参数:args -- type(object),传递给回调函数(doneCallbacks)的可选的参数,
context -- type(object),Context(上下文)作为this对象传递给完成回调函数(doneCallbacks)。
var func = function(arg){console.log(arg);}; $.Deferred().done(func).resolve("done!"); //'done!' var func = function(arg1,arg2){console.log(arg1.name + ',' + arg2);}; $.Deferred().done(func).resolve({name:'Lucy'},'How are you!'); // 'Lucy,How are you!'
resolve和resolveWith的区别就等同于fire和fireWith的区别。
var func = function () { console.log(this.name + ',' + arguments[0] + ' ' + arguments[1] + ' ' + arguments[2]); }; $.Deferred().done(func).resolveWith({ name: "Lucy" }, ["How", "are", "you!"]);//'Lucy,How are you!'
(5)deferred.reject(args) and deferred.rejectWith(context [,args]) -- 拒绝Deferred(延迟)对象,并根据给定的args参数(rejectWith给定context)调用任何failCallbacks。
参数:args -- type(object),传递给回调函数(doneCallbacks)的可选的参数,
context -- type(object),Context(上下文)作为this对象传递给完成回调函数(doneCallbacks)。
var func = function(arg){console.log(arg);}; $.Deferred().fail(func).reject("error!"); //'error!' var func = function(ctx,arg){console.log(ctx.name + ',' + arg);}; $.Deferred().fail(func).reject({name:'Mark'},'What happend!'); // 'Mark,What happend!'
reject和rejectWith的区别就等同于fire和fireWith的区别。
var func = function () { console.log(this.name + ',' + arguments[0] + ' ' + arguments[1]); }; $.Deferred().fail(func).rejectWith({ name: "Mark" }, ["what", "happend!"]); // 'Mark,What happend!'
(6)deferred.promise([target]) -- 返回Deferred(延迟)的Promise(承诺)对象。
参数可选,无参数时返回一个Promise(承诺)对象,Promise(承诺)对象仅会暴露那些需要绑定额外的处理或判断状态的延迟方法(then, done, fail, always,pipe, progress, state,和 promise)时,并不会暴露任何用于改变状态的延迟方法(resolve, reject, notify,resolveWith, rejectWith, 和 notifyWith)。使用Promise(承诺)会阻止其他人破坏你制造的promise。
function asyncEvent() { var dfd = jQuery.Deferred(); // Resolve after a random interval setTimeout(function () { dfd.resolve("hurray"); }, Math.floor(400 + Math.random() * 2000)); // Reject after a random interval setTimeout(function () { dfd.reject("sorry"); }, Math.floor(400 + Math.random() * 2000)); // Show a "working..." message every half-second setTimeout(function working() { if (dfd.state() === "pending") { dfd.notify("working... "); setTimeout(working, 500); } }, 1); // Return the Promise so caller can't change the Deferred return dfd.promise(); } // Attach a done, fail, and progress handler for the asyncEvent $.when(asyncEvent()).then( function (status) { alert(status + ", things are going well"); }, function (status) { alert(status + ", you fail this time"); }, function (status) { alert(status); } );
有参数时,会将事件绑定到参数上,然后返回该参数对象(返回的实际是一个扩展的Promise(承诺)对象)。
var obj = { hello: function (name) { alert("Hello " + name); } }, // Create a Deferred dfd = $.Deferred(); // Set object as a promise dfd.promise(obj); // Resolve the deferred dfd.resolve("John"); // Use the object as a Promise obj.done(function (name) { obj.hello(name); // will alert "Hello John" }).hello("Karl");
(7)$.when(deferreds) -- 提供一种方法来执行一个或多个对象的回调函数。
参数:type(Deferred),一个或多个延迟对象,或者普通的JavaScript对象。
参数仅传入一个单独的Deferred对象,返回它的Promise对象。
function func() { var dfd = $.Deferred(); setTimeout(function () { dfd.resolve("hurry"); }, 500); return dfd.promise(); }; $.when(func()).done(function (arg) { alert(arg); /*alert "hurry"*/ });
参数传入一个非Deferred和Promise对象,那么该参数会被当成一个被解决(resolved)的延迟对象,并且绑定到上面的任何doneCallbacks都会被立即执行。
$.when( { name: 123 } ).done( function(arg) { alert(arg.name); } /* alerts "123" */ );
无参数,返回一个resolved(解决)状态的Promise对象。
$.when().state(); // "resolved"
参数为多个Deferred对象,该方法根据一个新的“宿主” Deferred(延迟)对象,跟踪所有已通过Deferreds聚集状态,返回一个Promise对象。当所有的延迟对象被解决(resolve)时,“宿主” Deferred(延迟)对象才会解决(resolved)该方法,或者当其中有一个延迟对象被拒绝(rejected)时,“宿主” Deferred(延迟)对象就会reject(拒绝)该方法。
var d1 = $.Deferred(); var d2 = $.Deferred(); $.when( d1, d2 ).done(function ( v1, v2 ) { console.log( v1 ); // "Fish" console.log( v2 ); // "Pizza" }); d1.resolve( "Fish" ); d2.resolve( "Pizza" );
(8)deferred.then(doneFilter [,failFilter] [,progressFilter]) -- 当Deferred(延迟)对象解决,拒绝或仍在进行中时,调用添加处理程序。
参数:
doneFilter -- type(Function),当Deferred(延迟)对象得到解决时被调用的一个函数。
failFilter -- type(Function),当Deferred(延迟)对象拒绝时被调用的一个函数,可选。
progressFilter -- type(Function),当Deferred(延迟)对象生成进度通知时被调用的一个函数,可选。
其实,then方法可以理解成,把done(),fail(),progress()合在一起写。
var filterResolve = function () { var dfd = $.Deferred(), filtered = dfd.then(function (value) { return value * 2; }); dfd.resolve(5); filtered.done(function (value) { console.log(value); }); }; filterResolve(); //'10' var defer = $.Deferred(), filtered = defer.then(null, function (value) { return value * 3; }); defer.reject(6); filtered.fail(function (value) { alert("Value is 3*6 = " + value); });
(9)deferred.always(alwaysCallbacks [,alwaysCallbacks]) -- 当Deferred(延迟)对象解决或拒绝时,执行alwaysCallbacks。
顾名思义,只要Deferred对象的状态发生更改(解决或者拒绝)均会调用alwaysCallbacks。
(10)deferred.state() -- 获取一个Deferred(延迟)对象的当前状态,不接受任何参数。
$.Deferred().state();//"pending"
上面讲述过deferre(延迟)对象的三种状态,这个方法对于debug非常有用,例如,在准备reject一个deferred对象之前,判断它是否处于resolved状态。
(11)deferred.notify(args) and deferred.notifyWith()
(12)deferred.progress()
(13)deferred.pipe()
(14).promise()
(15)deferred.isRejected() 和 deferred.isResolved() -- 从jQuery 1.7开始被弃用,较新版本的jQuery类库中已经被删除,可以使用state()方法代替这两个方法。
(16)deferred.pipe() -- 从jQuery 1.8开始被弃用。
4.什么情况下使用deferred对象和Promises?
上面讲了很多,那么我们究竟在什么情况下使用Deferred对象和Promises对象呢?
(1)复杂的动画
不知道动画什么时候结束,但是又必须在动画结束的时候做一些操作或者是启动其他的动画,这种情况下,如果采用其他的方式,很容易导致代码可读性差,尤其是还夹带着一些其它的操作,比如渲染、表单操作等,现在jQuery会为你的动画操作返回一个Promise,这样这些动画可以进行链式操作。
(2)处理队列
function wait(ms) { var deferred = $.Deferred(); setTimeout(function(){deferred.resolve()}, ms); return deferred.promise(); } wait(1500).then(function () { // After 1500ms this will be executed });
(4)典型的Ajax操作
$.when($.ajax({}), $.ajax({})) .done(function(){ alert("success!"); }) .fail(function(){ alert("error!"); });
(5)一些耗时的大循环操作
以上就是本文的全部内容,希望对大家的学习有所帮助。