Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to analyze Deferred with jQuery 2.0.3 source code

2025-03-29 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article is about how to use jQuery 2.0.3 source code to analyze Deferred, the editor thinks it is very practical, so I share it with you to learn. I hope you can get something after reading this article, without saying much, follow the editor to have a look.

Flowchart when building Deferred objects

* * Source code parsing * *

Because after the callback is stripped out, the whole deferred appears to be very concise.

JQuery.extend ({Deferred: function () {} when: function ())}

With regard to the inheritance of extend, I mentioned earlier how jquery handles the problem of internal jquery and init referencing this each other.

Http://www.cnblogs.com/aaronjs/p/3278578.html must be understood for the overall structure of JQ.

So when jQuery.extend has only one parameter, it is actually an extension of the jQuery static method.

Let's take a specific look at what is done inside the two static methods:

The overall structure of Deferred:

The source code simplifies part of the code.

Deferred: function (func) {var tuples = [/ / action, add listener, listener list, final state ["resolve", "done", jQuery.Callbacks ("once memory"), "resolved"], ["reject", "fail", jQuery.Callbacks ("once memory"), "rejected"], ["notify", "progress" JQuery.Callbacks ("memory")]], state = "pending", promise = {state: function () {}, always: function () {}, then: function (/ * fnDone, fnFail, fnProgress * /) {} / / Get a promise for this deferred / / If obj is provided, the promise aspect is added to the object promise: function (obj) {}}, deferred = {} JQuery.each (tuples, function (I, tuple) {deferred [tuple [0] + "With"] = list.fireWith;}); promise.promise (deferred); / / All done! Return deferred;}

It is obvious that Deferred is a factory class that returns an internally built deferred object

Tuples creates three $. Calling objects, which represent success, failure, and three states in processing

Create a promise object with state, always, then, primise methods

Extend the primise object to generate the final Deferred object and return it

Here are actually three processes, but there is a place to optimize the code, that is, the common code is abstracted and dynamically generated.

Specific source code analysis:

Deferred itself makes a higher-level abstraction around these three objects.

Trigger callback function list execution (function name)

Add callback function (function name)

Callback function list (jQuery.Callbacks object)

Deferred final status (except for the third set of data)

Var tuples = [/ / action, add listener, listener list, final state ["resolve", "done", jQuery.Callbacks ("once memory"), "resolved"], ["reject", "fail", jQuery.Callbacks ("once memory"), "rejected"], ["notify", "progress", jQuery.Callbacks ("memory")]]

Two groups of camps are abstracted here:

Group 1: callback method / event subscription

Done,fail,progress

2 groups: notification method / event release

Resolve,reject,notify,resolveWith,rejectWith,notifyWith

The tuples element set is actually a combination of the same code with common characteristics into a structure, and then processed at once.

JQuery.each (tuples, function (I, tuple) {var list = tuple [2], stateString = tuple [3]; promise [tuple [1]] = list.add; if (stateString) {list.add (function () {state = stateString) / / [reject_list | resolve_list] .disable; progress_list.lock}, tuples [I ^ 1] [2] .disable, tuples [2] [2] .lock);} deferred [tuple [0]] = function () {deferred [tuple [0] + "With"] (this = deferred? Promise: this, arguments); return this;}; deferred [tuple [0] + "With"] = list.fireWith;})

The three datasets of tuples are processed in two parts.

The first part stores the callback function into the

Promise [tuple [1]] = list.add

In fact, it is to give promise three callback functions.

Promise.done = $.Callflower ("once memory"). Addpromise.fail = $.Callflower ("once memory"). Addpromise.progressl = $.Callflower ("memory") .add

If there is a deferred final state

By default, three callback functions are added to list in doneList,failList in advance.

If (stateString) {list.add (function () {/ / state = [resolved | rejected] state = stateString; / / [reject_list | resolve_list] .disable; progress_list.lock}, tuples [I ^ 1] [2] .disable, tuples [2] [2] .disable);}

* * *

Here's a little trick.

I ^ 1 bitwise XOR operator

So in fact, the second pass parameter is 1, 0 index is switched, so the values are failList.disable and doneList.disable

* * *

Through the condition that stateString has a value, add three callback functions to list in doneList,failList in advance.

They are:

DoneList: [changeState, failList.disable, processList.lock] failList: [changeState, doneList.disable, processList.lock]

The anonymous function of changeState to change state, the state of deferred, is divided into three types: pending (initial state), resolved (resolution state), and rejected (reject state).

Regardless of whether the deferred object is ultimately resolve (or reject), another function list failList (or doneList) will be disable after the object's state is changed first.

Then lock processList maintains its state, and finally executes the callback function that done (or fail) came in before.

So the first step finally revolves around this add method.

Done/fail/ is list.add or callbacks.add, which stores the callback function in the callback object

The second part is very simple, extending 6 methods to the deferred object

Resolve/reject/notify is callbacks.fireWith, execute callback function

ResolveWith/rejectWith/notifyWith is a callbacks.fireWith queue method reference

Finally merge promise into deferred

Promise.promise (deferred); jQuery.extend (obj, promise)

So in the end, the asynchronous object built through the factory method Deferred has all the methods

The deferred object inside return.

It can be seen that we are in

Var defer = $.Deferred (); / / build asynchronous objects

The internal object has four property methods

Deferred: Object

Always: function () {

Done: function () {

Fail: function () {

Notify: function () {

NotifyWith: function (context, args) {

Pipe: function (/ * fnDone, fnFail, fnProgress * /) {

Progress: function () {

Promise: function (obj) {

Reject: function () {

RejectWith: function (context, args) {

Resolve: function () {

ResolveWith: function (context, args) {

State: function () {

Then: function (/ * fnDone, fnFail, fnProgress * /) {

Promise: Object

Always: function () {

Done: function () {

Fail: function () {

Pipe: function (/ * fnDone, fnFail, fnProgress * /) {

Progress: function () {

Promise: function (obj) {

State: function () {

Then: function (/ * fnDone, fnFail, fnProgress * /) {

State: "pending"

Tuples: Array [3]

Structural diagram

The above is just when initializing the build, let's look down at the dynamic execution processing.

* execution period *

An example of the simplest demo

Var d = $.Deferred (); setTimeout (function () {d.resolve (22)}, 0); d.then (function (val) {console.log (val);})

When a deferred object is resolved, any doneCallbacks added through deferred.then or deferred.done will be called. Callback functions are executed in the same order in which they are added. The args argument passed to deferred.resolve () is passed to each callback function. When the delay object enters the resolved state, any additional doneCallbacks that is added will be executed immediately, with the parameters passed to .delay ().

In other words, when we call d.resolve (22), we call.

Anonymous function and pass in the parameter value 22

Function (val) {console.log (val); / / 22}

There are all kinds of complex combinations in the current actual use, but the whole external invocation process is like this.

* implementation of resolve *

In retrospect, the internal implementation of the Deferred object is still the Callbacks object, but there is another layer of API encapsulated on the outside for the interface to call.

D.resolve (22)

What is actually called is generated through this code.

Deferred [tuple [0]] = function () {deferred [tuple [0] + "With"] (this = = deferred? Promise: this, arguments); return this;}; deferred [tuple [0] + "With"] = list.fireWith;deferred.resolveWith ()

The final execution is list.fireWith.

Callbacks.fireWith ()

So it finally goes back to the private method fire () in the callback object callbacks

Callbacks will pass

Callbacks.add ()

Register the callback function to the internal list = []. Let's go back and have a look.

Deferred.then ()

D.then (function (val) {console.log (val);})

* implementation of then *

Then: function (/ * fnDone, fnFail, fnProgress * /) {var fns = arguments; return jQuery.Deferred (function (newDefer) {jQuery.each (tuples, function (I, tuple) {var action = tuple [0], fn = jQuery.isFunction (fns [I]) & & fns [I] / / deferred [done | fail | progress] for forwarding actions to newDefer deferred [tuple [1]] (function () {/ / omitted. }); fns = null;}) .promise ();}

Recursive jQuery.Deferred

Passed func

The chain calls promise ()

Because the methods of asynchronous objects are nested to find scoped property methods.

Here I would like to mention the scope in addition.

Var d = $.Deferred ()

What is the scope of this asynchronous object d?

Layer 1: it is indisputable that the outermost layer of the browser environment is window

Layer 2: jquery itself is a closure

Layer 3: the scope generated by the Deferred factory method

What if you use the d.then () method?

It is obvious that the then method is also a nested function, so the execution will include the scope generated by the above three layers of scope + its own function by default.

Let's describe it with a simple picture.

According to the rules, the innermost function can access all variables in the upper scope

Let's first consider the structural design from the perspective of usage:

Demo 1

Var defer = $.Deferred (); var filtered = defer.then (function (value) {return value * 2;}); defer.resolve (5); filtered.done (function (value) {console.log (value) / / 10})

Demo 2

Var defer = $.Deferred (); defer.then (function (value) {return value * 2;}). Then (function (value) {return value * 2;}) .done (function (value) {alert (value) / / 20}); defer.resolve (5)

In fact, this is related to defer.then (). Then (). Done () chain call

API is defined as this:

Deferred.then (doneFilter [, failFilter] [, progressFilter])

Starting with jQuery 1.8, the method returns a new promise (commitment), and the status and value of the deferred (delay) can be filtered through a function. Replace the now obsolete deferred.pipe () method. The doneFilter and failFilter functions filter the status and value of the resolution / rejection of the original deferred (delay). The progressFilter function filters any calls to the original deferred (deferred) notify and notifyWith methods. These filter functions can return a callback of .done () or .fail () passed by a new value to promise (promise), or they can return a callback of resolution / reject status and value promise (commitment) passed to it by another observed object (deferred, commitment, etc.). If the filter function is empty or not specified, promise (promise) will get the same resolution (resolved) or reject (rejected) as the original value.

Let's grasp the following points:

The new promise object is returned

There is a filter function inside

We can see from demo 1

The this (filtered) returned in the code processed by the x.then () method is not the asynchronous object (defer) generated by the original $.Deferred ().

So, every this that goes through an then is reset, so why do you do it this way?

Source code

Then: function (/ * fnDone, fnFail, fnProgress * /) {var fns = arguments / / add callback functions for the three callbacklist of deferred, depending on whether the fn is a function or not There are two cases return jQuery.Deferred (function (newDefer) {jQuery.each (tuples, function (I, tuple) {var action = tuple [0], fn = jQuery.isFunction (fns [I]) & & fns [I]) / / deferred [done | fail | progress] for forwarding actions to newDefer deferred [tuple [1]] (function () {var returned = fn & & fn.apply (this, arguments) If (returned & & jQuery.isFunction (returned.promise)) {returned.promise () .done (newDefer.resolve) .fail (newDefer.reject) .progress (newDefer.notify) } else {newDefer [action + "With"] (this = = promise? NewDefer.promise (): this, fn? [returned]: arguments);}});}); fns = null;}) .promise ();}

A flag,jQuery.Deferred (func) is supported when Deferred passes arguments.

Pass a callback function

/ / Call given func if any if (func) {func.call (deferred, deferred);}

So newDefer can be seen as

NewDefer = $.Deferred ()

So the func callback handles the filter function.

Deferred [tuple [1]] (function () {var returned = fn & & fn.apply (this, arguments); if (returned & & jQuery.isFunction (returned.promise)) {returned.promise () .done (newDefer.resolve) .fail (newDefer.reject) .progress (newDefer.notify);} else {newDefer [action + "With"] (this = = promise? NewDefer.promise (): this, fn? [returned]: arguments);}})

In fact, there is also the concept of compiling functions, saying that the code to be executed in the future will be saved in advance through closure functions to make them access their respective scopes.

The first step

Decompose the set of tuples elements

JQuery.each (tuples, function (I, tuple) {/ / filter function first step processing})

Step two

Execute the corresponding add method for deferred [done | fail | progress], and add a filter function to done | fail | progress method

Deferred [tuple [1]] (incoming filter function) / / decompose when the filter function is executed

The code is

Deferred [done] = list.add = callback.add

Step three

Returns return jQuery.Deferred () .promise ()

A new Deferred object is built, but what is returned is processed by the promise () method, and a restricted promise object is returned.

So the whole then method deals with two things.

Build a new deferred object that returns a restricted promise object

Add a filter function to the [done | fail | progress] method of the parent deferred object

We know that the defer.then method returns a new jQuery.Deferred (). Promise () object

So we call what defer.then returns a child object, so how does it relate to the parent object var defer = $.Deferred ()?

Let me see the source code.

Deferred [tuple [1]] (/ / filter function / /)

Deferred is actually a reference to the parent object at the root level, so no matter how deep the nesting is, the parent object deferred [done | fail | progress executes add is called.

It is obvious from the figure that done fail progress saves different processing callbacks in two different deferred objects.

Deferred.resolve (args)

When a deferred object is resolved, any doneCallbacks added through deferred.then or deferred.done will be called

Callback functions are executed in the same order as they are added.

The args argument passed to deferred.resolve (), which is passed to each callback function

When the delay object enters the resolved state, any additional doneCallbacks that is added will be executed immediately, with the parameters passed to .delay ().

The process is shown in the figure

Process resolution:

1 executes the fire () method, and recursively executes all the processing methods contained in list

2 executed the default changeState, disable, lock methods,

3 execute filter function

According to whether the return value of var returned = fn.apply (this, arguments) (called returnReferred) is a deferred object

The return value is the deferred object, so add the resolve (reject,notify) method of newDeferred to the list of three callback functions in the returnReferred object, that is, the execution of newDeferrred depends on the state of returnDeferred

If it is not a function (such as undefined or null), link directly to the resolve (reject,notify) method of newDeferred, that is, the execution of newDeferrred depends on the state of the outer caller deferred or whether to perform the action (resolve or reject or notify). In this case, deferred.then () is equivalent to connecting its own callbacklist with the callbacklist of newDeferred.

Here is the partition of nested deferred objects

The above is how to use jQuery 2.0.3 source code to analyze Deferred, the editor believes that there are some knowledge points that we may see or use in our daily work. I hope you can learn more from this article. For more details, please follow the industry information channel.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report