I am trying to understand the difference between the 3 options:
.then(myCallback)
.then(myCallback())
.then(() => { myCallback() })
The myCallback function returns nothing and is used only for its side effects, so I don't need it to pass anything back to the promise chain. What I don't understand is why in my code only the second option triggers the function, when it seems like all the 3 should.
UPDATE: Here is the code with the bare essentials
serverCall(url, data) // fetch function
.then((response) => response.json())
.then(myCallback) // not running, only when () added
.catch((error) => { console.log(error) })
const myCallback = () => {
anotherServerCall(...).then(...)
}
UPDATE 2
After some digging I see that having .then((response) => response.json()) as the first then response is what is preventing .then(myCallback) to execute. Still no idea why...
All three should trigger the function, but the order differs between the approaches. If you're sometimes seeing evidence that the serverCall or myCallback aren't being invoked, then that has something to do with the particulars of those functions, not the ways you are calling them.
To demonstrate, consider two proxies for serverCall and myCallback that we know will always work. Let's apply each idea in your question to those:
This is the "normal" way to use then. Pass it a function that is invoked after the promise to which it is attached...
function serverCall() {
console.log('began server call');
return new Promise(function(resolve) {
setTimeout(() => {
console.log('completed server call');
resolve();
}, 2);
});
}
function myCallback() {
console.log('callback called');
}
serverCall().then(myCallback).then(() => console.log('done'));
// produces:
// began server call
// completed server call
// callback called
// done
Your first and third ideas are almost the same. Pass then a function which is invoked after the promise. In your third idea, the function isn't the callback, it's a function that calls the callback. One more stack frame, but the exact same effect...
function serverCall() {
console.log('began server call');
return new Promise(function(resolve) {
setTimeout(() => {
console.log('completed server call');
resolve();
}, 2);
});
}
function myCallback() {
console.log('callback called');
}
serverCall().then(() => { myCallback() }).then(() => console.log('done'))
// produces:
// began server call
// completed server call
// callback called
// done
Your second idea, as a commenter points out, invokes the function and passes it's result to then. The chaining of the then runs synchronously after starting the promise, so the results appear reordered: myCallback runs before the promise completes...
function serverCall() {
console.log('began server call');
return new Promise(function(resolve) {
setTimeout(() => {
console.log('completed server call');
resolve();
}, 2);
});
}
function myCallback() {
console.log('callback called');
}
serverCall().then(myCallback()).then(() => console.log('done'))
// produces:
// began server call
// callback called <------ CHANGED!!!
// completed server call
// done
The only way I've found to duplicate your problem is if the callback returns a function. Options 1 and 3 don't do anything because the returned function isn't being called. Option 2 is called and is successful.
function fetch() {
return new Promise((resolve, reject) => {
resolve();
});
}
function myCallBack() {
return function () {
console.log('Success');
};
}
fetch().then(myCallBack); // nothing
fetch().then(myCallBack()); // Success
fetch().then(() => myCallBack()); // nothing
Your use cases are different because
The first one then call, you are passing a callback to be invoked when then it’s executed, passing a naming function will appear in the stack trace if some error occurred
The second one, execute the callback passed before being passed to the then function and the result of that execution callback will be passing to the then function, so imagine this.
function myCallback(){
return function theRealCallbackPassed(thenResponse){
// do something with then response
}
}
The last one, will define an anonymous arrow function , so what is the diff between
then( () => {} )
And
then( function(){} )
The different is that using arrow function () => {} you have a short syntax and you are binded the function contexto to the current context where the arrow function it’s declared.
While the function() {} syntax is not shorter and it is not auto bindable.
Hope it can help you.
Related
I have function:
function a(){ setTimeout(()=>{console.log('a')}, 0) }
This function send console.log on very end of callstack isn't it?
How can I write a function that runs after this console.log
the only way i found is send function on the of of callstack in the same way
a();
setTimeout(()=>{ console.log('after a'), 0 });
But it looks bad for me. I tried with promise, but i can react then on 'a' function not on console.log inside.
function 'a' is uneditable
you can use "callback" , a reference to another function to the a( ) function. Example:
function yourFunctionToBeRunnedAfter(){
..
..
}
function a(callback){
setTimeout(()=>{
console.log('a');
calback(); // ==> here is your function execution.
}, 0);
}
a(yourFunctionToBeRunnedAfter);
if you have parameters to pass to the callback() call, you can use apply() or call() or bind() or the spread operator (...) example:
function a(callback,...params){ // ... is the spread operator
setTimeout(()=>{
console.log('a');
callback([...params]); // ==> here is your function execution by spreading your params to the callback call.
}, 0);
}
a(yourFunctionToBeRunnedAfter, 1,2,3);
You need to use a promise. I'd recommend using async-await, but it really depends on your preference.
function a () {
return new Promise(resolve => {
setTimeout(() => {
console.log('a')
resolve()
}, 0)
})
}
a().then(() => console.log('after a'))
If you need to know when timeouts finish often, I'd recommend making a helper function:
const wait = (time) => new Promise(r => setTimeout(r, time))
Then you can use it like so:
(async () => {
console.log('Hello')
await wait(1000) // wait 1 second
console.log('Hello a second later')
})()
I see a piece of code similar to the one below in some npm package:
this.func(callback).then(function() {
...
return x;
}).then(function() {
...
return y;
}).then(function() {
...
return z;
}).then(function() {
mocha.run(function(failures) {
...
callback(failures);
});
}).catch(callback);
Questions:
What is the meaning of this catch(callback) with no {...} block following it?
I would like to add a finally clause to execute the callback, but every syntax that I'm trying seems to fail:
.catch(callback).finally(callback);
.catch(callback).finally(callback());
.catch(callback).finally{callback()};
.catch(callback).finally(){callback()};
In your case, then and catch refer to Promise's prototype and not to the native catch implementation. Check this example to understand it better:
let doSomething = () {
return new Promise((resolve, reject) => {
try {
reject();
} catch(e) {
reject();
} finally {
console.log('done');
}
});
}
doSomething().then(() => {}).catch(() => {});
Note that anything you'd do, catch will be called.
In your case catch function refers to your callback function which you are passing in catch block
//first case
function callback(){}
catch(callback);
//second case
catch(function(){})
Both case will work
and for finally It is still lacking browser support, you can check here at bottom of this page
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally
and check this for how to do finally in alternative way.
what is the equivalent of bluebird Promise.finally in native ES6 promises?
you can try like this.
For more detail on promise: see here
doSomething(()=>{//do something})
.then(()=>{})
.catch((error)=> { console.log(error); })
.finally(()=> { //finally block here });
Question 1: The Promise API calls the function passed in the catch whenever a promise gets "rejected". So consider the following code:
// method 1: define the callback function
var callback = function(error){
console.error(error); // console the error
};
this.func.then(...).catch(callback);
// method 2: pass the function assigned to "callback" variable itself
this.func.then(...).catch(function(error){
console.error(error); // console the error
});
You are just telling the promise returned by the above (in your code) function call(s) that:
"Hey, whenever you fail to do the task, call this function callback that I (or someone else) have defined."
Question 2: The first method in your list of four methods should work.
I have a series of promise functions that I'm chaining together. I don't need the specific result from the previous function so I'm not adding anything into resolve(); I just need them to run sequentially.
However, there is a variable in the containing scope that I want to pass into the first and fourth(last) promise. When I add it as a parameter to the third promise, the function runs immediately.
How can I pass a parameter into a chained promise and not call the function/promise at that time?
Here is the basics of what I'm trying to do:
const containingFunction = function() {
const varToPass = document.querySelector("#ID").value.trim();
firstPromise(varToPass)
.then(secondPromise)
.then(thirdPromise)
.then(fourthPromise(varToPass))
.catch(e =>{
console.error(e));
} );
};
FourthPromise:
const fourthPromise = function(varPassed) {
return new Promise(function(resolve, reject) {
do some stuff but only after the thirdPromise has been resolved
console.log(varPassed)
resolve();
});
};
You have two possibilities depending on how you want to resolve varToPass.
Lambda
Using a lambda function (anonymous function without own scope) as described by #Jaromanda X:
() => return fourthPromise(varToPass)
which will cause the function to keep a reference to the variable and not its value. The value of varToPass will be evaluated as soon as the fourthPromise is fired, not when this code is run.
Wrapper
The second option is using a wrapper, i.e. a function that returns a function:
function fourthPromise(varToPass) {
return function() {
return new Promise(function(resolve, reject) {
do some stuff but only after the thirdPromise has been resolved
console.log(varToPass)
resolve();
});
};
}
In this case the value of the passed variable is evaluated at the time this code runs and not when the callback is called.
Which option is better applicable to your case we can't tell without more context though.
Very simple change
const containingFunction = function() {
const varToPass = document.querySelector("#ID").value.trim();
firstPromise(varToPass)
.then(secondPromise)
.then(thirdPromise)
.then(() => fourthPromise(varToPass))
.catch(e =>{
console.error(e));
} );
};
I have a function, which computes some stuff, notifying the user via callbacks about some events:
function returnAndCallback(callback) {
callback(5); // not always invoked
return 3;
}
Using Mocha and Should.js, I wrote this test:
describe('mixing sync and async code', function() {
it('should test callback AND return value', function(done) {
returnAndCallback(function(value) {
value.should.be.eql(5);
done();
}).should.be.eql(4);
});
});
This succeeds because the test ends when done() is called. It seems, I can either write a synchronous test and check the return value, or I can write an asynchronous test and check the callback.
One can not use a sync test like this:
describe('mixing sync and async code', function() {
it('should test callback AND return value', function() {
returnAndCallback(function(value) {
should.be.eql('difficult to get result');
}).should.be.eql(3);
});
});
... because I want to assert that the callback is called. This test would succeed, if the callback is never called.
How can I test both that the callback is called with the right value AND the correct value is returned?
Duplicating tests is the only option I see.
Edit: I notice, that I use the term asynchron wrongly here. The callbacks are merely a way to inject optional actions into a function, which transforms an object. All code is synchronous, but the control flow sometimes branches of into callbacks and I want to be able to recognize that.
Here is another possible way to test that:
describe('mixing sync and async code', function() {
it('should test callback AND return value', function() {
let callbackValue;
returnAndCallback(function(value) {
callbackValue = value;
}).should.be.eql(3);
callbackValue.should.be.eql(5);
});
});
But still not perfectly elegant due to the extra variable.
First, be aware that done() implies a synchronous test; Mocha's default is to run tests asynchronously. If you want to test the 'returned' value from asynchronous functions (functions that return a value in a callback function), you run them synchronously, via done().
Next, you can't return a value from an asynchronous function. These two behaviours are mutually exclusive:
function returnAndCallback(callback) {
callback(5); // not always invoked
return 3;
}
You want to only execute the callback.
It appears to me that you're expecting that sometimes a callback is passed, but not always. In that case, I'd separate the function tests (I think you'll need to use done() everywhere, to persist the synchronous nature of the tests) and do a check for callback inside the function itself.
Now that we've got that clarified, since you want to assert that a callback is called, we need to establish some baseline assumptions:
A) A callback should be a function
B) A callback should be called with a parameter that contains a value
You want to test for both of these things. A) is easy to prove: you're writing the callback function as part of your test, so if you passed say, null or undefined, of course the test will fail, but that's not the point of this test. Here is how you prove both A) and B):
function optionalAsync(callback) {
if (typeof callback === 'function') {
callback(4)
} else {
return 3
}
}
describe('optional async function', function() {
it('should return 4 when a callback is passed', function(done) {
optionalAsync(function(value) {
should(value).be.eql(4)
done()
})
})
it('should return 3 when no callback is passed', function(done) {
optionalAsync().should.be.eql(3)
done()
})
})
This is kind of strange, but given your use case, it does make sense to check for both possibilities. I'm sure you could reduce the code footprint a bit too, but I'd suggest keeping it this way for the sake of readability for when you shelve tis for a year and forget what you did ;)
Now after all of this if you still want to have the option for a function to run synchronously you can do so by blocking the event loop: https://stackoverflow.com/a/22334347/1214800.
But why would you want to?
Save yourself the trouble of handling synchronous operations in an inherently non-blocking, asynchronous platform, and write everything (even the non-IO-blocking operations) with a callback:
function optionallyLongRunningOp(obj, callback) {
if (typeof callback === 'function') {
validateObject(obj, function(err, result) {
// not always long-running; may invoke the long-running branch of your control-flow
callback(err, result)
})
} else {
throw new Error("You didn't pass a callback function!")
}
}
describe('possibly long-running op async function', function() {
it('should return 4 when control flow A is entered', function(done) {
obj.useControlFlow = "A"
validateObject(obj, function(err, result) {
// this is a slow return
should(result.value).be.eql(4)
done()
})
it('should return 3 when control flow B is entered', function(done) {
obj.useControlFlow = "B"
validateObject(obj, function(err, result) {
// this is a quick return
should(result.value).be.eql(3)
done()
})
})
})
Here is your answer written with everything as callback (even the short ops):
var doLongRunnignOp = function(cb) {
var didNotify = true
cb(didNotify)
}
function doubleAndNotifyEven(num, cb) {
if (num % 2 == 0) {
doLongRunnignOp(function(didNotify) {
cb(num)
// did notify
})
} else {
cb(2 * num)
// instant return, did not notify
}
}
describe('checking return value and callback execution', function() {
it('should double and report given an even number', function() {
doubleAndNotifyEven(2, function(value) {
should(value).be.eql(2)
})
})
it('should double and not report anything given an odd number', function() {
doubleAndNotifyEven(3, function(value) {
should(value).be.eql(6)
})
})
})
Here is another solution for this problem. It just adds an additional line of code in tests which want to ensure callback execution.
let should = require('should');
function doubleAndNotifyEven(num, reportEven) {
if (num % 2 == 0) {
reportEven(num);
}
return 2 * num;
}
function mandatoryCallback(callback) {
mandatoryCallback.numCalled = 0;
return function () {
mandatoryCallback.numCalled++;
return callback.apply(this, arguments);
};
}
describe('checking return value and callback execution', function() {
it('should double and report given an even number', function() {
doubleAndNotifyEven(2, mandatoryCallback(function(value) {
should(value).be.eql(2);
})).should.be.eql(4);
should(mandatoryCallback.numCalled).greaterThan(0);
});
it('should double and not report anything given an odd number', function() {
doubleAndNotifyEven(3, function(value) {
throw new Error('Wrong report!');
}).should.be.eql(6);
});
});
Please also note sinon which does something similar.
You have a prototype object Foo with two async method calls, bar and baz.
var bob = new Foo()
Foo.prototype.bar = function land(callback) {
setTimeout(function() {
callback()
console.log('bar');
}, 3000);
};
Foo.prototype.baz = function land(callback) {
setTimeout(function() {
callback()
console.log('baz');
}, 3000);
};
We want to do bob.bar().baz() and have it log "bar" and "baz" sequentially.
If you cannot modify the method calls (including passing in your callback function), how can you pass a default callback into these method calls?
Some ideas:
Wrap "bob" with decorator (still fuzzy on how to implement, could use a small example)
Modify constructor to assign default callback if none assigned (have not considered if this is possible or not)
Use a generator wrapper that will continue to call next method until none are left?
The more recommended way instead is to use promises as this is the community-wide practice to do async work.
We want to do bob.bar().baz() and have it log "bar" and "baz"
sequentially.
Why would you want to do that just to achieve this bob.bar().baz() "syntax"? You could do it pretty neatly using the Promise API w/o additional efforts to make that syntax work that indeed increases code complexity reducing the actual readability.
So, you might want to consider using the promise-based approach like this. It offers much flexibility than what you would have achieved with your approach:
Foo.prototype.bar = function () {
return new Promise(function (resolve) {
setTimeout(function () {
resolve()
console.log('bar');
}, 3000);
};
};
Foo.prototype.baz = function () {
return new Promise(function (resolve) {
setTimeout(function () {
resolve()
console.log('baz');
}, 3000);
};
};
Now you'd do this to run them sequentially one after another:
var bob = new Foo();
bob.bar().then(function() {
return bob.baz();
});
// If you're using ES2015+ you could even do:
bob.bar().then(() => bob.baz());
If you need to chain more functions you could simply do it:
bob.bar()
.then(() => bob.baz())
.then(() => bob.anotherBaz())
.then(() => bob.somethingElse());
Anyway, if you're not used to using promises you might want to read this
Warning this isn't quite right yet. Ideally we'd subclass Promise and have proper then/catch functionality but there are some caveats with subclassing bluebird Promise. The idea is to store an internal array of promise generating functions, then when a Promise is waited on (then/await) serially wait on those promises.
const Promise = require('bluebird');
class Foo {
constructor() {
this.queue = [];
}
// promise generating function simply returns called pGen
pFunc(i,pGen) {
return pGen();
}
bar() {
const _bar = () => {
return new Promise( (resolve,reject) => {
setTimeout( () => {
console.log('bar',Date.now());
resolve();
},Math.random()*1000);
})
}
this.queue.push(_bar);
return this;
}
baz() {
const _baz = () => {
return new Promise( (resolve,reject) => {
setTimeout( () => {
console.log('baz',Date.now());
resolve();
},Math.random()*1000);
})
}
this.queue.push(_baz);
return this;
}
then(func) {
return Promise.reduce(this.queue, this.pFunc, 0).then(func);
}
}
const foo = new Foo();
foo.bar().baz().then( () => {
console.log('done')
})
result:
messel#messels-MBP:~/Desktop/Dropbox/code/js/async-chain$ node index.js
bar 1492082650917
baz 1492082651511
done
If you want to avoid callback hell and keep your sanity ES6 promises are the most appropriate approach for the sake of functional programming. You just chain up your sequential asynchronous tasks in the asynchronous timeline just like working in a synchronous timeline.
In this particular case you just need to promisify your asynchronous functions. Assume that your asynch functions takes a data and a callback like asynch(data,myCallback). Let us assume that the callback is error first type.
Such as;
var myCallback = (error,result) => error ? doErrorAction(error)
: doNormalAction(result)
When your asynch function is promisified, you will actually be returned a function which takes your data and returns a promise. You are expected to apply myCallback at the then stage. The return value of myCallback will then be passed to the next stage at where you can invoke another asynch function supplied with the return value of myCallback and this goes on and on as long as you need. So let's see how we shall implement this abstract to your workflow.
function Foo(){}
function promisify(fun){
return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}
function myCallback(val) {
console.log("hey..! i've got this:",val);
return val;
}
var bob = new Foo();
Foo.prototype.bar = function land(value, callback) {
setTimeout(function() {
callback(false,value*2); // no error returned but value doubled and supplied to callback
console.log('bar');
}, 1000);
};
Foo.prototype.baz = function land(value, callback) {
setTimeout(function() {
callback(false,value*2); // no error returned but value doubled and supplied to callback
console.log('baz');
}, 1000);
};
Foo.prototype.bar = promisify(Foo.prototype.bar);
Foo.prototype.baz = promisify(Foo.prototype.baz);
bob.bar(1)
.then(myCallback)
.then(bob.baz)
.then(myCallback)