Home  >  Article  >  Web Front-end  >  Anatomy of JQuery Deferred Object

Anatomy of JQuery Deferred Object

巴扎黑
巴扎黑Original
2017-06-26 14:24:201116browse

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.

Create Deferred

Users can use the Deferred function to create a Deferred object

var d1 = $.Deferred();console.log(d1.state());

The state of the Deferred object

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.

Get the state of the Deferred object

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

The following code defines two Deferred objects, And use resolve/reject to change their status respectively

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"
Deferred callback function

JQuery provides a method to specify the callback function when the state of the Deferred object changes

Specify the callback function

Use the then method

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 the

defered.resolve function is called. The second function accepts one parameter, which is the first parameter passed when the
defered.reject function is called.

  • Use the then and resolve methods

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'});
  • Use the then and reject methods

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

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

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'});
Use the always method

The always method accepts a function as a parameter, and the function will be called whenever the state of the Deferred object changes.

Chain call

Because 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 objects

JQuery also provides Promise objects, which can be passed through the

promise method of the Deferred object To get the Promise object of this object. =The Promise object has the following characteristics:

  • The Promise object has no

    reject/resolve method

  • The state of the Promise object is consistent with the Deferred object

  • The Promise object obtains the state of the Deferred bound to it through the

    state() method

  • The Proimse object can also use the

    then/done/fail method to specify the callback function

  • Promise can call the promise method to get itself

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() 方法返回的是它自己
The difference between then and done/fail

done/fail returns a Deferred The object itself

Then method returns a new Promise object

Use the done/fail method to return the Deferred object

var d1 = $.Deferred();var d2 = d1.done();var d3 = d1.fail();console.log(d1 === d2); // trueconsole.log(d1 === d3); // true

使用 then 方法返回 Promise 对象

使用 then 方法我们需要明白的几个关键点是:

方法返回的是 Promise 对象,该对象没有 resolve/reject 方法。

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 对象的状态不会立即发生变化

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 的原理,我们就可以使用 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);});

$.when

跟 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!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn