नोडबैक का उपयोग करके कॉलबैक कहा जाने तक फ़ंक्शन को प्रतीक्षा कैसे करें


266

मेरा एक सरलीकृत कार्य है जो इस तरह दिखता है:

function(query) {
  myApi.exec('SomeCommand', function(response) {
    return response;
  });
}

मूल रूप से मैं इसे कॉल करना चाहता हूं myApi.exec, और कॉलबैक लैंबडा में दी गई प्रतिक्रिया को वापस कर सकता हूं । हालाँकि, उपरोक्त कोड काम नहीं करता है और बस तुरंत वापस आ जाता है।

बस एक बहुत ही हैकिश प्रयास के लिए, मैंने नीचे की कोशिश की जो काम नहीं किया, लेकिन कम से कम आपको यह विचार मिलता है कि मैं क्या हासिल करने की कोशिश कर रहा हूं:

function(query) {
  var r;
  myApi.exec('SomeCommand', function(response) {
    r = response;
  });
  while (!r) {}
  return r;
}

असल में, इस बारे में जाने का एक अच्छा 'नोड.जेएस / इवेंट संचालित' तरीका क्या है? मैं चाहता हूं कि जब तक कॉलबैक नहीं हो जाता, तब तक मेरा फ़ंक्शन प्रतीक्षा करें, फिर उस मान को लौटाएं जो इसे पारित किया गया था।


3
या मैं इसके बारे में पूरी तरह से गलत तरीके से यहां जा रहा हूं, और क्या मुझे प्रतिक्रिया वापस करने के बजाय एक और कॉलबैक कॉल करना चाहिए?
क्रिस

यह मेरी राय में सबसे अच्छा एसओ स्पष्टीकरण है कि व्यस्त लूप क्यों काम नहीं करता है।
bluenote10

इंतजार करने की कोशिश मत करो। बस कॉलबैक के अंत में अगले फ़ंक्शन (कॉलबैक-निर्भर) को कॉल करें
अतुल

जवाबों:


282

यह करने के लिए "अच्छा नोड.जेएस / इवेंट संचालित" तरीका इंतजार नहीं करना है

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

function(query, callback) {
  myApi.exec('SomeCommand', function(response) {
    // other stuff here...
    // bla bla..
    callback(response); // this will "return" your value to the original caller
  });
}

तो आप इसे इस तरह उपयोग न करें:

var returnValue = myFunction(query);

लेकिन इस तरह:

myFunction(query, function(returnValue) {
  // use the return value here instead of like a regular (non-evented) return value
});

5
ठीक है अच्छा है। अगर myApi.exec ने कॉलबैक को कभी नहीं बुलाया तो क्या होगा? मैं इसे कैसे बनाऊंगा कि कॉलबैक 10 सेकंड के बाद एक त्रुटि मान के साथ कहे कि यह हमारी या कुछ और समय की है?
क्रिस

5
या बेहतर अभी तक (एक चेक जोड़ा है तो कॉलबैक कैंट को दो बार आमंत्रित किया जा सकता है): jsfiddle.net/LdaFw/1
Jakob

148
यह स्पष्ट है कि नॉन-ब्लॉकिंग नोड / जेएस में मानक है, हालांकि निश्चित रूप से ऐसे समय होते हैं जब ब्लॉकिंग वांछित होती है (जैसे स्टड पर ब्लॉक करना)। यहां तक ​​कि नोड में "ब्लॉकिंग" विधियां हैं (सभी fs sync*विधियों को देखें )। जैसे, मुझे लगता है कि यह अभी भी एक वैध प्रश्न है। क्या व्यस्त प्रतीक्षा से अलग नोड में अवरोध को प्राप्त करने का एक अच्छा तरीका है?
12'12

7
@Nategood द्वारा टिप्पणी का देर से जवाब: मैं कुछ तरीकों के बारे में सोच सकता हूं; इस टिप्पणी में बहुत समझाने के लिए, लेकिन उन्हें गूगल करें। याद रखें कि नोड को अवरुद्ध करने के लिए नहीं बनाया गया है, इसलिए ये सही नहीं हैं। उन्हें सुझाव समझें। वैसे भी, यहां जाता है: (1) अपने फ़ंक्शन को लागू करने के लिए सी का उपयोग करें और इसका उपयोग करने के लिए इसे एनपीएम पर प्रकाशित करें। यही syncविधियाँ करती हैं। (2) फाइबर का उपयोग करें, github.com/laverdet/node-fibers , (3) उपयोग के वादे, उदाहरण के लिए क्यू-लाइब्रेरी, (4) जावास्क्रिप्ट के ऊपर एक पतली परत का उपयोग करें, जो अवरुद्ध दिखता है, लेकिन async के लिए संकलित करता है, जैसे maxtaco.github.com/cfish-script
Jakob

106
यह बहुत निराशाजनक है जब लोग एक सवाल का जवाब देते हैं "आपको ऐसा नहीं करना चाहिए।" यदि कोई मददगार बनना चाहता है और किसी सवाल का जवाब देना चाहता है, तो यह एक स्टैंड अप बात है। लेकिन मुझे असमान रूप से कहना कि मुझे कुछ नहीं करना चाहिए, बस अमित्र है। एक लाख अलग-अलग कारण हैं कि क्यों कोई नियमित रूप से या असिंक्रोनस रूप से एक रूटीन कॉल करना चाहेगा। यह एक प्रश्न था कि इसे कैसे किया जाए। यदि आप उत्तर प्रदान करते समय आपी की प्रकृति के बारे में उपयोगी सलाह देते हैं, तो यह उपयोगी है, लेकिन यदि आप उत्तर नहीं देते हैं, तो परेशान क्यों हो रहे हैं। (मुझे लगता है कि मुझे वास्तव में अपनी सलाह खुद देनी चाहिए।)
हॉवर्ड स्वोप

45

इसे प्राप्त करने का एक तरीका एपीआई कॉल को एक वादा में लपेटना है और फिर awaitपरिणाम के लिए इंतजार करना है।

// let's say this is the API function with two callbacks,
// one for success and the other for error
function apiFunction(query, successCallback, errorCallback) {
    if (query == "bad query") {
        errorCallback("problem with the query");
    }
    successCallback("Your query was <" + query + ">");
}

// myFunction wraps the above API call into a Promise
// and handles the callbacks with resolve and reject
function apiFunctionWrapper(query) {
    return new Promise((resolve, reject) => {
        apiFunction(query,(successResponse) => {
            resolve(successResponse);
        }, (errorResponse) => {
            reject(errorResponse)
        });
    });
}

// now you can use await to get the result from the wrapped api function
// and you can use standard try-catch to handle the errors
async function businessLogic() {
    try {
        const result = await apiFunctionWrapper("query all users");
        console.log(result);

        // the next line will fail
        const result2 = await apiFunctionWrapper("bad query");
    } catch(error) {
        console.error("ERROR:" + error);
    }
}

// call the main function
businessLogic();

आउटपुट:

Your query was <query all users>
ERROR:problem with the query

यह एक कॉलबैक के साथ एक फ़ंक्शन को रैप करने का एक बहुत अच्छा किया गया उदाहरण है, इसलिए आप इसे async/await I नॉट के साथ अक्सर उपयोग कर सकते हैं , इसलिए यह याद रखने में परेशानी होती है कि इस स्थिति को कैसे संभालना है, मैं इसे अपने व्यक्तिगत नोट्स / संदर्भों के लिए कॉपी कर रहा हूं।
रॉबर्ट


10

यदि आप कॉल बैक का उपयोग नहीं करना चाहते हैं तो आप "क्यू" मॉड्यूल का उपयोग कर सकते हैं।

उदाहरण के लिए:

function getdb() {
    var deferred = Q.defer();
    MongoClient.connect(databaseUrl, function(err, db) {
        if (err) {
            console.log("Problem connecting database");
            deferred.reject(new Error(err));
        } else {
            var collection = db.collection("url");
            deferred.resolve(collection);
        }
    });
    return deferred.promise;
}


getdb().then(function(collection) {
   // This function will be called afte getdb() will be executed. 

}).fail(function(err){
    // If Error accrued. 

});

अधिक जानकारी के लिए इसे देखें: https://github.com/kriskowal/q


9

यदि आप इसे बहुत सरल और आसान चाहते हैं, तो कोई फैंसी लाइब्रेरी नहीं, किसी अन्य कोड को निष्पादित करने से पहले, नोड में निष्पादित किए जाने वाले कॉलबैक कार्यों की प्रतीक्षा करें, इस तरह है:

//initialize a global var to control the callback state
var callbackCount = 0;
//call the function that has a callback
someObj.executeCallback(function () {
    callbackCount++;
    runOtherCode();
});
someObj2.executeCallback(function () {
    callbackCount++;
    runOtherCode();
});

//call function that has to wait
continueExec();

function continueExec() {
    //here is the trick, wait until var callbackCount is set number of callback functions
    if (callbackCount < 2) {
        setTimeout(continueExec, 1000);
        return;
    }
    //Finally, do what you need
    doSomeThing();
}

5

नोट: इस उत्तर का उपयोग संभवतः उत्पादन कोड में नहीं किया जाना चाहिए। यह एक हैक है और आपको निहितार्थ के बारे में पता होना चाहिए।

वहाँ uvrun मॉड्यूल ( यहाँ नए Nodejs संस्करणों के लिए अद्यतन किया गया है ) जहाँ आप libuv मुख्य ईवेंट लूप (जो Nodejs मुख्य लूप है) के एक लूप राउंड को निष्पादित कर सकते हैं।

आपका कोड इस तरह दिखेगा:

function(query) {
  var r;
  myApi.exec('SomeCommand', function(response) {
    r = response;
  });
  var uvrun = require("uvrun");
  while (!r)
    uvrun.runOnce();
  return r;
}

(आप वैकल्पिक उपयोग uvrun.runNoWait()कर सकते हैं । यह अवरुद्ध होने के साथ कुछ समस्याओं से बच सकता है, लेकिन 100% सीपीयू लेता है।)

ध्यान दें कि यह दृष्टिकोण Nodejs के पूरे उद्देश्य को अमान्य कर देता है, अर्थात सब कुछ async और गैर-अवरुद्ध है। इसके अलावा, यह आपके कॉलस्टैक की गहराई को बहुत बढ़ा सकता है, इसलिए आप स्टैक ओवरफ्लो के साथ समाप्त हो सकते हैं। यदि आप ऐसे फ़ंक्शन को पुनरावर्ती रूप से चलाते हैं, तो आप निश्चित रूप से मुसीबतों में भाग लेंगे।

अपने कोड को "सही" करने के लिए कैसे पुन: डिज़ाइन करें, इसके बारे में अन्य उत्तर देखें।

यहां यह समाधान केवल तभी उपयोगी है जब आप परीक्षण और जासूसी करते हैं। सिंक और सीरियल कोड चाहते हैं।


5

नोड 4.8.0 के बाद से आप ES6 नामक जनरेटर की सुविधा का उपयोग करने में सक्षम हैं। आप गहरी अवधारणाओं के लिए इस लेख का अनुसरण कर सकते हैं। लेकिन मूल रूप से आप इस काम को करने के लिए जनरेटर और वादों का उपयोग कर सकते हैं। मैं जनरेटर को बढ़ावा देने और प्रबंधित करने के लिए ब्लूबर्ड का उपयोग कर रहा हूं ।

आपका कोड नीचे दिए गए उदाहरण की तरह ठीक होना चाहिए।

const Promise = require('bluebird');

function* getResponse(query) {
  const r = yield new Promise(resolve => myApi.exec('SomeCommand', resolve);
  return r;
}

Promise.coroutine(getResponse)()
  .then(response => console.log(response));

1

आप एक समारोह है:

var fetchPage(page, callback) {
   ....
   request(uri, function (error, response, body) {
        ....
        if (something_good) {
          callback(true, page+1);
        } else {
          callback(false);
        }
        .....
   });


};

आप इस तरह से कॉलबैक का उपयोग कर सकते हैं:

fetchPage(1, x = function(next, page) {
if (next) {
    console.log("^^^ CALLBACK -->  fetchPage: " + page);
    fetchPage(page, x);
}
});

-1

गैर-अवरुद्ध IO के उद्देश्य को पराजित करता है - आप इसे अवरुद्ध कर रहे हैं जब इसे अवरुद्ध करने की आवश्यकता नहीं होती है :)

आपको अपने कॉलबैक को नोड के लिए मजबूर करने के बजाय घोंसला बनाना चाहिए। प्रतीक्षा करने के लिए, या कॉलबैक के अंदर एक और कॉलबैक पर कॉल करें जहां आपको परिणाम की आवश्यकता है r

संभावना है, यदि आपको अवरुद्ध करने की आवश्यकता है, तो आप अपनी वास्तुकला के बारे में गलत सोच रहे हैं।


मुझे संदेह था कि मेरे पास यह पीछे की ओर है।
क्रिस

31
संभावना है, मैं बस http.get()कुछ URL और console.log()इसकी सामग्री के लिए एक त्वरित स्क्रिप्ट लिखना चाहता हूं । मुझे नोड में ऐसा करने के लिए पीछे की ओर क्यों कूदना है?
दान डस्केल्सस्कु

6
@DanDascalescu: और मुझे स्थैतिक भाषाओं में इसे करने के लिए टाइप हस्ताक्षर क्यों घोषित करने होंगे? और मुझे इसे सी-लाइक भाषाओं में मेन-मेथड में क्यों डालना है? और मुझे इसे संकलित भाषा में क्यों संकलित करना है? आप जो सवाल कर रहे हैं, वह Node.js. में एक मौलिक डिजाइन निर्णय है। उस निर्णय के पक्ष और विपक्ष हैं। यदि आपको यह पसंद नहीं है, तो आप एक और भाषा का उपयोग कर सकते हैं जो आपकी शैली को बेहतर बनाती है। इसलिए हमारे पास एक से अधिक हैं।
जकॉब

@ जाकोब: आपके द्वारा सूचीबद्ध समाधान वास्तव में उप-रूपी हैं। इसका मतलब यह नहीं है कि वहाँ अच्छे लोग नहीं हैं, जैसे उल्का का सर्वर-साइड फाइबर में नोड का उपयोग, जो कॉलबैक नरक समस्या को समाप्त करता है।
डैन डेस्केल्सस्क्यू

13
@ जाकोब: अगर "क्यों पारिस्थितिकी तंत्र एक्स सामान्य कार्य वाई को अनावश्यक रूप से कठिन बना देता है?" "यदि आप इसे पसंद नहीं करते हैं, तो पारिस्थितिकी तंत्र एक्स का उपयोग न करें," तो यह एक मजबूत संकेत है कि पारिस्थितिकी तंत्र एक्स के डिजाइनर और अनुरक्षक अपने पारिस्थितिकी तंत्र की वास्तविक प्रयोज्यता से ऊपर अपने स्वयं के अहंकार को प्राथमिकता दे रहे हैं। यह मेरा अनुभव रहा है कि नोड समुदाय (रूबी, अमृत और यहां तक ​​कि पीएचपी समुदायों के विपरीत) आम कार्यों को कठिन बनाने के लिए अपने रास्ते से हट जाता है। अपने आप को उस एंटीपैटर के जीवित उदाहरण के रूप में पेश करने के लिए SO MUCH को धन्यवाद।
जैज

-1

एसिंक्स का उपयोग करना और इंतजार करना अधिक आसान है।

router.post('/login',async (req, res, next) => {
i = await queries.checkUser(req.body);
console.log('i: '+JSON.stringify(i));
});

//User Available Check
async function checkUser(request) {
try {
    let response = await sql.query('select * from login where email = ?', 
    [request.email]);
    return response[0];

    } catch (err) {
    console.log(err);

  }

}

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