JQuery uses Deferred objects to provide functions similar to Promise in ES2016 (aka. es7).
The AJAX request function in JQuery returns a Deferred object.
Make asynchronous calls more readable and enable advanced usage (specify multiple callback functions/wait for multiple AJAX requests) by using Defered.
Traditional AJAX request in JQuery
$.getJSON('url', data=>{// 处理返回的JSON数据console.log(data);}, err=>{// 处理出错信息console.log(err.status);})
AJAX request using Deferred
$.getJSON('url').done(data=>{console.log(data);}).fail(err=>{console.log(err.status);});
This article will focus on analyzing the state change process of Deferred/Promise, and describe the application of Deferred objects in actual coding.
Users can use the Deferred
function to create a Deferred object
var d1 = $.Deferred();console.log(d1.state());
Deferred objects have the following three states
pending
rejected
resolved
The Deferred object can be converted from the pending state to the rejected state or resolved.
This state transition is one-way, that is to say, once the state of the Deferred object is rejected/resolved, the object The state will be immutable.
JQuery provides three functions to view/modify the state of the Deferred object:
##deferred.state (), returns a string representing the status of the current Deferred object
deferred.reject(), sets the status of the object to rejected
deferred.resolve(), Set the object’s status to resolved
let d1 = $.Deferred();console.log(d1.state()); // "pending"d1.resolve();console.log(d1.state()); // "resolved"let d2 = $.Deferred();console.log(d2.state()); // "pending"d2.reject();console.log(d2.state()); // "rejected"
The then method accepts two functions as parameters. When the state of the object becomes resolved, the first function will be called.When the object's status becomes rejected, the second function will be called.
The first function accepts one parameter, which is the first parameter passed when thedefered.resolve
function is called.
The second function accepts one parameter, which is the first parameter passed when the
defered.rejectfunction is called.
var d1 = $.Deferred();// 注册回调函数d1.then(function(data){console.log('First function: ' + data.state);}, function(err){console.log('Second function: ' + err);});// 做一些耗时的操作// 改变 deferred 对象状态为 resolved// 回调函数将被调用,打印信息:First function: successedd1.resolve({state: 'successed'});
var d2 = $.Deferred();// 注册回调函数d2.then(function(data){console.log('First function: ' + data.state);}, function(err){console.log('Second function: ' + err.state);});// 改变 deferred 对象状态为 rejected// 回调函数将被调用,打印信息:Second function: failedd2.reject({state: 'failed'});
Use the done method to specify the function to be called when the deferred object status becomes resolved
var deferred = $.Deferred();deferred.done(function(data){console.log('Done: ' + data.state);});deferred.resolve({state: 'successed'});
Use the fail method to specify the function to be called when the deferred object status becomes rejected
var deferred = $.Deferred();deferred.fail(function(err){console.log('Fail: ' + err.state);})deferred.resolve({state: 'failed'});
The always method accepts a function as a parameter, and the function will be called whenever the state of the Deferred object changes.Chain callBecause the then/done/fail function still returns a Deferred-like object, we can use them to specify multiple callback functions.
The following Example uses done/fail to specify multiple
var deferred = $.Deferred();deferred.done(data=>{// Do something}).done(data=>){// Do something}.fail(err=>{// Handle the error}).always(()=>{// Clean the environment})
promise method of the Deferred object To get the Promise object of this object. =
The Promise object has the following characteristics:
reject/
resolve method
state() method
then/
done/
fail method to specify the callback function
var deferred = $.Deferred();var promise = deferred.promise();deferred.reject();console.log(deferred.state()); // rejectedconsole.log(promise.state()); // rejectedconsole.log(promise.promise() === promise); // true, Promise 对象的 promis() 方法返回的是它自己
Then method returns a new Promise object
var d1 = $.Deferred();var d2 = d1.done();var d3 = d1.fail();console.log(d1 === d2); // trueconsole.log(d1 === d3); // true
使用 then 方法我们需要明白的几个关键点是:
Deferred 对象的
then
方法, 会创建一个新的 Deferred 对象,并返回新 Deferred 对象的 Promise 对象。
而且then
方法返回的对象 跟 Deferred 对象的 Promise 对象不相等, 多次调用 then 对象会产生多个 Deferred 对象。
下面的例子对比了多次调用 then 方法产生的 Promise 对象
var d1 = $.Deferred();var p2 = d1.then(); // 调用 then 方法返回一个 Promise 对象var p3 = d1.then(); // 调用 then 方法返回一个新的 Promise 对象console.log('reject' in d1); // false, 查看 then 方法返回的对象中是否有 reject 方法console.log('reject' in p2); // false, 查看 then 方法返回的对象中是否有 reject 方法console.log(p2 === d1); // false, 检查 d1 是否与 p2 相等console.log(p2 === d1.promise()); // false, 查看 d1 的 promise 是否与 p2 相等console.log(p2 === p3); // false, p2 和 p3 的值不同
Deferred 对象状态发生变化后, 等待一段时间后 then 方法产生的 Promise 对象的状态才会发生相应的变化
var deferred = $.Deferred();var new_promise = deferred.then();deferred.reject('reject')console.log(`d1 state: ${deferred.state()}`); // rejectedconsole.log(`new_promise state: ${new_promise.state()}`); // pendingsetTimeout(`console.log("new_promise state after 100 miliseconds: ${new_promise.state()}")`, 100); // 100 毫秒后, new_promise 的状态变成了 rejected
Deferred 对象的状态发生改变后,then 方法产生的 Promise 对象的状态并没有立即发生变化, 而是等待了一段时间后才改变。
这段时间内,发生了什么那?
我们以调用 Deferred 对象的 resolve 方法作为例子来说明。 调用 reject 方法的情况与此类似。
首先假设我们构造了Deferred 对象 d1
然后调用 then 方法,并且传入两个函数作为参数 fun1, fun2: var p2 = d1.then(fun1, fun2)
调用 d1.resolve(data)
方法将 d1 的状态设置为 resolved, 此时d1 的状态是 resolved, p2 的状态是 pending
fun1 会被调用, 参数为 d1.resolve
方法的参数: var new_data = fun1(data)
假设 p2 对应的 Deferred 对象是 d2.
d2 的 resolve 方法会被调用, 参数为 fun1 的返回值: d2.resolve(new_data)
p2 的状态变为 resolved
p2 的回调函数会被调用
下面的代码展示了 then 方法产生的 Promise 对象的状态变化。以及如何给回调函数传递参数
var d1 = $.Deferred();function fun1(data){console.log(`p2 state in fun1: ${p2.state()}`);console.log(`data in fun1: ${data}`);return data * 3;}function fun2(error){return 'new data from fun2';}var p2 = d1.then(fun1, fun2);p2.done(data=>{console.log(`p2 state in done: ${p2.state()}`);console.log(`data in done: ${data}`);});d1.resolve(10);/* 屏幕输出为p2 state in fun1: pendingdata in fun1: 10p2 state in done: resolveddata in done: 30*/
明白了 Deferred 的原理,我们就可以使用 Deferred.
下面一段代码定义了一个函数, 在函数中定义了一些耗时的操作。
函数返回 Promise 对象, 可以使用 done/fail/then 注册回调函数
function say_hello(){// 创建 Deferred 对象var deferred = $.Deferred();// 做一些耗时的操作,操作完成后调用 resolve 或者 reject 函数结束。// 我们用 setTimeout 函数模拟一段耗时操作:// 等待五秒钟后,调用 Deferred 的 resolve 方法来改变状态setTimeout(deferred.resolve.bind(deferred, 'hello world'), 5000);// 也可以使用 AJAX 操作/* $.getJSON('/api/names').done(data=>{ if(data.state == 'successed'){ deferred.resolve(data); }else{ deferred.reject(data); } }); */return deferred.promise(); // 返回 promise 对象, 防止外界对 Deferred 对象的状态进行改变}// 调用 say_hello函数,并使用 done/fail/then 方法注册回调函数say_hello().done(msg=>{console.log(msg);});
跟 ES2016 中 Prmomise.all
函数类似。
JQuery 提供了 when
函数, 它可以接受多个 Deferred/Promise 对象作为参数。并返回一个 Promise 对象。
新的 Promise 对象会等待参数中所有的对象状态变为 resolved/reject。
如果参数中任何一个对象的状态变为 rejected, 那么 Promise 对象的状态变为 rejected。 否则变为 resolved。
// 创建一个函数,如果参数大于500, 则将内置的 Deferred 对象状态变为 resolved// 如果参数小于500, 则将内置的 Deferred 对象状态变为 rejectedfunction get_promise(delay){// 创建 Deferred 对象var deferred = $.Deferred();if(delay > 500){setTimeout(deferred.resolve.bind(deferred, delay/100), delay);}else{setTimeout(deferred.reject.bind(deferred, delay/100), delay);}return deferred.promise(); // 返回 promise 对象}// 如果任一参数状态转变为 rejected, when 函数产生的 promise 对象状态会理解变为 rejected。// 并将第一个 Deferred 对象的错误信息传递给回调函数$.when(get_promise(800), get_promise(100), get_promise(300)).fail(error=>{console.log(error); // 1});// 否则 when 函数会等待所有的 Deferred 对象状态变为 resolved, 并将所有 Deferred 对象的返回值依次传递给回调函数$.when(get_promise(900), get_promise(600), get_promise(1000)).done((d1, d2, d3)=>{console.log(d1); // 9console.log(d2); // 6console.log(d3); // 10});$.when(get_promise(800), get_promise(900), get_promise(1000)).done((...datas)=>{console.log(datas); // [8, 9, 10]});
The above is the detailed content of Anatomy of JQuery Deferred Object. For more information, please follow other related articles on the PHP Chinese website!