मेरा मानना है कि निरंतरता कॉलबैक का एक विशेष मामला है। एक फ़ंक्शन किसी भी संख्या में फ़ंक्शन, किसी भी समय कॉलबैक कर सकता है। उदाहरण के लिए:
var array = [1, 2, 3];
forEach(array, function (element, array, index) {
array[index] = 2 * element;
});
console.log(array);
function forEach(array, callback) {
var length = array.length;
for (var i = 0; i < length; i++)
callback(array[i], array, i);
}
हालाँकि यदि कोई फ़ंक्शन किसी अन्य फ़ंक्शन को अंतिम चीज़ के रूप में वापस करता है तो वह दूसरा फ़ंक्शन पहले की निरंतरता कहलाता है। उदाहरण के लिए:
var array = [1, 2, 3];
forEach(array, function (element, array, index) {
array[index] = 2 * element;
});
console.log(array);
function forEach(array, callback) {
var length = array.length;
// This is the last thing forEach does
// cont is a continuation of forEach
cont(0);
function cont(index) {
if (index < length) {
callback(array[index], array, index);
// This is the last thing cont does
// cont is a continuation of itself
cont(++index);
}
}
}
यदि कोई फ़ंक्शन किसी अन्य फ़ंक्शन को अंतिम चीज़ के रूप में कहता है, तो उसे टेल कॉल कहा जाता है। कुछ भाषाएं जैसे स्कीम टेल कॉल ऑप्टिमाइज़ेशन करती हैं। इसका मतलब यह है कि टेल कॉल एक फ़ंक्शन कॉल के पूर्ण ओवरहेड को उकसाता नहीं है। इसके बजाय इसे साधारण गोटो के रूप में लागू किया जाता है (कॉल फ़ंक्शन के स्टैक फ्रेम के साथ टेल कॉल के स्टैक फ्रेम द्वारा प्रतिस्थापित किया जाता है)।
बोनस : निरंतरता की शैली में आगे बढ़ना। निम्नलिखित कार्यक्रम पर विचार करें:
console.log(pythagoras(3, 4));
function pythagoras(x, y) {
return x * x + y * y;
}
अब अगर हर ऑपरेशन (इसके अलावा, गुणा, आदि) को कार्यों के रूप में लिखा गया था, तो हमारे पास होगा:
console.log(pythagoras(3, 4));
function pythagoras(x, y) {
return add(square(x), square(y));
}
function square(x) {
return multiply(x, x);
}
function multiply(x, y) {
return x * y;
}
function add(x, y) {
return x + y;
}
इसके अलावा अगर हमें किसी भी मान को वापस करने की अनुमति नहीं थी, तो हमें निरंतरता का उपयोग करना होगा:
pythagoras(3, 4, console.log);
function pythagoras(x, y, cont) {
square(x, function (x_squared) {
square(y, function (y_squared) {
add(x_squared, y_squared, cont);
});
});
}
function square(x, cont) {
multiply(x, x, cont);
}
function multiply(x, y, cont) {
cont(x * y);
}
function add(x, y, cont) {
cont(x + y);
}
प्रोग्रामिंग की यह शैली जिसमें आपको मूल्यों को वापस करने की अनुमति नहीं है (और इसलिए आपको पास के निरंतरता का सहारा लेना चाहिए) को निरंतरता शैली कहा जाता है।
हालांकि निरंतरता शैली के साथ दो समस्याएं हैं:
- निरंतरता के आसपास गुजरने से कॉल स्टैक का आकार बढ़ जाता है। जब तक आप किसी भाषा जैसी योजना का उपयोग नहीं कर रहे हैं जो टेल कॉल को समाप्त कर देती है तो आपको स्टैक स्पेस से बाहर निकलने का जोखिम होगा।
- यह नेस्टेड फ़ंक्शन लिखने के लिए एक दर्द है।
निरंतरता को अतुल्यकालिक रूप से कॉल करके जावास्क्रिप्ट में पहली समस्या को आसानी से हल किया जा सकता है। कॉल को जारी रखने से पहले एसिंक्रोनस रूप से फ़ंक्शन को अतुल्यकालिक रूप से कॉल करके। इसलिए कॉल स्टैक का आकार नहीं बढ़ता है:
Function.prototype.async = async;
pythagoras.async(3, 4, console.log);
function pythagoras(x, y, cont) {
square.async(x, function (x_squared) {
square.async(y, function (y_squared) {
add.async(x_squared, y_squared, cont);
});
});
}
function square(x, cont) {
multiply.async(x, x, cont);
}
function multiply(x, y, cont) {
cont.async(x * y);
}
function add(x, y, cont) {
cont.async(x + y);
}
function async() {
setTimeout.bind(null, this, 0).apply(null, arguments);
}
दूसरी समस्या आमतौर पर एक फ़ंक्शन का उपयोग करके हल की call-with-current-continuation
जाती है जिसे अक्सर संक्षिप्त रूप में देखा जाता है callcc
। दुर्भाग्य से callcc
जावास्क्रिप्ट में पूरी तरह से लागू नहीं किया जा सकता है, लेकिन हम इसके अधिकांश उपयोग मामलों के लिए एक प्रतिस्थापन कार्य लिख सकते हैं:
pythagoras(3, 4, console.log);
function pythagoras(x, y, cont) {
var x_squared = callcc(square.bind(null, x));
var y_squared = callcc(square.bind(null, y));
add(x_squared, y_squared, cont);
}
function square(x, cont) {
multiply(x, x, cont);
}
function multiply(x, y, cont) {
cont(x * y);
}
function add(x, y, cont) {
cont(x + y);
}
function callcc(f) {
var cc = function (x) {
cc = x;
};
f(cc);
return cc;
}
callcc
समारोह एक समारोह लेता है f
और करने के लिए इसे लागू होता है current-continuation
(के रूप में संक्षिप्त cc
)। current-continuation
एक निरंतरता समारोह जो करने के लिए कॉल के बाद समारोह शरीर के बाकी ऊपर लपेटता है callcc
।
फ़ंक्शन के मुख्य भाग पर विचार करें pythagoras
:
var x_squared = callcc(square.bind(null, x));
var y_squared = callcc(square.bind(null, y));
add(x_squared, y_squared, cont);
current-continuation
दूसरे की callcc
है:
function cc(y_squared) {
add(x_squared, y_squared, cont);
}
इसी तरह current-continuation
पहले का callcc
है:
function cc(x_squared) {
var y_squared = callcc(square.bind(null, y));
add(x_squared, y_squared, cont);
}
चूंकि current-continuation
पहले callcc
में एक और सम्मिलित है, callcc
इसे निरंतरता गुजर शैली में परिवर्तित किया जाना चाहिए:
function cc(x_squared) {
square(y, function cc(y_squared) {
add(x_squared, y_squared, cont);
});
}
तो अनिवार्य callcc
रूप से तार्किक रूप से पूरे फ़ंक्शन बॉडी को वापस वही करता है जो हमने शुरू किया था (और उन अनाम कार्यों को नाम देता है cc
)। पाइकगोरस फंक्शन का उपयोग करते हुए कॉलक का यह कार्यान्वयन तब बन जाता है:
function pythagoras(x, y, cont) {
callcc(function(cc) {
square(x, function (x_squared) {
square(y, function (y_squared) {
add(x_squared, y_squared, cont);
});
});
});
}
फिर से आप callcc
जावास्क्रिप्ट में लागू नहीं कर सकते हैं , लेकिन आप इसे जावास्क्रिप्ट में जारी रखने की शैली को निम्नानुसार लागू कर सकते हैं:
Function.prototype.async = async;
pythagoras.async(3, 4, console.log);
function pythagoras(x, y, cont) {
callcc.async(square.bind(null, x), function cc(x_squared) {
callcc.async(square.bind(null, y), function cc(y_squared) {
add.async(x_squared, y_squared, cont);
});
});
}
function square(x, cont) {
multiply.async(x, x, cont);
}
function multiply(x, y, cont) {
cont.async(x * y);
}
function add(x, y, cont) {
cont.async(x + y);
}
function async() {
setTimeout.bind(null, this, 0).apply(null, arguments);
}
function callcc(f, cc) {
f.async(cc);
}
फ़ंक्शन callcc
का उपयोग जटिल नियंत्रण प्रवाह संरचनाओं को लागू करने के लिए किया जा सकता है जैसे कि ट्राई-कैच ब्लॉक, कोरटाइन, जनरेटर, फाइबर आदि।