JQuery को स्थगित कैसे किया जा सकता है?


279

jQuery 1.5 नई आस्थगित वस्तु और संलग्न तरीकों को लाता है .when, .Deferredऔर ._Deferred

जिन लोगों ने .Deferredपहले उपयोग नहीं किया है , मैंने इसके लिए स्रोत को एनोटेट किया है

इन नए तरीकों के संभावित उपयोग क्या हैं, हम उन्हें पैटर्न में कैसे फिट करते हैं?

मैंने पहले ही एपीआई और स्रोत को पढ़ा है , इसलिए मुझे पता है कि यह क्या करता है। मेरा सवाल यह है कि हम इन नई सुविधाओं का उपयोग रोजमर्रा के कोड में कैसे कर सकते हैं?

मेरे पास बफर क्लास का एक सरल उदाहरण है जो AJAX अनुरोध को क्रम में कहता है। (पिछले एक खत्म के बाद एक शुरुआत)।

/* Class: Buffer
 *  methods: append
 *
 *  Constructor: takes a function which will be the task handler to be called
 *
 *  .append appends a task to the buffer. Buffer will only call a task when the 
 *  previous task has finished
 */
var Buffer = function(handler) {
    var tasks = [];
    // empty resolved deferred object
    var deferred = $.when();

    // handle the next object
    function handleNextTask() {
        // if the current deferred task has resolved and there are more tasks
        if (deferred.isResolved() && tasks.length > 0) {
            // grab a task
            var task = tasks.shift();
            // set the deferred to be deferred returned from the handler
            deferred = handler(task);
            // if its not a deferred object then set it to be an empty deferred object
            if (!(deferred && deferred.promise)) {
                deferred = $.when();
            }
            // if we have tasks left then handle the next one when the current one 
            // is done.
            if (tasks.length > 0) {
                deferred.done(handleNextTask);
            }
        }
    }

    // appends a task.
    this.append = function(task) {
        // add to the array
        tasks.push(task);
        // handle the next task
        handleNextTask();
    };
};

मैं प्रदर्शनों और के संभावित उपयोगों के लिए देख रहा हूँ .Deferredऔर .when

के उदाहरणों को देखना भी प्यारा होगा ._Deferred

jQuery.ajaxउदाहरण के लिए नए स्रोत से जोड़ना धोखा है।

मुझे विशेष रूप से इस बात में दिलचस्पी है कि जब हम एक ऑपरेशन को सिंक्रोनाइज़ या असिंक्रोनस तरीके से करते हैं तो क्या तकनीक उपलब्ध हैं।


19
एफएक्यू से: व्यक्तिपरक प्रश्न पूछने से बचें जहां ... हर उत्तर समान रूप से मान्य है: "आपका पसंदीदा ______ क्या है?" (उनका जोर)
टीजे क्राउडर

2
@TJCrowser मैं इसे फिर से देख रहा हूँ।
रेयानोस

5
यह एक अच्छा सवाल है, लेकिन ऐसा नहीं हो सकता है कि बहुत से लोग जो जवाब दे सकते हैं :-)
Pointy

2
@ मैं मुख्य रूप से उन लोगों को देख रहा हूं जिन्होंने इसका उपयोग तब किया जब यह एक 3 पार्टी प्लगइन था। और लोगों को बैठकर इसका इस्तेमाल करने के लिए प्रोत्साहित करना!
रेयानोस

1
._Deferredबस सच है "आस्थगित वस्तु" जो .Deferredउपयोग करता है। यह एक आंतरिक वस्तु है जिसकी आपको सबसे अधिक आवश्यकता होगी।
डेविड टैंग

जवाबों:


212

AJAX प्रतिक्रियाओं को कैशिंग करने के लिए मैं सबसे अच्छा उपयोग करने वाला मामला सोच सकता हूं। यहाँ इस विषय पर रेबेका मर्फी के इंट्रो पोस्ट से एक संशोधित उदाहरण दिया गया है :

var cache = {};

function getData( val ){

    // return either the cached value or jqXHR object wrapped Promise
    return $.when(
        cache[ val ] || 
        $.ajax('/foo/', {
            data: { value: val },
            dataType: 'json',
            success: function( resp ){
                cache[ val ] = resp;
            }
        })
    );
}

getData('foo').then(function(resp){
    // do something with the response, which may
    // or may not have been retrieved using an
    // XHR request.
});

मूल रूप से, यदि कैश से तुरंत वापस आने से पहले मूल्य एक बार पहले ही अनुरोध किया जा चुका है। अन्यथा, एक AJAX अनुरोध डेटा प्राप्त करता है और इसे कैश में जोड़ता है। $.when/ .thenइस में से किसी के बारे में परवाह नहीं करता है; आप सभी के बारे में चिंतित होने की जरूरत है प्रतिक्रिया का उपयोग कर रहा है, जो .then()दोनों मामलों में हैंडलर को दिया जाता है । jQuery.when()एक गैर-वादा / स्थगित एक पूर्ण के रूप में संभालता है, तुरंत किसी भी .done()या .then()श्रृंखला पर अमल।

जब कार्य असंबद्ध रूप से संचालित हो सकता है या नहीं हो सकता है, तो आप इसके लिए सही हैं, और आप उस शर्त को कोड से बाहर करना चाहते हैं।

$.whenसहायक का उपयोग करके एक और वास्तविक विश्व उदाहरण :

$.when($.getJSON('/some/data/'), $.get('template.tpl')).then(function (data, tmpl) {

    $(tmpl) // create a jQuery object out of the template
    .tmpl(data) // compile it
    .appendTo("#target"); // insert it into the DOM

});

4
दो शानदार उदाहरण। मैंने 2 वें के समान कुछ लागू किया, लेकिन 4 ajax अनुरोधों के साथ, और यह बहुत अच्छा है, इसके अलावा, अधिक सुपाठ्य, कॉम्पैक्ट, तर्क, बनाए रखने योग्य, आदि के अलावा jQuery.Deferred एक वास्तविक अच्छी बात है।
PJP

5
यहाँ इस विषय पर एक उपयोगी वीडियो है bigbinary.com/videos/3-using-deferred-in-jquery
Nick Vanderbilt

5
यदि परिणाम गलत है, तो कैशिंग काम नहीं करेगा। इसके अलावा, मुझे यह तथ्य पसंद नहीं है कि गेटडाटा ली गई शाखा के आधार पर 2 अलग-अलग प्रकार देता है।
मार्को ड्युमिक

3
अजाक्स कैशिंग के बेहतर कार्यान्वयन के लिए जूलियन डी का जवाब नीचे देखें।
Event_jr

1
मुझे समझ में नहीं आता है कि पहला कोड उदाहरण कैसे काम करता है: मैं उस मामले को समझता हूं जहां ऑब्जेक्ट को कैश नहीं किया गया है, लेकिन अगर यह तब cache[ val ]नहीं है तो एक वादा वापस न करें (jquery प्रलेखन कहता है कि पैरामीटर प्रेषक द्वारा लौटाया गया डेटा है) जिसका अर्थ है .thenविल की सदस्य पहुंच त्रुटि ... सही है? मैं क्या खो रहा हूँ?
चाचम 15

79

यहाँ eHnd के उत्तर के रूप में AJAX कैश का थोड़ा अलग कार्यान्वयन है ।

जैसा कि भाग्य के अनुवर्ती प्रश्न में उल्लेख किया गया है , यदि उनमें से एक के लौटने से पहले अनुरोध किए गए थे, तो ehynd के कार्यान्वयन ने वास्तव में कई समान अनुरोधों को नहीं रोका। अर्थात्,

for (var i=0; i<3; i++) {
    getData("xxx");
}

3 AJAX अनुरोधों में सबसे अधिक संभावना परिणाम होगा यदि "xxx" के लिए परिणाम पहले ही कैश नहीं किया गया है।

परिणाम के बजाय अनुरोध के आस्थगितों को कैशिंग द्वारा हल किया जा सकता है:

var cache = {};

function getData( val ){

    // Return a promise from the cache (if available)
    // or create a new one (a jqXHR object) and store it in the cache.
    var promise = cache[val];
    if (!promise) {
        promise = $.ajax('/foo/', {
            data: { value: val },
            dataType: 'json'
        });
        cache[val] = promise;
    }
    return promise;
}

$.when(getData('foo')).then(function(resp){
    // do something with the response, which may
    // or may not have been retreived using an
    // XHR request.
});

1
मुझे लगता है कि यह अभी भी सही नहीं है, क्योंकि आपने पहली बार एक बार कैश प्राप्त / अपडेट नहीं किया है। यह AJAX कॉल को किसी भी अपडेट के लिए काम नहीं करेगा।
zyzyis

45

म्यूटेक्स के स्थान पर आस्थगित किया जा सकता है। यह मूल रूप से एकाधिक अजाक्स उपयोग परिदृश्यों के समान है।

म्युटेक्स

var mutex = 2;

setTimeout(function() {
 callback();
}, 800);

setTimeout(function() {
 callback();
}, 500);

function callback() {
 if (--mutex === 0) {
  //run code
 }
}

स्थगित

function timeout(x) {
 var dfd = jQuery.Deferred();
 setTimeout(function() {
  dfd.resolve();
 }, x);
 return dfd.promise();
}

jQuery.when(
timeout(800), timeout(500)).done(function() {
 // run code
});

केवल एक म्यूटेक्स के रूप में एक आस्थगित का उपयोग करते समय, प्रदर्शन प्रभावों के लिए देखें (http://jsperf.com/deferred-vs-mutex/2)। हालांकि सुविधा, साथ ही साथ एक Deferred द्वारा आपूर्ति की गई अतिरिक्त लाभ इसके लायक है, और वास्तविक में (उपयोगकर्ता संचालित घटना आधारित) प्रदर्शन प्रभाव ध्यान देने योग्य नहीं होना चाहिए।


यह खोजना मेरे लिए आश्चर्यजनक रूप से कठिन था। मैंने इसे एक समारोह में इस्तेमाल किया जिसमें एक सेटइंटरवल था जो कि हल किए गए वादे को वापस करेगा और एक बार एक निश्चित संख्या से अधिक होने पर div की चौड़ाई को नष्ट कर दिया। यह समस्या निवारण और एक समाधान के लिए था अगर मैं अपने मुद्दे को हल नहीं कर सकता था, लेकिन मैं इसके बारे में खुश हूं।
JSG

28

यह एक स्व-प्रचारक उत्तर है, लेकिन मैंने इस पर शोध करने के लिए कुछ महीने बिताए और परिणामों को jQuery सम्मेलन सैन फ्रांसिस्को 2012 में प्रस्तुत किया।

यहाँ बात का एक नि: शुल्क वीडियो है:

https://www.youtube.com/watch?v=juRtEEsHI9E


20

एक और उपयोग जो मैंने अच्छे उद्देश्य के लिए किया है, वह कई स्रोतों से डेटा प्राप्त करना है। नीचे दिए गए उदाहरण में, मैं एक ग्राहक और एक REST सर्वर के बीच सत्यापन के लिए एक मौजूदा एप्लिकेशन में कई, स्वतंत्र JSON स्कीमा ऑब्जेक्ट का उपयोग कर रहा हूं। इस स्थिति में, मैं नहीं चाहता कि ब्राउज़र-साइड एप्लिकेशन को डेटा लोड करने से पहले शुरू किया जाए, क्योंकि इसमें सभी स्कीम लोड हैं। $ .when.apply ()। तब () इसके लिए एकदम सही है। त्रुटि स्थितियों की निगरानी के लिए (fn1, fn2) का उपयोग करने पर संकेत के लिए Raynos को धन्यवाद।

fetch_sources = function (schema_urls) {
    var fetch_one = function (url) {
            return $.ajax({
                url: url,
                data: {},
                contentType: "application/json; charset=utf-8",
                dataType: "json",
            });
        }
    return $.map(schema_urls, fetch_one);
}

var promises = fetch_sources(data['schemas']);
$.when.apply(null, promises).then(

function () {
    var schemas = $.map(arguments, function (a) {
        return a[0]
    });
    start_application(schemas);
}, function () {
    console.log("FAIL", this, arguments);
});     

10

Deferredकिसी भी प्रकार की संगणना के लिए कैश को लागू करने के लिए एस का उपयोग करने वाला एक अन्य उदाहरण (आमतौर पर कुछ प्रदर्शन-गहन या लंबे समय तक चलने वाले कार्य):

var ResultsCache = function(computationFunction, cacheKeyGenerator) {
    this._cache = {};
    this._computationFunction = computationFunction;
    if (cacheKeyGenerator)
        this._cacheKeyGenerator = cacheKeyGenerator;
};

ResultsCache.prototype.compute = function() {
    // try to retrieve computation from cache
    var cacheKey = this._cacheKeyGenerator.apply(this, arguments);
    var promise = this._cache[cacheKey];

    // if not yet cached: start computation and store promise in cache 
    if (!promise) {
        var deferred = $.Deferred();
        promise = deferred.promise();
        this._cache[cacheKey] = promise;

        // perform the computation
        var args = Array.prototype.slice.call(arguments);
        args.push(deferred.resolve);
        this._computationFunction.apply(null, args);
    }

    return promise;
};

// Default cache key generator (works with Booleans, Strings, Numbers and Dates)
// You will need to create your own key generator if you work with Arrays etc.
ResultsCache.prototype._cacheKeyGenerator = function(args) {
    return Array.prototype.slice.call(arguments).join("|");
};

यहाँ कुछ (नकली भारी) गणना करने के लिए इस वर्ग का उपयोग करने का एक उदाहरण है:

// The addingMachine will add two numbers
var addingMachine = new ResultsCache(function(a, b, resultHandler) {
    console.log("Performing computation: adding " + a + " and " + b);
    // simulate rather long calculation time by using a 1s timeout
    setTimeout(function() {
        var result = a + b;
        resultHandler(result);
    }, 1000);
});

addingMachine.compute(2, 4).then(function(result) {
    console.log("result: " + result);
});

addingMachine.compute(1, 1).then(function(result) {
    console.log("result: " + result);
});

// cached result will be used
addingMachine.compute(2, 4).then(function(result) {
    console.log("result: " + result);
});

उसी अंतर्निहित कैश का उपयोग Ajax अनुरोधों को कैश करने के लिए किया जा सकता है:

var ajaxCache = new ResultsCache(function(id, resultHandler) {
    console.log("Performing Ajax request for id '" + id + "'");
    $.getJSON('http://jsfiddle.net/echo/jsonp/?callback=?', {value: id}, function(data) {
        resultHandler(data.value);
    });
});

ajaxCache.compute("anID").then(function(result) {
    console.log("result: " + result);
});

ajaxCache.compute("anotherID").then(function(result) {
    console.log("result: " + result);
});

// cached result will be used
ajaxCache.compute("anID").then(function(result) {
    console.log("result: " + result);
});

आप इस jsFiddle में उपरोक्त कोड के साथ खेल सकते हैं ।


9

1) कॉलबैक के आदेशित निष्पादन को सुनिश्चित करने के लिए इसका उपयोग करें:

var step1 = new Deferred();
var step2 = new Deferred().done(function() { return step1 });
var step3 = new Deferred().done(function() { return step2 });

step1.done(function() { alert("Step 1") });
step2.done(function() { alert("Step 2") });
step3.done(function() { alert("All done") });
//now the 3 alerts will also be fired in order of 1,2,3
//no matter which Deferred gets resolved first.

step2.resolve();
step3.resolve();
step1.resolve();

2) एप्लिकेशन की स्थिति को सत्यापित करने के लिए इसका उपयोग करें:

var loggedIn = logUserInNow(); //deferred
var databaseReady = openDatabaseNow(); //deferred

jQuery.when(loggedIn, databaseReady).then(function() {
  //do something
});

2

आप एक द्रव डिजाइन बनाने के लिए एक आस्थगित वस्तु का उपयोग कर सकते हैं जो वेबकिट ब्राउज़र में अच्छी तरह से काम करता है। वेबकिट ब्राउज़र एफएफ और आईई के विपरीत प्रत्येक पिक्सेल के लिए विंडो का आकार बदलने वाली घटना का आकार बदल देगा। नतीजतन, आपके पास उस क्रम पर कोई नियंत्रण नहीं है जिसमें आपकी विंडो आकार परिवर्तन की घटना से संबंधित कार्य निष्पादित होंगे। कुछ इस तरह समस्या हल करती है:

var resizeQueue = new $.Deferred(); //new is optional but it sure is descriptive
resizeQueue.resolve();

function resizeAlgorithm() {
//some resize code here
}

$(window).resize(function() {
    resizeQueue.done(resizeAlgorithm);
});

यह आपके कोड के निष्पादन को क्रमबद्ध करेगा जिससे कि आप इसे निष्पादित करें जैसा कि आप इसे करना चाहते हैं। जब एक आस्थगित करने के लिए कॉलबैक के रूप में वस्तु तरीकों से गुजरने से नुकसान से सावधान रहें। एक बार इस तरह की विधि को आस्थगित करने के लिए कॉलबैक के रूप में निष्पादित किया जाता है, तो 'इस' संदर्भ को आस्थगित ऑब्जेक्ट के संदर्भ में अधिलेखित कर दिया जाएगा और वह विधि उस ऑब्जेक्ट के लिए संदर्भित नहीं होगी जो विधि से संबंधित है।


यह किसी भी क्रम को कैसे करता है? आपने पहले से ही कतार को हल कर लिया है, resizeQueue.done(resizeAlgorithm)जैसा कि ठीक वैसा ही है resizeAlgorithm। यह पूरी तरह से शर्मनाक है!
रेयानोस

जब आपका आकार बदलने का कोड जटिल होता है, तो वेबकिट में जावास्क्रिप्ट का कार्यान्वयन तब सिंक्रनाइज़ हो जाएगा जब विंडो के आकार वाले प्रत्येक पिक्सेल के लिए फ़ंक्शन को कॉल किया जाता है। आस्थगित आपके कॉलबैक को एक कतार में रखता है और उन्हें FIFO क्रम में निष्पादित करता है। इसलिए, यदि आप एक 'किया' कॉलबैक जोड़ते हैं और यह तुरंत निष्पादित होता है क्योंकि आस्थगित पहले से ही हल हो गया है, तो एक अन्य 'किया' कॉलबैक को हटा दिया गया है जबकि पहला कॉलबैक अभी भी निष्पादित हो रहा है और कतार में जोड़ा जाएगा और इंतजार करना होगा वापसी के लिए पहली कॉलबैक के लिए। हम उम्मीद करते है कि यह आपके सवाल का जवाब दे देगा।
मिलो राच्य

ब्राउज़र में JS दुभाषिया सिंगल थ्रेडेड है। जब तक कि आपके resizeAlgorithm के अंदर कुछ async कोड नहीं होता है, तो अगली कॉल करने .doneसे पहले पूरे फ़ंक्शन को पूरा करना चाहिए ।
रेयनोस

@Raynos: मैं इसके बारे में जानता हूं, लेकिन मैंने रिसाइज पर एल्गोरिदम को केवल कॉल करने की कोशिश की और यह दूसरों में पूरी तरह से काम करते हुए वेबकिट ब्राउज़र में एक खाली सफेद पेज देता है। आस्थगित इस समस्या को हल करता है। मेरे पास इस बारे में कुछ गहन शोध करने के लिए पर्याप्त समय नहीं है। एक वेबकिट बग हो सकता है। मुझे नहीं लगता कि मेरे उदाहरण में उपयोग किए गए आस्थगित यदि resizeAl एल्गोरिदम कुछ अतुल्यकालिक कोड था मदद मिलेगी।
मिलो रासिक

2
क्या आपको अपने कार्यों को रोकने के लिए थ्रॉटल / डेब्यू प्लग इन benalman.com/projects/jquery-throttle-debounce-plugin जैसी चीज़ों का उपयोग नहीं करना चाहिए ताकि एक बार आकार बदलने के बाद अधिक टैन फायरिंग हो सके।
wheresrhys

2

आप इसे किसी भी 3-पार्टी लाइब्रेरी के साथ एकीकृत कर सकते हैं जो JQuery का उपयोग करती है।

ऐसी ही एक लाइब्रेरी है बैकबोन, जो वास्तव में अपने अगले संस्करण में डिफर्ड को सपोर्ट करने वाली है।


2
के read more hereस्थान पर उपयोग करें on my blog। इसका बेहतर अभ्यास है और आप (गलती से) अनचाहे से जवाब दे सकते हैं। :)
लोकेश मेहरा

1

मैंने वास्तविक कोड में सिर्फ Deferred का उपयोग किया है। प्रोजेक्ट jQuery टर्मिनल में मैंने फ़ंक्शन निष्पादित किया है जो कि उपयोगकर्ता द्वारा परिभाषित कॉल कमांड्स (जैसे वह इसे दर्ज कर रहा था और एंटर दबा रहा था), मैंने एपीआई में डीएफ़रड्स जोड़ दिया है और सरणियों के साथ कॉल निष्पादित किया है। इस तरह:

terminal.exec('command').then(function() {
   terminal.echo('command finished');
});

या

terminal.exec(['command 1', 'command 2', 'command 3']).then(function() {
   terminal.echo('all commands finished');
});

आदेश async कोड चला सकते हैं, और आदेश में उपयोगकर्ता कोड को कॉल करने की आवश्यकता है। मेरा पहला एपीआई पोज़ / रिज्यूमे कॉल की जोड़ी का उपयोग करता है और नए एपीआई में मैं उन स्वचालित को कॉल करता हूं जब उपयोगकर्ता वापसी का वादा करता है। तो उपयोगकर्ता कोड का उपयोग कर सकते हैं

return $.get('/some/url');

या

var d = new $.Deferred();
setTimeout(function() {
    d.resolve("Hello Deferred"); // resolve value will be echoed
}, 500);
return d.promise();

मैं इस तरह कोड का उपयोग करता हूं:

exec: function(command, silent, deferred) {
    var d;
    if ($.isArray(command)) {
        return $.when.apply($, $.map(command, function(command) {
            return self.exec(command, silent);
        }));
    }
    // both commands executed here (resume will call Term::exec)
    if (paused) {
        // delay command multiple time
        d = deferred || new $.Deferred();
        dalyed_commands.push([command, silent, d]);
        return d.promise();
    } else {
        // commands may return promise from user code
        // it will resolve exec promise when user promise
        // is resolved
        var ret = commands(command, silent, true, deferred);
        if (!ret) {
            if (deferred) {
                deferred.resolve(self);
                return deferred.promise();
            } else {
                d = new $.Deferred();
                ret = d.promise();
                ret.resolve();
            }
        }
        return ret;
    }
},

dalyed_commands का उपयोग फिर से शुरू होने वाले फ़ंक्शन में किया जाता है जो सभी dalyed_commands के साथ फिर से कॉल निष्पादित करता है।

और कमांड फ़ंक्शन का एक हिस्सा (मैंने संबंधित भागों को नहीं छीन लिया है)

function commands(command, silent, exec, deferred) {

    var position = lines.length-1;
    // Call user interpreter function
    var result = interpreter.interpreter(command, self);
    // user code can return a promise
    if (result != undefined) {
        // new API - auto pause/resume when using promises
        self.pause();
        return $.when(result).then(function(result) {
            // don't echo result if user echo something
            if (result && position === lines.length-1) {
                display_object(result);
            }
            // resolve promise from exec. This will fire
            // code if used terminal::exec('command').then
            if (deferred) {
                deferred.resolve();
            }
            self.resume();
        });
    }
    // this is old API
    // if command call pause - wait until resume
    if (paused) {
        self.bind('resume.command', function() {
            // exec with resume/pause in user code
            if (deferred) {
                deferred.resolve();
            }
            self.unbind('resume.command');
        });
    } else {
        // this should not happen
        if (deferred) {
            deferred.resolve();
        }
    }
}

1

Ehynds द्वारा उत्तर देने से काम नहीं चलेगा, क्योंकि यह प्रतिक्रियाओं के डेटा को कैश करता है। यह jqXHR को कैश करना चाहिए जो एक वादा भी है। यहाँ सही कोड है:

var cache = {};

function getData( val ){

    // return either the cached value or an
    // jqXHR object (which contains a promise)
    return cache[ val ] || $.ajax('/foo/', {
        data: { value: val },
        dataType: 'json',
        success: function(data, textStatus, jqXHR){
            cache[ val ] = jqXHR;
        }
    });
}

getData('foo').then(function(resp){
    // do something with the response, which may
    // or may not have been retreived using an
    // XHR request.
});

जूलियन डी का जवाब सही काम करेगा और एक बेहतर समाधान है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.