It is possible to determine the order of TWO tasks using callbacks, as shown below.
a(b);
function a(callback) {
// do something
callback();
}
function b() {
// do next
}
See Fiddle
First do a(), then do b().
I would like to concatenate more than two tasks.
As I´m dealing with quite big functions, I´m looking for something like that:
a(b(c));
First do a(), then do b(), then do c().
However I'm not successful with this. See Fiddle
Is there an easy way to do so, maybe without needing Promises?
You're calling b immediately, not passing a callback to a. You'll need to use a function expression:
a(function(aResult) {
b(c);
});
Of course, you can avoid these by returning closures from all your functions:
function a(callback) {
return function(args) {
// do something
if (callback) callback(res);
};
}
function b(callback) {
return function(aResult) {
// do next
if (callback) callback(res);
};
}
function c(callback) {
return function(bResult) {
// do next
if (callback) callback(res);
};
}
which you would call like this:
a(b(c())();
(this is known as pure continuation passing style)
Related
I'm a noob in coding. I'm really sorry about that. I have this code here....
function fn1(){
setTimeout(function(){console.log("fn1")},3000)
}
function fn2(){
console.log("fn2")
}
function fn3(callback, callback2){
if(callback){
console.log("It works!")
} else {
callback2
}
}
fn3(fn1(),fn2())
The objective here is to call Function3(fn3) console.log ("It works") only after Function1(fn1) runs ok. If Function1 fails, it should return Function2.
The output that I'm getting is:
fn2
fn1
I know it is extremely wrong but I don't know WHAT is wrong.
I know that there are other stuff (that I still don't know - like promises, async, await and stuff) but I wanna learn this method first.
Could you guys help me?
Yes, you are right. This is a huge nonsense.
Correct me if I'm wrong: you want the console to log "It works!" after the timeout set in the fn1. Then you'll need the fn1 to tell somehow that it finished it's execution so it can run the console.log. If you want to use the callback approach, then the fn1 could be something like:
function fn1(callback, errorCallback) {
setTimeout(function() {
try {
callback();
} catch (error) {
errorCallback();
}
}, 3000);
}
And the fn3 could go like this:
function fn3() {
fn1(function() { console.log('It works!'); }, fn2);
}
You can call fn3 just with fn3().
To be honest, as simple as these functions are, there's no way the fn2 is called as there are no errors in the code and no human interaction. Well, maybe if there's no standard output available to do the console.log, but I don't know if that's even possible.
As you can see, in the fn3 we are passing two functions as parameters to fn1. The first is an anonymous function that shows the text in the output. The second function is the fn2, that should be run in case of error. No parenthesis here when passing them as parameters as we want to pass functions, not the result of calling them (which would be undefined in this example).
The fn1 receives those functions as parameters and runs them in different situations. The first, when the timeout has finished, and the second if there's any error when calling the callback.
You have to pass references on the functions as parameters of fn3 (you currently call them and pass their result).
Also, in the fn3, you need to call the function.
Your code fixed below (if I correctly understood what you were looking for):
function fn1(next) {
setTimeout(next, 3000);
}
function fn2() {
console.log("fn2")
}
function fn3(callback, callback2){
function itWorks() {
console.log('it works!');
}
try {
callback(itWorks);
}
catch (e) {
callback2();
}
}
fn3(fn1, fn2); // Will display "it works!" after 3 seconds
fn3(undefined, fn2); // Will display "fn2" because trying to call an undefined function
I understood that you want to call f3 after the f1 is called successfully and call the f2 when f1 fails? If yes then see this
function fn3(){
console.log("It Works!");
}
function fn2(){
console.log("fn2");
}
function fn1(){
const promisePass = new Promise((resolve, reject) => {
setTimeout(resolve, 100)}); //Emulate success
const promiseFail = new Promise((resolve, reject) => {
setTimeout(reject, 100);});//Emulate fail
promisePass.then(function(){
fn3();
}).catch(function(){
fn2();
});
promiseFail.then(function(){
fn3();
}).catch(function(){
fn2();
});
}
fn1(); //<<-- calling f1 now will pass and fail the setTimeout method and then
// fn2 and fn3 are called accordingly
I would like to make a conditional asynchronous function call in JavaScript/ES5.
Say I have a module myService with a function myAsyncFunction, which is doing some logic inside and returns result with a callback like the following:
myService.myAsyncFunction(param1, param2, (error, success) => {
if (error) {
// handle error
return;
}
// handle success
});
But in certain conditions, I would like it to directly fall into the "handle success" without running the logic inside myService.myAsyncFunction, which may take some time and resources.
The most straightforward way that I imagine is to create a dummy function which imitates myService.myAsyncFunction and calls the callback with no error like the following:
var myGenericFunction;
if ( /* I would like to skip unnecessary logic */) {
myGenericFunction = function (param1, param2, callback) {
callback(null);
}
} else {
myGenericFunction = myService.myAsyncFunction;
}
myGenericFunction(param1, param2, (error, success) => {
if (error) {
// handle error
return;
}
// handle success
});
This is what I have ended up with but I guess there should be a better way in terms of both syntax and performance. Do you have any suggestion for it?
Note: Promise.resolve on ES6 can propose a solution to directly fall into success case but I wonder what would be ES5 solution.
First, it's super easy to pull in a Promise polyfill for javascript environments that don't support it. It should work all the way back to Ecmascript 3.
Ignoring that, if you have an async function such as this:
function myAsync(arg1, callback) {
}
But for some value or arg1 you want to immediately call the callback, but you can't change the function, the reasonable way to do this, is indeed to just wrap it:
function myAsyncWrapped(arg1, callback) {
if (arg1 === SOME_VALUES) {
callback(null, true);
} else {
myAsync(arg1, callback);
}
}
If you need to do this for many async functions, it could be generalized as:
function asyncWrapper(innerFunction) {
return (arg1, callback) {
if (arg1 === SOME_VALUES) {
callback(null, true);
} else {
innerFunction(arg1, callback);
}
}
}
so you can call this as:
myAsyncWrapped = asyncWrapper(myAsync);
I can think of a couple of alternate approaches to rolling your own Promise implementation.
You can use an existing one, such as Bluebird - http://bluebirdjs.com/docs/getting-started.html
You can also use a transpiler, such as Babel, which would allow you to write all of the next-gen ES that you wish and have it converted to ES5 pre-runtime - https://babeljs.io/docs/en
Here are two callback function:
function callback_a(){
alert('a');
}
function callback_b(p){
alert('b says'+ p)'
}
If I want use callback_a
function test(callback){
if(condition){
callback();
}
}
test(callback_a);
But the function test isn't applicable to callback_b, So how to implement a common function that you can passing some callbacks function with multiple possible parameter lists.
There are three options:
The easiest way is to use spread operator:
function test(callback, ...callback_args) {
callback(...callback_args);
}
in this case the invocation of test for function callback_b would be like this:
test(callback_b,"b")
The second way is using arguments which are scoped to any function in JavaScript:
function test(callback) {
callback.apply(null, arguments.slice(1));
}
the invocation of test for function callback_b would be the same:
test(callback_b,"b")
Another options is to use partially applied functions. In this case you should define b_callback like this (ES6 syntax):
let callback_b = (p) => () => void{
alert('b says'+ p)'
}
or without ES6:
function callback_b(p) {
return function(){
alert('b says'+ p)'
}
}
and invoke it like this:
test(callback_b("b"))
There is a special object called arguments that gets created when a function is invoked. It's an array-like object that represents the arguments passed in to a function:
It can be used like this:
test();
// no arguments passed, but it still gets created:
// arguments.length = 0
// arguments >> []
test(a);
// ONE argument passed:
// arguments.length = 1
// arguments >> [a]
test(a,b,c,d);
// FOUR arguments passed:
// arguments.length = 4
// arguments >> [a,b,c,d]
Knowing this, one can call a callback with the rest of the arguments passed in from the parent function using apply like this:
function test(callback) {
callback.apply(null, Array.prototype.slice.call(arguments, 1));
}
// arguments passed into test are available in the function scope when
// .slice is used here to only pass the portion of the arguments
// array relevant to the callback (i.e. any arguments minus the
// first argument which is the callback itself.)
//
// N.B. The arguments object isn't an array but an array like object so
// .slice isn't available on it directly, hence .call was used here)
Might be worth reading up on:
The arguments object
Function.prototype.apply, Function.prototype.call and Function.prototype.bind as they are way to bind a context and arguments to a function (i.e. they'll work with the arguments object to call a function where you may not know how many arguments will be passed)
So how to implement a common function that you can passing some callbacks function with multiple possible parameter lists.
Basically, you don't. The function receiving the callback is in charge of what the callback receives as arguments. When you call Array#forEach, it's Array#forEach that decides what arguments your callback gets. Similarly, String#replace defines what it will call its callback with.
Your job is to say what test will do, what it will call its callback with. Then it's the job of the person using test to write their callback appropriately. For instance: You might document test as calling the callback with no arguments. If the caller wants to use callback_b, then it's up to them to handle the fact that callback_b expects a parameter. There are several ways they can do that:
The could wrap it in another function:
test(function() {
callback_b("appropriate value here");
});
...or use Function#bind
test(callback_b.bind(null, "appropriate value here"));
...but it's their problem, not yours.
Side note: If they pass you callback_b and you call it without any arguments, you won't get an error. JavaScript allows you to call a function with fewer arguments than it expects, or more. How the function handles that is up to the author of the function.
You can pass an anonymous function as the callback that will itself return your desired callback function with parameters.
test(function() { return callback_b(' how are you'); });
see this working snippet that will first use callback_a, then callback_b (with parameter) as the callback:
function callback_a(){
alert('a');
}
function callback_b(p){
alert('b says'+ p);
}
function test(callback){
if(true){
callback();
}
}
test(callback_a);
test(function() { return callback_b(' how are you'); });
You can pass the parameter while calling the callback
function test(callback){
if(condition){
callback();
}
else if(other condition){
callback("b");
}
}
test(callback_b);
You can write your callback function like
function callback_a_b(){
if(arguments.length){
var arg = [].slice.call(arguments);
alert('b says'+ arg[0])
}
else{
alert('a');
}
}
You can pass array of parameters as second param of test function or in ES6 use spread operator read more here
function test(callback, params){
if(condition){
if (params === undefined){
callback();
} else {
callback.apply(null, params); //params must be array
//ES6: callback(...params);
}
}
}
test(callback_a);
test(callback_b, [" whatever"]);
I've just checked in my browser (ffox 51.0.1) that the following works:
function test(callback,other_args){if(condition){callback(other_args);}}
results:
condition=true
test(callback_a)
=> shows the alert with 'a'
condition=false
test(callback_a)
=> doesn't show anything
condition=true
test(callback_b,"pepe")
=> shows the alert with 'b sayspepe'
condition=false
test(callback_b,"pepe")
=> doesn't show anything
I have the following pattern which strings together function1, 2 and 3 through their callbacks.
Assume that function1, 2 and 3 can take up to 1 second to complete. I would like to know other "better" ways of doing the same so that it doesn't turn into a monster when more callback functions are nested.
function1(function(cbData1){
if(cbData1){
function2(cbData1, function(cbData2){
if(cbData2){
function3(cbData2, function(cbData3){
// success
}
} else {
// failed for reason#2
}
});
} else {
//failed for reason#1
}
});
//example function
function function2(data, callback) {
// do dirty things
callback(newData);
}
If I understand you correctly you need to organize the callbacks in a chain. Look at Chain of Responsibility pattern.
So you will create an object containing the function to execute and callback function to execute if needed.
The last time I played with really nasty callbacks, I ended up doing something like this:
// Typed on the fly, be kind
var callbackList = []; // A list of functions to call in order.
function doNextCallback() {
if (callbackList.length) {
var f = callbackList.shift(); // Get the next callback function
window.setTimeout(f); // Give breathing space.
}
}
// Set up our callbacks
callbackList.push(f1);
callbackList.push(f2);
callbackList.push(f3);
// Start it happening.
doNextCallback();
function f1() {
console.log("Busy busy");
doNextCallback();
}
function f2() {
console.log("Busy busy");
doNextCallback();
}
function f3() {
console.log("Busy busy");
doNextCallback();
}
I had it all wrapped up in a nice object, but you get the idea.
This also made it very easy to re-arrange callbacks or to call just two of them in a big loop for testing purposes.
I need a bunch of functions to be called in strict order. It's also very important that the next function waits until the previous one has finished.
Right now I'm using chained callbacks:
callMe1(function(){
callMe2(function(){
callMe3(function(){
callMeFinal();
});
});
});
This works but seems to be a little ugly.
Any suggestions for a different approach?
If you use jQuery, then you can use queue to chain the functions.
$(document)
.queue(callMe1)
.queue(callMe2);
where callMeX should be of form:
function callMeX(next) {
// do stuff
next();
}
You can implement a "stack" system:
var calls = [];
function executeNext(next) {
if(calls.length == 0) return;
var fnc = calls.pop();
fnc();
if(next) {
executeNext(true);
}
}
/*To call method chain synchronously*/
calls.push(callMe3);
calls.push(callMe2);
calls.push(callMe1);
executeNext(true);
/*To call method chain asynchronously*/
calls.push(callMe3);
calls.push(function(){
callMe2();
executeNext(false);
});
calls.push(function(){
callMe1();
executeNext(false);
});
Not sure if this would help you, but there is a great article on using deferreds in jQuery 1.5. It might clean up your chain a bit...
Also, my answer on Can somebody explain jQuery queue to me has some examples of using a queue for ensuring sequential calls.
You might want to pass parameters to the functions, I do not believe you can at the time of this writing. However...
function callMe1(next) {
console.log(this.x);
console.log("arguments=");
console.log(arguments);
console.log("/funct 1");
this.x++;
next();
}
function callMe2(next) {
console.log(this.x);
console.log("arguments=");
console.log(arguments);
console.log("/funct 2");
this.x++;
next();
}
function callMe3(next) {
console.log(this.x);
console.log("arguments=");
console.log(arguments);
console.log("/funct 3");
this.x++;
next();
}
var someObject = ({x:1});
$(someObject).queue(callMe1).queue(callMe2).queue(callMe3);
Wrapping your functions, arguments intact, with an anonymous function that plays along with .queue works too.
Passing Arguments in Jquery.Queue()
var logger = function(str, callback){
console.log(str);
//anything can go in here, but here's a timer to demonstrate async
window.setTimeout(callback,1000)
}
$(document)
.queue(function(next){logger("Hi",next);})
.queue(function(next){logger("there,",next);})
.queue(function(next){logger("home",next);})
.queue(function(next){logger("planet!",next);});
Example on JSFiddle: http://jsfiddle.net/rS4y4/