क्या कॉलबैक और वादों के बीच वास्तव में एक बुनियादी अंतर है?


94

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

कुछ विशिष्ट jQueryकोड इस तरह से डिज़ाइन किए गए हैं:

$.get('userDetails', {'name': 'joe'}, function(data) {
    $('#userAge').text(data.age);
});

हालाँकि, इस प्रकार का कोड गड़बड़ हो सकता है और अत्यधिक नेस्टेड हो सकता है जब हम पिछले एक के खत्म होने के बाद एक के बाद एक अतिरिक्त async कॉल करना चाहते हैं।

इसलिए एक दूसरा तरीका प्रॉमिस का उपयोग कर रहा है। एक वादा एक वस्तु है जो एक मूल्य का प्रतिनिधित्व करता है जो अभी तक मौजूद नहीं हो सकता है। आप उस पर कॉलबैक सेट कर सकते हैं, जिसे तब पढ़ा जाएगा जब मूल्य पढ़ने के लिए तैयार हो।

प्रोमिस और पारंपरिक कॉलबैक दृष्टिकोण के बीच का अंतर यह है कि एसिंक्रियस तरीके अब सिंक्रोनाइज़ की गई वस्तुओं को वापस करते हैं, जिसे क्लाइंट कॉलबैक सेट करता है। उदाहरण के लिए, AngularJS में वादों का उपयोग करते हुए समान कोड:

$http.get('userDetails', {'name': 'joe'})
    .then(function(response) {
        $('#userAge').text(response.age);
    });

तो मेरा सवाल यह है कि क्या वास्तव में वास्तविक अंतर है? अंतर विशुद्ध रूप से वाक्यात्मक लगता है।

क्या एक तकनीक का दूसरे पर उपयोग करने का कोई गहरा कारण है?


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


5
@gnat: दो प्रश्नों / उत्तरों की सापेक्ष गुणवत्ता को देखते हुए, डुप्लिकेट वोट IMHO के आसपास दूसरा रास्ता होना चाहिए।
बार्ट वैन इनगेन शेनौ

जवाबों:


110

यह कहना उचित है कि वादे सिर्फ चीनी हैं। सब कुछ आप वादे के साथ कर सकते हैं आप कॉलबैक के साथ कर सकते हैं। वास्तव में, अधिकांश वादे क्रियान्वयन जब चाहें तब दोनों के बीच रूपांतरण के तरीके प्रदान करते हैं।

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

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

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


यहाँ कुछ काल्पनिक कोड में वादों के साथ और फिर कॉलबैक के साथ एक प्रयास है जो आपको केवल कुछ विचार देने के लिए पर्याप्त जटिल होना चाहिए।

वादे के साथ:

createViewFilePage(fileDescriptor) {
    getCurrentUser().then(function(user) {
        return isUserAuthorizedFor(user.id, VIEW_RESOURCE, fileDescriptor.id);
    }).then(function(isAuthorized) {
        if(!isAuthorized) {
            throw new Error('User not authorized to view this resource.'); // gets handled by the catch() at the end
        }
        return Promise.all([
            loadUserFile(fileDescriptor.id),
            getFileDownloadCount(fileDescriptor.id),
            getCommentsOnFile(fileDescriptor.id),
        ]);
    }).then(function(fileData) {
        var fileContents = fileData[0];
        var fileDownloads = fileData[1];
        var fileComments = fileData[2];
        fileTextAreaWidget.text = fileContents.toString();
        commentsTextAreaWidget.text = fileComments.map(function(c) { return c.toString(); }).join('\n');
        downloadCounter.value = fileDownloads;
        if(fileDownloads > 100 || fileComments.length > 10) {
            hotnessIndicator.visible = true;
        }
    }).catch(showAndLogErrorMessage);
}

कॉलबैक के साथ:

createViewFilePage(fileDescriptor) {
    setupWidgets(fileContents, fileDownloads, fileComments) {
        fileTextAreaWidget.text = fileContents.toString();
        commentsTextAreaWidget.text = fileComments.map(function(c) { return c.toString(); }).join('\n');
        downloadCounter.value = fileDownloads;
        if(fileDownloads > 100 || fileComments.length > 10) {
            hotnessIndicator.visible = true;
        }
    }

    getCurrentUser(function(error, user) {
        if(error) { showAndLogErrorMessage(error); return; }
        isUserAuthorizedFor(user.id, VIEW_RESOURCE, fileDescriptor.id, function(error, isAuthorized) {
            if(error) { showAndLogErrorMessage(error); return; }
            if(!isAuthorized) {
                throw new Error('User not authorized to view this resource.'); // gets silently ignored, maybe?
            }

            var fileContents, fileDownloads, fileComments;
            loadUserFile(fileDescriptor.id, function(error, result) {
                if(error) { showAndLogErrorMessage(error); return; }
                fileContents = result;
                if(!!fileContents && !!fileDownloads && !!fileComments) {
                    setupWidgets(fileContents, fileDownloads, fileComments);
                }
            });
            getFileDownloadCount(fileDescriptor.id, function(error, result) {
                if(error) { showAndLogErrorMessage(error); return; }
                fileDownloads = result;
                if(!!fileContents && !!fileDownloads && !!fileComments) {
                    setupWidgets(fileContents, fileDownloads, fileComments);
                }
            });
            getCommentsOnFile(fileDescriptor.id, function(error, result) {
                if(error) { showAndLogErrorMessage(error); return; }
                fileComments = result;
                if(!!fileContents && !!fileDownloads && !!fileComments) {
                    setupWidgets(fileContents, fileDownloads, fileComments);
                }
            });
        });
    });
}

वादों के बिना भी कॉलबैक संस्करण में कोड डुप्लीकेशन को कम करने के कुछ चतुर तरीके हो सकते हैं, लेकिन मैं उन सभी को सोच सकता हूं जो कुछ बहुत ही वादों को लागू करने के लिए उबाल कर सकते हैं।


1
वादों का एक अन्य प्रमुख लाभ यह है कि वे async / प्रतीक्षा के साथ "चीनीकरण" के लिए उत्तरदायी हैं या एक coroutine है जो yielded वादों के लिए दिए गए मानों को वापस करता है। यहां लाभ यह है कि आपको मूल नियंत्रण प्रवाह संरचनाओं में मिश्रण करने की क्षमता मिलती है, जो कि वे कितने async संचालन में भिन्न हो सकते हैं। मैं एक संस्करण जोड़ूंगा जो यह दिखाता है।
अक्जय

9
कॉलबैक और वादों के बीच मूलभूत अंतर नियंत्रण का उलटा है। कॉलबैक के साथ, आपके API को कॉलबैक स्वीकार करना होगा , लेकिन वादे के साथ, आपके API को एक वादा प्रदान करना होगा । यह प्राथमिक अंतर है, और एपीआई डिजाइन के लिए इसके व्यापक निहितार्थ हैं।
cwharris

@ChristopherHarris यकीन नहीं है कि मैं सहमत हूँ। then(callback)प्रॉमिस पर एक विधि होना जो कॉलबैक स्वीकार करता है (एपीआई कॉल करने की एक विधि के बजाय इस कॉलबैक को स्वीकार करना) आईओसी के पास कुछ भी करने की आवश्यकता नहीं है। वादा एक स्तर के अप्रत्यक्ष परिचय देता है जो रचना, जंजीर और त्रुटि से निपटने के लिए उपयोगी है (प्रभाव में रेलवे उन्मुख प्रोग्रामिंग), लेकिन कॉलबैक अभी भी क्लाइंट द्वारा निष्पादित नहीं किया गया है, इसलिए आईओसी की अनुपस्थिति वास्तव में नहीं है।
ड्रैगन.स्टेपानोविक

1
@ dragan.stepanovic आप सही हैं, और मैंने गलत शब्दावली का उपयोग किया है। अंतर अप्रत्यक्ष है। कॉलबैक के साथ, आपको पहले से ही पता होना चाहिए कि परिणाम के साथ क्या करने की आवश्यकता है। एक वादा के साथ, आप बाद में फैसला कर सकते हैं।
cwharris
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.