Delegates - Construction/Execution -Time-Parameter - javascript

I have a helper function which allows me to call functions in a different context. It's pretty simple:
function delegate(that, thatMethod)
{
return function() { return thatMethod.apply(that,arguments); }
}
This is ok if I wan't evaluate the variables at execution of the function, but sometimes I want to give the delegate-function values which are fixed at construction time.
Sample:
var callbacks = new Array();
for(var i = 0; i < 5; i++)
{
callbacks.push(delegate(window, function() { alert(i) }));
}
callbacks[3]();
In this case my expected behavior is that I get an alert(3) but because i is evaluated at execution we don't.
I know there is another delegate function which looks something like:
function delegatedd( that, thatMethod )
{
if(arguments.length > 2)
{
var _params = [];
for(var n = 2; n < arguments.length; ++n)
_params.push(arguments[n]);
return function() { return thatMethod.apply(that,_params); }
}
else
return function() { return thatMethod.call(that); }
}
But that doesn't help me either because I want to mix both methods. It can be written like that (first version of delegate used):
function(foo) {
return delegate(window, function() {
alert(foo);
});
}(i)
So i is construction time and everything else execution time.
The disadvatage of this is that it looks pretty ugly. Is there a better way to do it? Can I somehow hide it in a function?
Thanks

You can use the bind function:
var callbacks = new Array();
for(var i = 0; i < 5; i++)
{
//callbacks.push(delegate(window, function() { alert(i) }));
callbacks.push(function(n) { alert(n) }.bind(window, i);
}
callbacks[3]();
But bind is not implemented on IE(don't know about IE9), for how get it to work on IE see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind#Compatibility.

Related

Avoiding a global variable

The purpose of the example code below is to be able to restrict a function from printing something via console.log (a loop in this case) if the function is executed twice.
However, the function uses the global variable "condition" and I want to find a way to avoid this. I've been playing around with workarounds, but to no avail. Also, I have checked sources online, but relate to other more complex examples which are beyond my level. This is a simpler example, but haven't been able to crack this.
Grateful for some guidance. Thanks.
let condition = false;
const testFunc = function (value) {
for (let i = 0; i < 10; i++) {
if (!condition) {
console.log(value + i);
}
}
condition = true;
};
testFunc(5);
testFunc(5);
The usual answer is to create yet another scope to hold the state:
function once(fn) {
let called = false
return function(...args) {
if (!called) {
fn(...args)
called = true
}
}
}
const test = once(function() {
console.log('hello')
})
test() // hello
test() // nothing
Thanks for your feedback - I considered the use of using closure by way of returning a function within another.
Would this be a viable option in order to avoid a global variable? Note that "func" needs to be declared globally -
const testFuncEncaps = function (value) {
let trueOrFalse = false;
return function () {
for (let i = 0; i < 10; i++) {
if (!trueOrFalse) {
console.log(value + i);
}
}
trueOrFalse = true;
};
};
let func = testFuncEncaps(5);
func();
func();

How to test the function call count of another function in Javascript?

Say I have this function:
function doSomething(n) {
for (var i = 0; i < n; i++) {
doSomethingElse();
}
}
How would I test if the doSomethingElse function is called n times??
I tried something like:
test("Testing something", function () {
var spy = sinon.spy(doSomethingElse);
doSomething(12);
equal(spy.callCount, 12, "doSomethingElse is called 12 times");
});
but this does not seem to work, because you have to call the spy while the doSomething() calls the original doSomethingElse(). How can I make this work with QUnit/sinon.js?
EDIT
Maybe it isn't even a good idea? Does this fall outside the 'unit testing' because another function is called?
You could do something like this:
test('example1', function () {
var originalDoSomethingElse = doSomethingElse;
doSomethingElse = sinon.spy(doSomethingElse);
doSomething(12);
strictEqual(doSomethingElse.callCount, 12);
doSomethingElse = originalDoSomethingElse;
});
For example: JSFiddle.
function doSomething(n) {
for (var i = 0; i < n; i++) {
doSomethingElse();
}
}
you cant spy on doSomethingElse.
doSomethingElse is not testable ,when something is not testable it needs to be refactored.
You either need to inject doSomethingElse in doSomething
OR
use a pointer:
pointer={doSomethingElse:function(){}};
function doSomething(n) {
for (var i = 0; i < n; i++) {
pointer.doSomethingElse();
}
}
Declare a global variable named count and assign it 0
window.count = 0;
Now, inside the doSomethingElse() function, increment it like count++
So, whenever you access count variable, it will return the number of times the doSomethingElse() is called.
Full code might be:
window.count = 0;
function doSomething(n) {
for (var i = 0; i < n; i++) {
doSomethingElse();
}
}
function doSomethingElse() {
count++;
// do something here
}
doSomething(22);
alert(count);// alerts 22
Or even better, call count++ whenever the function you want to be tested is called in code.
Demo: http://jsfiddle.net/583ZJ/
Note: If you want to remove it, then just remove the variable declaration (window.count=0;) and count++
function debugCalls(f) {
if (!f.count)
f.count = 0;
f.count++;
}
function doSomethingElse()
{
debugCalls(arguments.callee);
// function code...
}
// usage
for(var i = 0; i < 100; i++) doSomethingElse();
alert(doSomethingElse.count);
this way it makes it easier for you to debug any function you want just by inserting debugCalls(arguments.callee) inside the function you want to save the number of times it has been called.
In Node.js 14.2.0 one can use the new currently experimental CallTracker API to do the job without using Sinon or another additional library.
var assert = require('assert');
test("Testing something", function () {
var originalDoSomethingElse = doSomethingElse;
var tracker = new assert.CallTracker();
doSomethingElse = tracker.calls(doSomethingElse, 12);
try {
doSomething(12);
tracker.verify();
} finally {
doSomethingElse = originalDoSomethingElse;
}
});

Pass variable to closure on event trigger

I have a loop where I define a closure for an event:
for (var i = 0; i < 3; i++) {
obj.onload = function(e) {
me.myFunction(e, i);
};
}
I need to pass that counter variable to the function that is called when the load event is triggered. I always get the same value being passed.
How can I pass the right counter value to the function?
The usual way to solve this is with a builder function, so that the handler closes over something that doesn't change:
function buildHandler(index) {
return function(e) {
me.myFunction(e, index);
};
}
for (var i = 0; i < 3; i++) {
obj.onload = buildHandler(i);
}
You can do that with an immediately-invoked function expression (IIFE), but in theory it creates and throws away a function on every loop (I expect most JavaScript engines these days optimize that) and I, at least, think it's a lot harder to read:
for (var i = 0; i < 3; i++) {
obj.onload = (function(index) {
return function(e) {
me.myFunction(e, index);
};
})(i);
}
There's an ES5 feature, Function#bind, that you can use if you want to use me.myFunction directly, but you'd have to change the order in which you were expecting the arguments (index first, then the event):
for (var i = 0; i < 3; i++) {
obj.onload = me.myFunction.bind(me, i);
}
bind creates a function that, when called, calls the original function with this set to the first argument (so, me above), then any further arguments you give it, and then any arguments that were used when calling the function. (That's why the order myFunction was expecting them had to change to index, e rather than e, i.)
On older browsers, you need a shim for Function#bind.

Is there a better way to do callback chaining in javascript?

I wrote a callback helper, that lets me group multiple callbacks into one function variable:
function chainCallbacks() {
var callbacks = arguments;
return function () {
for(var i = 0; i < callbacks.length; i++) {
if(callbacks[i] != null) {
callbacks[i].apply(null, arguments);
}
}
};
}
this works, but I'm wondering if there are any javascript libraries that provide the same functionality? or even better, something that simulates the .NET "event" pattern?
myEvent+=myCallback;
I have modified your chainCallbacks function. You can test below code in JS console (I'm using Chrome -works fine), and check the result.
var result = 0;
function a() {
result += 5;
console.log(result);
_next();
}
function b() {
result += 10;
console.log(result);
_next();
}
function c() {
result += 20;
console.log(result);
_next();
}
function chainCallbacks() {
var _this = this;
var _counter = 0;
var _callbacks = arguments;
var _next = function() {
_counter++;
if(_counter < _callbacks.length) {
_callbacks[_counter].apply(_this);
}
};
_this._next = _next;
return function() {
if(_callbacks.length > 0) {
_callbacks[0].apply(_this);
}
};
}
var queue = chainCallbacks(a, b, c);
queue();
Idea is simple - you call _next() whenever your callback function has finished executing, and you want to jump to another. So you can call _next() e.g. after some jQuery animation as well, and this way you will preserve the order of the functions.
If you want to replace a callback with one that calls the original as well as some others, I'd probably just do something like this:
Requirejs.config.callback = function(orig) {
var fns = [orig, first, second, third];
return function() {
fns.forEach(function(fn) { fn.apply(null, this); }, arguments);
};
}(Requirejs.config.callback);
But if you're doing this often, I think your solution will be as good as it gets. I don't see need for a library.
Requirejs.config.callback = chainCallbacks(Requirejs.config.callback, first, second, third)
A library can't do anything to extend language syntax in JavaScript. It's limited to what's available... no operator overloading or anything.

Something similar to jQuery when / then: deferred execution with break

I'm searching for a solution where I'm able to run different functions, but some of them need a timeout and all following functions need to wait until the previous one is finished. Every function should be able to break the complete process.
Now I thought pushing all functions to a stack and loop through them:
function foo() {
// bar() should wait as long the following is finished:
setTimeout(function(){
if ((new Date()).getSeconds() % 2) {
alert('foo');
// break loop through functions (bar is not called)
}
else {
// start next function (bar is called)
}
}, 1000);
}
function bar() {
setTimeout(function(){
alert('bar')
}, 1000);
}
var functions = new Array('foo', 'bar');
for (var i = 0, length = functions.length; i < length; i++) {
window[functions[i]]();
}
But how to include wait/break?!
Note: This should work with 2+ functions (amount of functions is changeable)
Note2: I don't want to use jQuery.
Note: I have updated my answer, see bottom of post.
Alright, let's take a look.
You're using the window[func]() method, so you should be able to store and use return values from each function.
Proof:
function a(){
return "value";
}
var ret_val = window['a']();
alert(ret_val);
Let's create a return rule:
If function returns true, continue execution flow.
If function returns false, break execution flow.
function a(){
//Do stuff
return (condition);
}
for(i = 0; i < n; i++){
var bReturn = window['function']();
if(!bReturn) break;
}
Now let's put it into practice.
function a(){
//Do stuff
return ((new Date()).getSeconds() % 2); //Continue?
}
function b(){
//Do stuff
return true; //Continue?
}
function c(){
//Do stuff
return false; //Continue?
}
function d(){
//Do stuff
return true; //Continue?
}
var functions = new Array('a', 'b', 'c', 'd');
for (var i = 0; i < functions.length; i++ ) {
var bReturn = window[functions[i]]();
if(!bReturn) break;
}
Depending on when you execute the script, eg, an even or uneven time period, it will only execute function a or execute functions a b & c. In between each function, you can go about your normal business.
Of course, the conditions probably vary from each individual function in your case.
Here's a JSFiddle example where you can see it in action.
With some small modification, you can for instance, make it so that if function a returns false, it will skip the following function and continue on to the next, or the one after that.
Changing
for (var i = 0; i < functions.length; i++ ) {
var bReturn = window[functions[i]]();
if(!bReturn) break;
}
To this
for (var i = 0; i < functions.length; i++ ) {
var bReturn = window[functions[i]]();
if(!bReturn) i++;
}
Will make it skip one function, every time a function returns false.
You can try it out here.
On a side-note, if you were looking for a waiting function that "pauses" the script, you could use this piece of code.
function pausecomp(millis){
var date = new Date();
var curDate = null;
do {
curDate = new Date();
}while(curDate-date < millis);
}
Update
After adjusting the code, it now works with setTimeout.
The idea is that you have an entry point, starting with the first function in the array, and pass along an index parameter of where you currently are in the array and then increment index with one to execute the next function.
Example | Code
function next_function(index){
if(index >= functions.length) return false;
setTimeout(function(){
window[functions[index+1]](index+1);
}, 1000);
}
function a(index){
//Do stuff
if(((new Date()).getSeconds() % 2)) return false; //Stop?
next_function(index);
}
function b(index){
//Do stuff
if(false) return false; //Stop?
next_function(index);
}
function c(index){
//Do stuff
if(true) return false; //Stop?
next_function(index);
}
function d(index){
//Do stuff
if(false) return false; //Stop?
next_function(index);
}
var functions = new Array('a', 'b', 'c', 'd');
//entry point
window[functions[0]](0);
This is exactly the scenario promises solve. In particular, the fact that promises can be broken is perfect for your situation, since a broken promise prevents the chain from continuing (just like a thrown exception in synchronous code).
Example, using the Q promise library discussed in the above-linked slides:
function fooAsync() {
return Q.delay(1000).then(function () {
if ((new Date()).getSeconds() % 2) {
alert("foo");
throw new Error("Can't go further!");
}
});
}
function barAsync() {
return Q.delay(1000).then(function () {
alert("bar");
});
}
var functions = [fooAsync, barAsync];
// This code can be more elegant using Array.prototype.reduce, but whatever.
var promiseForAll = Q.resolve();
for (var i = 0; i < functions.length; ++i) {
promiseForAll = promiseForAll.then(functions[i]);
}
// Of course in this case it simplifies to just
// promiseForAll = fooAsync().then(barAsync);
promiseForAll.then(
function () {
alert("done!");
},
function (error) {
alert("someone quit early!");
// and if you care to figure out what they said, inspect error.
}
).end();

Categories