i have problem to create deep number of callback function in javascript dynamically. For example i have function like this.
function process(value, callback) {
console.log('process ' + value)
callback()
}
function complete() {
console.log('complete')
}
function running(count){
// number process function is two
if (count==2) {
process('number one', function () {
process('number two', function () {
complete() => // last callback is closed by complete function
})
})
}
// number process function is three
if (count==3) {
process('number one', function () {
process('number two', function () {
process('number three', function () {
complete() => // last callback is closed by complete function
})
})
})
}
}
running(3);
Output :
process number one
process number two
process number three
completed
i want to create number callback is dynamic and closed by complete function, not using if/switch command, how to do that? thank you
You could use recursion and that will exit and run complete when the count param is zero otherwise it will call process function.
function process(value, callback) {
console.log('process ' + value)
callback()
}
function complete() {
console.log('complete')
}
const p = ['three', 'two', 'one']
function running(count) {
if (count) {
process(`number ${p[count - 1]}`, () => {
running(count - 1)
})
} else {
complete()
}
}
running(3);
You can do without using callback in this case:
var processNames = ['one', 'two', 'three'];
function process(value) {
console.log('process ' + value);
}
function complete() {
console.log('complete');
}
function running(count){
for (var index = 0; index < count; index++) {
var name = 'number ' + processNames[index];
process(name);
}
complete();
}
running(3);
You could use promises for this.
I create the three promises using a for loop and put them in an array. The resolve functions write the index to the console. Use Promise.all to execute them in order and use finally to write completed.
With the promise we don't need to provide a callback function.
function process(value) {
console.log('process ' + value)
}
function complete() {
console.log('complete')
}
const textInt = ['one', 'two', 'three']
function running(count){
const promiseArray = [];
for (let i = 0; i < count; i++)
{
promiseArray[new Promise(function(){
process(textInt[i], function(){});
})];
}
Promise.all(promiseArray).finally(complete)
}
running(3);
Related
Hi I'm trying to understand callbacks in javascript and have come across this code here from a tutorial that I'm following:
var EventEmitter = require('events');
var util = require('util');
function Greetr() {
this.greeting = 'Hello world!';
}
util.inherits(Greetr, EventEmitter);
Greetr.prototype.greet = function(data) {
console.log(this.greeting + ': ' + data);
this.emit('greet', data);
}
var greeter1 = new Greetr();
greeter1.on('greet', function(data) {
console.log('Someone greeted!: ' + data);
});
greeter1.greet('Tony');
Now I notice that the greeter1.on function takes a callback with a parameter. However I'm not sure how this is implemented internally. I tried looking through the nodejs event.js file but I'm still confused. I am aware that there are ways around this specific implementation by using an anonymous function wrapping the callback with parameters but I want to understand how to use the same format as above.
tldr: How can I create my own function that takes a callback and a parameter in the same fashion as greeter1.on above.
Thank you
Your function needs to define a new property on the current instance with the callback passed as an argument, so it can be called later, like so:
function YourClass () {
this.on = function(key, callback) {
this[key] = callback;
}
}
// Usage
const instance = new YourClass();
instance.on('eventName', function (arg1, arg2) {
console.log(arg1, arg2);
});
instance.eventName("First argument", "and Second argument")
// logs => First argument and Second argument
Callback is just passing a function as a parameter to another function and that being triggered. You can implement callback fashion as below
function test(message, callback) {
console.log(message);
callback();
}
//Pass function as parameter to another function which will trigger it at the end
test("Hello world", function () {
console.log("Sucessfully triggered callback")
})
class MyOwnEventHandler {
constructor() {
this.events = {};
}
emit(evt, ...params) {
if (!this.events[evt]) {
return;
}
for (let i = 0, l = this.events[evt].length; i < l; i++) {
if (!params) {
this.events[evt][i]();
continue;
}
this.events[evt][i](...params);
}
}
on(evt, eventFunc) {
if (!this.events[evt]) {
this.events[evt] = [];
}
this.events[evt].push(eventFunc);
}
}
var myHandler = new MyOwnEventHandler();
myHandler.on('test', function (...params) {
console.log(...params);
});
myHandler.emit('test', 'Hello', 'World');
I am trying to execute following array (avoid callbackHell) of functions(sync/async), in a sequential order, implementing function runCallbacksInSequence (I need to implement my own function to understand how callbacks work and avoid using Async.js).
Here is what I have so far. The function runCallbacksInSequence works well till it gets the same callback more than once. It stops and does not continue to execute the next callback. Ideally if it gets the same callback more than once it should not execute it second time and continue with the next callback.
If you have any ideas let me know what I am doing wrong and how I can fix it.
- no promises and async/await
function first(cb) {
setTimeout(function() {
console.log('first()');
cb(null, 'one');
}, 0);
}
function second(cb) {
setTimeout(function() {
console.log('second()');
cb(null, 'two');
}, 100);
}
function third(cb) {
setTimeout(function() {
console.log('third()');
cb(null, 'three');
}, 0);
}
function last(cb) {
console.log('last()');
cb(null, 'lastCall');
}
const cache = {};
function runCallbacksInSequence(fns, cb) {
fns.reduce(
function(r, f) {
return function(k) {
return r(function() {
if (cache[f]) {
return;
// f(function(e, x) {
// e ? cb(e) : k(x);
// });
} else {
cache[f] = f;
return f(function(e, x) {
return e ? cb(e) : k(x);
});
}
});
};
},
function(k) {
return k();
}
)(function(r) {
return cb(null, r);
});
}
const fns = [first, second, third, second, last];
runCallbacksInSequence(fns, function(err, results) {
if (err) return console.log('error: ' + err.message);
console.log(results);
});
Your function chaining depends on the call to k(). Therefore in your cache logic:
if (cache[f]) {
return;
} else {
// ...
The chain breaks.
What you want instead is this:
if (cache[f]) {
return k();
} else {
// ...
Alternative Implementation
One of the problems with the nested function implementation is that it is hard to reason about due to multiple nesting scopes (and multiple functions being juggled at once (r, f, k, cb).
A simpler approach to this is rather than trying to programmatically build callback hell you can use a queue instead (which is what async.js does). The idea is simple, pop() or shift() functions from an array until the array is empty:
function runCallbacksInSequence(fns, cb) {
let result = [];
let cache = {};
function loop () {
if (fns.length > 0) {
let f = fns.shift(); // remove one function from array
if (cache[f]) {
loop(); // skip this round
return;
}
cache[f] = f;
f(function(err, val) {
if (!err) {
result.push(val); // collect result
loop();
}
else {
// Handle errors however you want.
// Here I'm just terminating the sequence:
cb(err, result);
}
});
}
else {
cb(null, result); // we've collected all the results!!
}
}
loop(); // start the loop
}
As you can see, it's fairly easy to implement any flow logic with this structure. We can easily implement things like waterfall, parallelLimit etc. by controlling how we keep track of results and how many functions we remove from the array per iteration.
I guess with implementation based on cache you may omit doubled step with a direct k() invocation.
return;
if (cache[f]) {
return;
// f(function(e, x) {
// e ? cb(e) : k(x);
// });
Idea:
if (cache[f]) {
return k(function(e, x) {
return e ? cb(e) : k(x);
});
Your code is a little bit hard to read for me. So here is the alternative solution:
<script>
// The data
function first(cb) {
setTimeout(function () {
console.log('first()');
cb(null, 'one');
}, 0);
}
function second(cb) {
setTimeout(function () {
console.log('second()');
cb(null, 'two');
}, 100);
}
function third(cb) {
setTimeout(function () {
console.log('third()');
cb(null, 'three');
}, 0);
}
function last(cb) {
console.log('last()');
cb(null, 'lastCall');
}
const fns = [first, second, third, second, last];
// We need hash function to create the identifyer of the function
function hashCode(str) {
return Array
.from(str)
.reduce((s, c) => Math.imul(31, s) + c.charCodeAt(0) | 0, 0);
}
const cache = [];
function reducer(accumulator, currentFunction) {
// Take the functon string representation to detect "the same function"
const hash = hashCode(currentFunction.toString());
// Process the result of the current function and call the next one.
// We use "reduceRight" so `accumulator` is the next function in the chain.
const cb = function (fp, result) {
console.log(result);
// Cache the result;
cache[hash] = result;
accumulator();
}
// Run just a callback if we already have the result of the current function
return () => cache[hash] ? cb(null, cache[hash]) : currentFunction(cb);
}
fns.reduceRight(reducer, () => { })();
</script>
Result:
first()
one
second()
two
third()
three
two
last()
lastCall
If you do not want to process the cached result at all, then replace the call to the callback with the call to the accumulator directly.
return () => cache[hash] ? cb(null, cache[hash]) : currentFunction(cb);
replace with:
return () => cache[hash] ? accumulator() : currentFunction(cb);
Result:
first()
one
second()
two
third()
three
last()
lastCall
Solution without cache
It is much cleaner:
<script>
// Use the same data as in the example with cache
function reducer(accumulator, currentFunction) {
const cb = function (fp, result) {
console.log(result);
accumulator();
}
return () => currentFunction(cb)
}
fns.reduceRight(reducer, () => { })();
</script>
Result:
first()
one
second()
two
third()
three
second()
two
last()
lastCall
I want to use async.whilst function and probably missing something badly when I'm getting just the first console.log on the output.
// app.js file
var async = require('async');
var count = 0;
async.whilst(
function () {
console.log('first')
return count < 5;
},
function (callback) {
count++;
console.log('second')
callback()
},
function (err) {
console.log('third')
}
);
// run the script
$ node app.js
first
$
Have a look at the documentation: you need a callback also for the first function
var async = require('async');
var count = 0;
async.whilst(
function (callback) {
console.log('first')
return callback(null, count < 5);
},
function (callback) {
count++;
console.log('second')
callback()
},
function (err) {
console.log('third')
}
);
You should use a callback inside your first function, async make calls to subsequent function when the callback gets invoked. your code should be
async.whilst(
function (cb) {
console.log('first')
cb(null,count < 5);
},
function (callback) {
count++;
console.log('second')
callback()
},
function (err) {
console.log('third')
}
);
Could you please tell me how do I write a function to check whether a specific function has been called, how many times it was called - with or without parameter.
Thanking you in advance
#elclanrs's solution is really good, but there are multiple problems with it:
You need to call the tracker function instead of the original function. That means you need to change some of your original code in order to use it.
You need to store a reference to the tracker object to get count.
Here is a solution for those problems:
function track() {
var calls = [],
context = window,
funcName,
i = 0;
if (arguments.length === 1) {
context = window;
funcName = arguments[0];
} else {
context = arguments[0];
funcName = arguments[1];
}
var func = context[funcName];
context[funcName] = function () {
calls.push({
count: i++,
args: Array.prototype.slice.call(arguments)
});
return func.apply(context, arguments);
};
context[funcName].getCalls = function () {
return calls;
};
}
Usage example:
// The function we want to track.
function log(val) {
console.log(val);
}
// Start tracking the function
track('log');
// Normal usage of the function
log('Message 1');
log('Message 2');
// Print the collected data of the function
console.log(log.getCalls());
/*^
[ { count: 0, args: [ 'Message 1' ] },
{ count: 1, args: [ 'Message 2' ] } ]
*/
Note: if your function is not in the global context (for example: document.getElementById), you need to do something like:
track(document, 'getElementById');
You can then collect the data normally:
document.getElementById.getCalls()
Each function has a caller property defined. You can make like this
function callfn() {
if (callfn.caller == null) {
return ("The function was called from the top!");
} else
return ("This function's caller was " + callfn.caller);
}
}
try something like this
var counter = {};
counter.with_param = 0;
counter.without_param = 0;
function test(arg){
if(!arg){
counter.without_param = counter.without_param + 1;
}else{
counter.with_param = counter.with_param + 1;
}
}
test(1);
test(5);
test();
test();
test(6);
console.log('WITH PARAM ' + counter.with_param);//3
console.log('WITHOUT PARAM ' + counter.without_param);//2
console.log('TOTAL CALLING ' + (counter.with_param + counter.without_param));//5
You could make a function decorator that saves the count and arguments in a closure, something like:
// Helper to convert pseudo-arrays
// such as `arguments` to real arrays
var __slice = Array.prototype.slice;
// Higher-order function that
// returns a function that saves the count
// and arguments when called
function track(f) {
var i = 0; // count
var calls = []; // array to save the info
var fn = function() {
// Save arguments and incremented counter
calls.push({
count: i++,
args: __slice.call(arguments)
});
// Call the original function with arguments
// preserving the context, and return its value
return f.apply(this, arguments);
};
// A function, attached to the decorated function,
// that gets the info out of the closure
fn.count = function() {
return calls;
};
// Return decorated function to be used
return fn;
}
// Decorate a normal function
var add1 = track(function(x) {
return x + 1;
});
// Run the function 3 times
// Internally the decorated function will keep
// track of the count and arguments
add1(1);
add1(2);
add1(3);
// Using the `count` function of our decorated function
console.log(add1.count());
/*^
[ { count: 0, args: [ 1 ] },
{ count: 1, args: [ 2 ] },
{ count: 2, args: [ 3 ] } ]
*/
I have a simple "async" JS function:
function asyncFunc(i) {
setTimeout(function () {
console.log(i);
}, 1000);
}
if I want to execute this asyncFunc 5 times in a for loop, i.e. log 1 - 5 per second, and totally costs 5 seconds.
1
2
3
4
5
I know jQuery's when().done() can do that however if I am in a environment with NO 3rd party JS libraries, what is the simplest and elegant way to achieve this?
Actually for example I want to write a util function which accepts an array of async functions, and this util function can execute passed in functions one by one:
function execAsyncTasks([asyncTask1, asyncTask2, asyncTask3]) {
asyncTask1();
// Wait until asyncTask1 finished
asyncTask2();
// Wait until asyncTask2 finished
asyncTask3();
// Wait until asyncTask3 finished
}
All your tasks will have to implement some sort of callback mechanism, because that's the only way you'll ever know that an asynchronous task has been completed. Having that, you could do something like:
function executeTasks() {
var tasks = Array.prototype.concat.apply([], arguments);
var task = tasks.shift();
task(function() {
if(tasks.length > 0)
executeTasks.apply(this, tasks);
});
}
executeTasks(t1, t2, t3, t4);
Demo
You can use Async module:
https://github.com/caolan/async
async.parallel([
function(){ ... },
function(){ ... }
], callback);
async.series([
function(){ ... },
function(){ ... }
]);
This is one approach of many
function execAsyncTasks(asyncTask1, asyncTask2, asyncTask3) {
var count = 0;
function nextCommand() {
switch (count) {
case 0:
asyncTask1();
break;
case 1:
asyncTask2();
break;
case 2:
asyncTask3();
default:
return;
}
count++;
setTimeout(nextCommand, 1000);
}
nextCommand();
}
you can have a sync mechanism using callbacks and recursive function calls: see http://jsfiddle.net
function asyncFunc(i, callback) {
setTimeout(function() {
document.body.innerHTML += '<p>' + i + '</p>';
callback();
}, 1000);
}
var args = [0, 1, 2, 3, 4];
function loopThroughArgs(callback) {
if (args.length == 0) {
callback();
} else {
asyncFunc(args[0], function() {
args.splice(0, 1); //remove the first item from args array
loopThroughArgs(callback);
});
}
}
loopThroughArgs(function() {
document.body.innerHTML += '<p>done !</p>';
});