📜 ⬆️ ⬇️

eachDeferred - delayed processing of the collection, one by one

During the development of the current enterprise project, it was necessary to implement deferred processing of a collection of jQuery elements - there was a set of widgets whose contents needed to be loaded in turn, and the load was asynchronous. I had to write a small extension to $.fn - eachDeferred .

The idea is quite simple - we have a collection of jQuery elements that need to be processed in a loop, and the iteration should occur only after the end of the processing of the current element.

Please note that the subject of discussion involves the knowledge of working with $.Deferred jQuery objects.

 (function ($) { $.fn.extend({ /* * Iterates over jQuery object collection using deferred callbacks. * the function assigned for iterator should return promise. * resolved promises notify the main deferred, so we can track each loop result. * returns promise. */ eachDeferred: function (c) { var that = this, dfd = $.Deferred(), elms = $.makeArray(that), i = elms.length, next = function () { setTimeout(function () { elms.length ? cb(elms.shift()) : dfd.resolve(that); }, 0); }, cb = function (elm) { $.when(c.call(elm, i - elms.length, $(elm))).done(function (result) { dfd.notify(result); next(); }); }; next(); return dfd.promise(); } }); })(jQuery); 

The iteration function must return a $.Deferred object to determine when the current iteration is finished. The plug-in code itself eachDeferred also returns a $.Deferred object that can be used in the call chain. The object notifies about the results of each iteration through notify/progress , passing the result of the iteration ( arg , which can be returned from $.Deferred.resolve(arg) of the loop handler function) to the code of the .progress(function(arg){}) handler .progress(function(arg){}) ( if available).
')
Small copy / paste from code

 that.widgets.eachDeferred(function (i, widget) { return that.renderContent(widget); }).done(dfd.resolve); 

renderContent asynchronously pulls data from the server and displays the contents of the widget. Visually, it feels like each widget is loading in turn. In the case of using the usual $.Deferred ( $.get().success() ) mechanism, the widgets would be displayed randomly - which would rather load.

Another example
 var dfd = $.Deferred(), _r = []; xml.children("Tree").eachDeferred(function (i, xmlNode) { return that._buildTreeContents(xmlNode); }).progress(function (treeContents) { _r.push(treeContents); }).done(function () { dfd.resolve(_r); }); return dfd.promise(); 

_buildTreeContents asynchronously builds the tree _buildTreeContents , passing us the result of each iteration (we save it into an array). At the end of the collection processing, we have a common tree markup, which we can insert into the page.

Such a mechanism allows asynchronously to process synchronous (static) collections.

A small working example.

PS It may seem strange timeout inside the function next() . The timeout is needed because of the jQuery code - the .notify() call in it occurs after the same timeout. This is done so that the browser can complete the processing of the current Javascript VM stack.

If you do not make a timeout in my code, the call to the next iteration will fall into the normal stack, and it will be called before .notify() - we also need to notify the end of the iteration first, and then proceed to the next one.

Source: https://habr.com/ru/post/143592/


All Articles