जारी रखने से पहले समाप्त होने के लिए एक फ़ंक्शन की प्रतीक्षा करने का उचित तरीका?


187

मेरे दो जेएस कार्य हैं। एक दूसरे को बुलाता है। कॉलिंग फ़ंक्शन के भीतर, मैं दूसरे को कॉल करना चाहता हूं, उस फ़ंक्शन को समाप्त करने के लिए प्रतीक्षा करें, फिर जारी रखें। इसलिए, उदाहरण के लिए / छद्म कोड:

function firstFunction(){
    for(i=0;i<x;i++){
        // do something
    }
};

function secondFunction(){
    firstFunction()
    // now wait for firstFunction to finish...
    // do something else
};

मैं इस समाधान के साथ आया था, लेकिन यह नहीं जानता कि क्या यह इसके बारे में जाने का एक स्मार्ट तरीका है।

var isPaused = false;

function firstFunction(){
    isPaused = true;
    for(i=0;i<x;i++){
        // do something
    }
    isPaused = false;
};

function secondFunction(){
    firstFunction()
    function waitForIt(){
        if (isPaused) {
            setTimeout(function(){waitForIt()},100);
        } else {
            // go do that thing
        };
    }
};

क्या यह वैध है? क्या इसे संभालने का अधिक सुरुचिपूर्ण तरीका है? शायद jQuery के साथ?


11
firstFunctionवास्तव में ऐसा क्या है जो इसे अतुल्यकालिक बनाता है? किसी भी तरह से - वादों के बारे में जाँच करें
zerkms

पहला फ़ंक्शन तेजी से सेकंड के हर 10 वें स्कोर स्कोर को अपडेट कर रहा है। जो ... यह सोचने के लिए आता है, मुझे लगता है कि हम पूर्व-गणना कर सकते हैं और फिर मैन्युअल रूप से सेटटाइमआउट के माध्यम से दूसरे फ़ंक्शन कॉल को रोक सकते हैं। उस ने कहा, मैं अभी भी कहीं और विराम देने की इच्छा देख सकता हूं।
डीए।

यदि आप एक ठहराव की जरूरत नहीं है - jQuery में वादों के लिए गूगल
zerkms

@zerkms के वादे दिलचस्प लग रहे हैं! अभी भी ब्राउज़र समर्थन की जांच ...
डीए

1
मेरे साथ भी यही समस्या है।
वापोर वॉशमेड

जवाबों:


142

इस तरह के अतुल्यकालिक काम से निपटने का एक तरीका कॉलबैक फ़ंक्शन का उपयोग करना है, जैसे:

function firstFunction(_callback){
    // do some asynchronous work
    // and when the asynchronous stuff is complete
    _callback();    
}

function secondFunction(){
    // call first function and pass in a callback function which
    // first function runs when it has completed
    firstFunction(function() {
        console.log('huzzah, I\'m done!');
    });    
}

@ जानका पुष्पकुमारा के सुझाव के अनुसार, अब आप एक ही चीज़ को प्राप्त करने के लिए तीर फ़ंक्शन का उपयोग कर सकते हैं। उदाहरण के लिए:

firstFunction(() => console.log('huzzah, I\'m done!'))


अपडेट: मैंने कुछ समय पहले इसका उत्तर दिया था, और वास्तव में इसे अपडेट करना चाहता हूं। जबकि कॉलबैक बिल्कुल ठीक हैं, मेरे अनुभव में वे कोड में परिणाम देते हैं जो पढ़ना और बनाए रखना अधिक कठिन है। ऐसी परिस्थितियां हैं जहां मैं अभी भी उनका उपयोग करता हूं, जैसे कि प्रगति की घटनाओं में गुजरना और मापदंडों की तरह। यह अपडेट सिर्फ विकल्पों पर जोर देने के लिए है।

मूल प्रश्न भी विशिष्ट रूप से उल्लेख नहीं करता है, इसलिए यदि कोई व्यक्ति उलझन में है, यदि आपका फ़ंक्शन सिंक्रोनस है, तो इसे कॉल करने पर ब्लॉक हो जाएगा । उदाहरण के लिए:

doSomething()
// the function below will wait until doSomething completes if it is synchronous
doSomethingElse()

यदि यद्यपि निहित कार्य असिंक्रोनस है, तो जिस तरह से मैं अपने सभी अतुल्यकालिक कार्यों से निपटता हूं, वह आज एस्किंट / वेट के साथ है। उदाहरण के लिए:

const secondFunction = async () => {
  const result = await firstFunction()
  // do something else here after firstFunction completes
}

IMO, async / प्रतीक्षा आपके कोड को सीधे वादों का उपयोग करने की तुलना में बहुत अधिक पठनीय बनाता है (अधिकांश समय)। यदि आपको कैचिंग एरर को हैंडल करने की आवश्यकता है, तो ट्राय / कैच के साथ इसका उपयोग करें। इसके बारे में यहां और पढ़ें: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function


9
@zerkms - विस्तृत देखभाल करने के लिए?
मैट वे

7
कॉलबैक असुविधाजनक से निपटने के लिए असुविधाजनक हैं, वादे बहुत आसान और लचीले हैं
झटके

1
जब आप सही हैं कि मुझे सबसे अच्छा शब्द (अद्यतन) का उपयोग नहीं करना चाहिए, तो कॉलबैक बनाम वादों की सुविधा समस्या की जटिलता पर निर्भर करती है।
मैट वे

3
यह अपमानजनक हो जाता है, लेकिन मैं व्यक्तिगत रूप से इन दिनों कॉलबैक पसंद करने का एक कारण नहीं देखता हूं :-) पिछले 2 वर्षों में लिखे गए मेरे किसी भी कोड को याद नहीं कर सकता है जो वादों पर कॉलबैक प्रदान करता है।
झटके

3
यदि आप चाहें, तो आप es6 सेकंड फंक्शन () {firstFunction ((प्रतिक्रिया) => {कंसोल.लॉग (प्रतिक्रिया);}) में एरो फंक्शन का उपयोग कर सकते हैं; }
जनक पुष्पकुमारा

53

Async / प्रतीक्षा का उपयोग करें:

async function firstFunction(){
  for(i=0;i<x;i++){
    // do something
  }
  return;
};

फिर अपने अन्य फ़ंक्शन में प्रतीक्षा करने के लिए इसे वापस आने के लिए प्रतीक्षा करें:

async function secondFunction(){
  await firstFunction();
  // now wait for firstFunction to finish...
  // do something else
};

9
हममें से जो पुराने ब्राउज़रों का समर्थन करते हुए फंस गए हैं, IE
kyle

क्या इसका उपयोग दोनों कार्यों के बाहर किया जा सकता है, जैसे कि, में $(function() { await firstFunction(); secondFunction(); });?
लुईसट्रिक

1
@ Lewistrick बस awaitएक asyncविधि के अंदर ही इस्तेमाल किया जा सकता है याद रखें । इसलिए यदि आप अपना अभिभावक कार्य करते हैं, तो आप asyncजितने async methodsके साथ या उसके बिना अंदर बुला सकते awaitहैं।
फवाज

क्या यह संभव awaitहै setTimeout?
शयन

1
@ शायन हाँ यह एक और कार्य है, जो टाइमआउट के बाद एक वादा हल करता है।
फवाज

51

ऐसा प्रतीत होता है कि आप यहां एक महत्वपूर्ण बिंदु याद कर रहे हैं: जावास्क्रिप्ट एक एकल-थ्रेडेड निष्पादन वातावरण है। आइए फिर से अपने कोड को देखें, ध्यान दें कि मैंने जोड़ा है alert("Here"):

var isPaused = false;

function firstFunction(){
    isPaused = true;
    for(i=0;i<x;i++){
        // do something
    }
    isPaused = false;
};

function secondFunction(){
    firstFunction()

    alert("Here");

    function waitForIt(){
        if (isPaused) {
            setTimeout(function(){waitForIt()},100);
        } else {
            // go do that thing
        };
    }
};

आपको इंतजार नहीं करना पड़ेगा isPaused। जब आप "यहां" अलर्ट देखते हैं, isPausedतो falseपहले से ही firstFunctionहोगा , और वापस आ जाएगा। ऐसा इसलिए है क्योंकि आप forलूप ( // do something) के अंदर से "उपज" नहीं ले सकते हैं , लूप बाधित नहीं हो सकता है और पहले पूरी तरह से पूरा करना होगा (अधिक विवरण: जावास्क्रिप्ट थ्रेड-हैंडलिंग और रेस-कंडीशंस )।

उस ने कहा, आप अभी भी अंदर कोड firstFunctionको अतुल्यकालिक बना सकते हैं और कॉलर को सूचित करने के लिए कॉलबैक या वादे का उपयोग कर सकते हैं । आपको forलूप को छोड़ना होगा और ifइसके बजाय अनुकरण करना होगा ( JSFiddle ):

function firstFunction()
{
    var deferred = $.Deferred();

    var i = 0;
    var nextStep = function() {
        if (i<10) {
            // Do something
            printOutput("Step: " + i);
            i++;
            setTimeout(nextStep, 500); 
        }
        else {
            deferred.resolve(i);
        }
    }
    nextStep();
    return deferred.promise();
}

function secondFunction()
{
    var promise = firstFunction();
    promise.then(function(result) { 
        printOutput("Result: " + result);
    });
}

साइड नोट पर, जावास्क्रिप्ट 1.7 ने जनरेटर केyield एक भाग के रूप में कीवर्ड पेश किया है । यह अन्यथा तुल्यकालिक जावास्क्रिप्ट कोड प्रवाह ( अधिक विवरण और एक उदाहरण ) में अतुल्यकालिक छेद को "पंच" करने की अनुमति देगा । हालांकि, जनरेटर के लिए ब्राउज़र समर्थन वर्तमान में फ़ायरफ़ॉक्स और क्रोम, AFAIK तक सीमित है।


3
तुमने मुझे बचाया। $ .Deferred () मैं क्या कर रहा हूँ के लिए बंदूक चला रहा है। धन्यवाद
Temitayo

@noseratio मैंने यह कोशिश की। लेकिन WaitForIt विधि बिल्कुल नहीं कहा जाता है। मैं क्या गलत कर रहा हूं?
विजिगम

@vigamage, क्या आपने जो कुछ भी आज़माया है, वह आपको jsfiddle या कोडपेन का लिंक प्रदान कर सकता है?
noseratio

1
नमस्कार, आपकी चिंता के लिए धन्यवाद। मुझे यह काम आ गया। धन्यवाद..!
vigamage

18

एक फ़ंक्शन को पूरा करने के लिए पहले इंतजार करने का एक सुरुचिपूर्ण तरीका है, एसिंक्स / वेट फ़ंक्शन के साथ प्रोमिस का उपयोग करना।


  1. सबसे पहले, एक वादा बनाएँ । मेरे द्वारा बनाया गया फ़ंक्शन 2s के बाद पूरा हो जाएगा। मैंने setTimeoutउस स्थिति को प्रदर्शित करने के लिए उपयोग किया जहां निर्देशों को निष्पादित करने में कुछ समय लगेगा।
  2. दूसरे फ़ंक्शन के लिए, आप async / प्रतीक्षा फ़ंक्शन का उपयोग कर सकते हैं जहां आप awaitनिर्देशों के साथ आगे बढ़ने से पहले पूरा करने के लिए कार्य करेंगे ।

उदाहरण:

    //1. Create a new function that returns a promise
    function firstFunction() {
      return new Promise((resolve, reject) => {
          let y = 0
          setTimeout(() => {
            for(i=0; i<10; i++){
               y++
            }
             console.log('loop completed')  
             resolve(y)
          }, 2000)
      })
    }
    
    //2. Create an async function
    async function secondFunction() {
        console.log('before promise call')
        //3. Await for the first function to complete
        let result = await firstFunction()
        console.log('promise resolved: ' + result)
        console.log('next step')
    }; 

    secondFunction()


ध्यान दें:

आप बस किसी भी मूल्य resolveके Promiseबिना ऐसा कर सकते हैं resolve()। मेरे उदाहरण में, मैं resolvedउस Promiseमूल्य के साथ yहूं जिसे मैं दूसरे फ़ंक्शन में उपयोग कर सकता हूं।


हाँ यह हमेशा काम करेगा। अगर कोई भी इस तरह के परिदृश्य में शामिल है तो जावास्क्रिप्ट वादा उनके लिए चाल चलेगा।
पुट्टमरीगौड़ा MS

5

वादों के साथ एकमात्र मुद्दा यह है कि IE उन्हें समर्थन नहीं करता है। एज करता है, लेकिन वहां IE 10 और 11 में से बहुत कुछ है: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise (तल पर संगतता)

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

कार्य की परमाणु इकाई के रूप में तुल्यकालिक कार्य के बारे में सोचो । मुख्य जावास्क्रिप्ट थ्रेड इसे पूरी तरह से निष्पादित करेगा, जिस क्रम में बयान कोड में दिखाई देंगे।

लेकिन, निम्न स्थिति में, अतुल्यकालिक कॉल में फेंक दें:

showLoadingDiv(); // function 1

makeAjaxCall(); // function 2 - contains async ajax call

hideLoadingDiv(); // function 3

यह वह नहीं करता है जो आप चाहते हैं । यह तुरंत फ़ंक्शन 1, फ़ंक्शन 2 और फ़ंक्शन 3 को निष्पादित करता है। डिव लोड हो रहा है और यह चला गया है, जबकि अजाक्स कॉल लगभग पूरा नहीं है, भले ही makeAjaxCall()वापस आ गया हो। शिकायत यह है कि makeAjaxCall()इसके काम को विखंडू में तोड़ दिया है जो कि मुख्य जावास्क्रिप्ट धागे के प्रत्येक स्पिन द्वारा थोड़ा-थोड़ा उन्नत है - यह उचित रूप से व्यवहार कर रहा है। लेकिन वही मुख्य धागा, एक स्पिन / रन के दौरान, तुल्यकालिक भागों को जल्दी और अनुमानित रूप से निष्पादित करता है।

इसलिए, जिस तरह से मैंने इसे संभाला : जैसे मैंने कहा कि कार्य की परमाणु इकाई है। मैंने फ़ंक्शन 1 और 2 के कोड को मिलाया - मैंने asynch कॉल से पहले फ़ंक्शन 2 में फ़ंक्शन 1 का कोड डाल दिया। मुझे फंक्शन से छुटकारा मिल गया। 1. अतुल्यकालिक कॉल सहित सब कुछ क्रम में, अनुमानित रूप से निष्पादित होता है।

जब, अतुल्यकालिक कॉल पूरा हो जाता है, मुख्य जावास्क्रिप्ट धागे के कई spins के बाद, यह समारोह 3 कहते हैं। यह आदेश की गारंटी देता है । उदाहरण के लिए, अजाक्स के साथ, onreadystatechange घटना हैंडलर को कई बार कहा जाता है। जब यह रिपोर्ट पूरी हो जाती है, तो अंतिम फ़ंक्शन को कॉल करें जिसे आप चाहते हैं।

मैं मानता हूं कि यह गड़बड़ है। मुझे कोड सममित होना पसंद है, मुझे पसंद है कि फ़ंक्शंस एक काम (या उसके करीब) करते हैं, और मुझे पसंद नहीं है कि किसी भी तरह से अजाक्स कॉल प्रदर्शन के लिए ज़िम्मेदार हो (कॉलर पर निर्भरता पैदा करना)। लेकिन, एसिंक्रोनस कॉल के साथ एक सिंक्रोनस फ़ंक्शन में एम्बेडेड, निष्पादन के आदेश की गारंटी के लिए समझौता करना पड़ता है। और मुझे IE 10 के लिए कोड देना है इसलिए कोई वादा नहीं किया है।

सारांश : तुल्यकालिक कॉल के लिए, गारंटी आदेश तुच्छ है। प्रत्येक फ़ंक्शन उस आदेश में पूरी तरह से निष्पादित होता है जिसे यह कहा जाता था। एक एसिंक्रोनस कॉल के साथ एक फ़ंक्शन के लिए, ऑर्डर की गारंटी देने का एकमात्र तरीका यह है कि जब एसिंक्स कॉल पूरा हो जाए, तो मॉनिटर करें और तीसरे फ़ंक्शन को कॉल करें जब उस स्थिति का पता चलता है।

जावास्क्रिप्ट धागे की चर्चा के लिए, देखें: https://medium.com/@francesco_rizzi/javascript-main-thread-dissected-43c85fce7e23 और https : //developer.mozilla.org-en-US/docs/Web/JavaScript/JavaScript EventLoop

इसके अलावा, इस विषय पर एक और समान, अत्यधिक मूल्यांकन किया गया प्रश्न: मुझे एक के बाद एक निष्पादित करने के लिए 3 कार्यों को कैसे कॉल करना चाहिए?


8
डाउनवॉटर (ओं) के लिए, मुझे यह जानने के लिए उत्सुक होना चाहिए कि इस उत्तर में क्या गलत है।
Kermit

0

यह वही है जिसके साथ मैं आया था, क्योंकि मुझे एक श्रृंखला में कई ऑपरेशन चलाने की आवश्यकता है।

<button onclick="tprom('Hello Niclas')">test promise</button>

<script>
    function tprom(mess) {
        console.clear();

        var promise = new Promise(function (resolve, reject) {
            setTimeout(function () {
                resolve(mess);
            }, 2000);
        });

        var promise2 = new Promise(async function (resolve, reject) {
            await promise;
            setTimeout(function () {
                resolve(mess + ' ' + mess);
            }, 2000);
        });

        var promise3 = new Promise(async function (resolve, reject) {
            await promise2;
            setTimeout(function () {
                resolve(mess + ' ' + mess+ ' ' + mess);
            }, 2000);
        });

        promise.then(function (data) {
            console.log(data);
        });

        promise2.then(function (data) {
            console.log(data);
        });

        promise3.then(function (data) {
            console.log(data);
        });
    }

</script>

कृपया दिए गए स्निपेट के विवरण के लिए कम से कम जूस को एक उच्च स्तरीय टिप्पणी जोड़ें। यह समझाने के लिए है कि आपका कोड समस्या का समाधान कैसे करता है।
कोल्ड सेर्बस

समस्या पहले वाले के बाद एक दूसरे फ़ंक्शन को चलाने के लिए थी। मैं दिखाता हूं कि पहले एक के बाद दूसरा फ़ंक्शन कैसे चलाया जाता है, और दूसरे के बाद तीसरा फ़ंक्शन कैसे चलाया जाता है। मैंने तीन कार्य किए, जिससे यह समझना आसान हो सके कि किस कोड की आवश्यकता है।
निकलैस अर्नबर्गर

0

आपका मुख्य मज़ा पहले फ़न को कॉल करेगा, फिर उसके पूरा होने पर आपका अगला फ़न कॉल होगा।

async firstFunction() {
            const promise = new Promise((resolve, reject) => {
                for (let i = 0; i < 5; i++) {
                    // do something
                    console.log(i);
                    if (i == 4) {
                        resolve(i);
                    }
                }
            });
            const result = await promise;
        }

        second() {
            this.firstFunction().then( res => {
                // third function call do something
                console.log('Gajender here');
            });
        }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.