Assuming I have a function which calls two dependend async functions
function targetFn() {
asyncFn1();
asyncFn2();
}
This is clearly a defect, since asyncFn2 does not wait for asyncFn1 to be completed. The correct implementation would be:
function targetFn() {
asyncFn1().then(asyncFn2);
}
However, how to test this? Maybe I test it like this:
it("ensures the ordering", function() {
spyOn(window, "asyncFn1").and.callFake(function() {
return $q.reject("irrelevant");
});
spyOn(window, "asyncFn2");
$timeout.flush();
expect(asyncFn2).not.toHaveBeenCalled();
});
This is okay for this simple test case. But what if I have several chained async functions? The test effort would grow exponential; I have to test every possible combination of rejection and resolving for every promise. What if I change a sync function so that it is an async function afterwards? I would introduce a defect behavior in every callee function without any failing tests.
What I really want is an automatic detection of unhandled promises. Going back to the first example:
function targetFn() {
asyncFn1();
asyncFn2();
}
The test system should recognizes that asyncFn1 and asyncFn2 are creating new promises, but that there are no then and catch handler. Also the second example should of course fail:
function targetFn() {
asyncFn1().then(asyncFn2);
}
Because the then introduces a new promise which is not handled, since it is not returned.
I would like to write a test like:
it("does not introduce unhandled promises", function() {
expect(targetFn).doesNotIntroduceUnhandledPromises();
});
And it checks if every promise created during the execution of targetFn has either an "endpoint" like a finally or is chained to the promises returned by the targetFn.
Do you have any idea how to achive this? Or even a better solution for this?
Thanks
Timo
This is clearly a defect, since asyncFn2 does not wait for asyncFn1 to be completed. The correct implementation would be:
No it's not, it's pretty common to want to perform two operations in parallel, or even downright ignore one's result:
function loadNewData() {
reportDataLoadedToGoogleAnalytics();
return dataService.loadNewData("someParameter");
}
Your issue though, is that your promises aren't being returned. As a rule of thumb whenever a method performs something async with promises, it should return a promise. That would effectively eliminate your problem of testing interesting promises (return them in mocha)
What I really want is an automatic detection of unhandled promises.
That's actually quite possible, you can either use a powerful library like bluebird and swap out $q and use its built in facilities or decorate $q yourself. However when I tried to do these decorations it ended up so hacky I begged satan to take my soul (had to do new Error().stack and grep it for a function name).
Related
I'm writing code in Typescript and I'm having trouble with libraries which lack synchronic functions/methods. I just need to wait for a promise to be resolved and I can't find a reasonable way of doing so. What I intend to do is just:
public someObjectMethod(): any {
externalLibrary.doSomeghing()
// wait for that something to happen
// continue doing stuff
I know I can make the method async so it returns a Promise and then declare the calling method async too so that it also returns a Promise and so on until I eventually get to a point where I can await, but if I do that I'll end up with an object model where every method is async and every object is a Promise. The thing is that I took Typescript for this expecting to be able to write OO code and not a Javascript script. I don't think using Promises, async and await will scale in a big OO model and I really don't need the extra performance I can get from asynchronous calls but rather the flow control I can get from synchronous calls.
So the question is... Is there a - reasonable - way to wait for Promises to resolve?
THIS IS NOT A DUPLICATE QUESTION!
I'm asking if there's a reasonable way of waiting for a Promise to resolve in the context of writing an object-oriented model. Changing all functions in the call trace to async and then handling Promises everywhere is not a reasonable way of solving this problem. I need to know if there's a way of just waiting for a Promise to resolve instead of changing the signature of half my code every time I come across a library which doesn't have synchronous implementations.
Once you're invoking async code - whether it uses "callback hell", Promises, async/await (syntactic sugar around Promises), or even something Monadic (is that a dirty word?) - I'm afraid you're stuck in async code.
You can convert between callback syntax and Promises, if you like.
function foo(cb: (err: any, value: string) => void): void {
doSomethingPromisey()
.then(
(value) => cb(null, value),
cb
);
}
foo((err: any, value: string) => {
if (err) {
// Do something with the error here.
// Note that throwing an error will just cause a Promise rejection - once you're in a Promise chain, there's no escaping!
throw new Error("It didn't work :(");
} else {
console.log(value);
}
});
But this isn't want you want.
I see your options as:
Fork the libraries to write synchronous versions of the code.
Accept Promises into your life & codebase.
Based on my experience, you have nothing to fear using promises in OO code. :)
I've dealt with JavaScript years ago. So I'm familiar with the term "event" as an function which is automatically executed when a certain event happens. Please correct me in case my definition is wrong.
Now I have to deal with the language again. Try to get an idea from this promise-thing which is new to me. But can't find any good definition for it.
Can anyone give an easy to understand definition of what JavaScript promises are?
More over: They seem to me very similar to events. You define a callback-function which is automatically called when a certain event happens.
What's the difference between JavaScript-events and -promises?
For the first view, they are very similar. In events, you have a callback function, and in Promise you have a callback-function. Even more, technically, you can do almost similar stuff which Promises do only with Events.
Events and Promises both useful for Async code. I will write some abstract code to try explain. For example, you have some async code, and after that, it should alert something.
function someAsyncFunction() {
setTimeout(function() {
// some long async logic here
console.log('What the hell is Promises?');
}, 2000);
}
someAsyncFunction();
Timeout here is Async, because it will run your code not in main tread and run it after some time, you not sure when exactly it happens (ofcouse here it's around 2s. less-or-more).
So now imagine, that you need to do something with result of async action. For example you have function AsyncResultCalculator and you need to run that function. What you will do? You have few choices:
Pass a callback function to async code and run your function AsyncResultCalculator when async code complete it's work
Define some Event (for example 'DoSomethingAfterAsync' event) and trigger that event after async code is finished or failed.
Use promises
All this variants as result, will do only one thing - run your function AsyncResultCalculator. So, why we have 3 different ways to do the same result? Because it's cool! It's just different techniques to do the same stuff, but they change difficulty of your code. Some ways make your code more complicated, some ways make your code larger, some ways make your code more elegant.
So, i think you know how to run callback functions or how to trigger event, but what about Promises? How to use them? Very easy. Let's go back to Async Timeout and look:
function AsyncResultCalculator(res) {
// calculate result of async action!!!
console.log(res + 1);
}
function someAsyncFunction() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
// some long async logic here
resolve(55); // or reject
}, 2000);
}
}
someAsyncFunction().then(AsyncResultCalculator);
So, what the point to use Promises here? Just because it's modern style for async actions. With promises your code will be more elegant and more easy to read for different developers. Also, promises is useful because you can build a CHAIN of function like
someAsyncFunction()
.then(function(){ ... })
.then(function(){ ... });
But, of course, in some cases, it's not bad to use callbacks or events. For example, events is more useful, if you need to catch async result in some different JS file and you don't want to uses promises or callbacks in original file with async action.
All JavaScript language is mix of events, callbacks and promises ;D Use it with wise.
Use events for DOM events or some complicated situations.
Use promises for async actions
Use callbacks for sync actions or where you don't want to use Promises
At their most basic, promises are a bit like event listeners except:
A promise can only succeed or fail once. It cannot succeed or fail twice, neither can it switch from success to failure or vice versa.
If a promise has succeeded or failed and you later add a success/failure callback, the correct callback will be called, even though the event took place earlier.
Check out this JavaScript Promises: an Introduction
Short story:
Talking about Promises/A+, what is the proper way to reject a promise - throwing an error? But if I miss the catch - my whole app will blow!
How to use promisify and what are the benefits of it (maybe you'll need to read the longer version)?
Is .then(success, fail) really an anti-pattern and should I always use .then(success).catch(error)?
Longer version, described as real life problem (would love someone to read):
I have a library that uses Bluebird (A+ Promise implementation library), to fetch data from database (talking about Sequelize). Every query returns a result, but sometimes it's empty (tried to select something, but there wasn't any). The promise goes into the result function, because there is no reason for an error (not having results is not an error). Example:
Entity.find(1).then(function(result) {
// always ending here, despite result is {}
}).catch(function(error) {
// never ends here
});
I want to wrap this and check if the result is empty (can't check this everywhere in my code). I did this:
function findFirst() {
return Entity.find(1).then(function(result) {
if (result) { // add proper checks if needed
return result; // will invoke the success function
} else {
// I WANT TO INVOKE THE ERROR, HOW?! :)
}
}).catch(function(error) {
// never ends here
});
}
findFirst().then(function(result) {
// I HAVE a result
}).catch(function(error) {
// ERROR function - there's either sql error OR there is no result!
});
If you are still with me - I hope you will understand what's up. I want somehow to fire up the error function (where "ERROR function" is). The thing is - I don't know how. I've tried these things:
this.reject('reason'); // doesn't work, this is not a promise, Sequelize related
return new Error('reason'); // calls success function, with error argument
return false; // calls success function with false argument
throw new Error('reason'); // works, but if .catch is missing => BLOW!
As you can see by my comments (and per spec), throwing an error works well. But, there's a big but - if I miss the .catch statement, my whole app blows up.
Why I don't want this? Let's say I want to increment a counter in my database. I don't care about the result - I just make HTTP request.. So I can call incrementInDB(), which has the ability to return results (even for test reasons), so there is throw new Error if it failed. But since I don't care for response, sometimes I won't add .catch statement, right? But now - if I don't (on purpose or by fault) - I end up with your node app down.
I don't find this very nice. Is there any better way to work it out, or I just have to deal with it?
A friend of mine helped me out and I used a new promise to fix things up, like this:
function findFirst() {
var deferred = new Promise.pending(); // doesnt' matter if it's Bluebird or Q, just defer
Entity.find(1).then(function(result) {
if (result) { // add proper checks if needed
deferred.resolve(result);
} else {
deferred.reject('no result');
}
}).catch(function(error) {
deferred.reject('mysql error');
);
return deferred.promise; // return a promise, no matter of framework
}
Works like a charm! But I got into this: Promise Anti Patterns - wiki article written by Petka Antonov, creator of Bluebird (A+ implementation). It's explicitly said that this is wrong.
So my second question is - is it so? If yes - why? And what's the best way?
Thanks a lot for reading this, I hope someone will spent time to answer it for me :) I should add that I didn't want to depend too much on frameworks, so Sequelize and Bluebird are just things that I ended up working with. My problem is with Promises as a global, not with this particular frameworks.
Please ask only a single question per post :-)
Is .then(success, fail) really an anti-pattern and should I always use .then(success).catch(error)?
No. They just do different things, but once you know that you can choose the appropriate one.
How to use promisify and what are the benefits of it?
I think the Bluebird Promisification docs explain this pretty well - it's used to convert a callback api to one that returns promises.
Talking about Promises/A+, what is the proper way to reject a promise - throwing an error?
Yes, throwing an error is totally fine. Inside a then callback, you can throw and it will be caught automatically, resulting to the rejection of the result promise.
You can also use return Promise.reject(new Error(…));; both will have absolutely the same effect.
A friend of mine helped me out and I used a new promise to fix things up, like this: […]
No. You really shouldn't use that. Just use then and throw or return a rejected promise in there.
But if I miss the catch statement - my whole app will blow!
No, it won't. Notice that .catch() method is not a try catch statement - the error will already be caught where your then callback was invoked. It is then only passed to the catch callback as an argument, the .catch() call is not required to capture exceptions.
And when you miss .catch(), your app won't blow. All what happens is that you have a rejected promise laying around, the rest of your app won't be affected by this. You will get an unhandled rejection event, which can be dealt with globally.
Of course, that should not happen; every promise chain (not every promise instance) should be ended with a .catch() that handles errors appropriately. But you definitely don't need .catch() in every helper function, when it returns a promise to somewhere else then errors will usually be handled by the caller.
Talking about Promises/A+, what is the proper way to reject a promise - throwing an error? But if I miss the catch - my whole app will blow!
And if you use return codes to signal errors instead of exceptions, missing the check will cause subtle bugs and erratic behavior. Forgetting to handle errors is a bug, but having your app blow up in such an unfortunate case is more often better than letting it continue in corrupted state.
Debugging forgotten error code check that is only apparent by the application behaving weirdly at some unrelated-to-the-source-of-error place is easily many orders of magnitude more expensive than debugging a forgotten catch because you have the error message, source of error and stack trace. So if you want productivity in typical application development scenario, exceptions win by a pretty huge margin.
.then(success, fail)
This is usually signal that you are using promises as glorified callbacks where the callbacks are simply passed separately. This is obviously an anti-pattern as you will not get any benefits from using promises in this way. If that's not the case, even then you are just making your code slightly less readable compared to using .catch(), similar to how method(true, true, false) is much less readable than method({option1: true, option2: true, option3: false}).
How to use promisify and what are the benefits of it (maybe you'll need to read the longer version)?
Most modules only expose a callback interface, promisify turns a callback interface automatically into a promise interface. If you are writing such code manually (using deferred or new Promise), you are wasting your time.
So my second question is - is it so? If yes - why? And what's the best way?
The best way is to chain the promise:
function findFirst() {
return Entity.find(1).tap(function(result) {
if (!result) throw new Error("no result");
});
}
It's better because it's simpler, shorter, more readable and far less error-prone way to do exactly the same thing.
Is there any way to execute callback on both results of Promise object?
For example I want to make some cleanup logic after execution of xhr request. So I need to do something like this:
var cleanUp = function() { something.here(); }
myLib.makeXhr().then(cleanUp,cleanUp);
In jquery Defered for example i can use method always():
myLib.makeXhr().always(function() { something.here(); });
Does Promise support something like this?
No, there is none. It was discussed but the spec is minimal. It doesn't include a bunch of other functionality. It's designed to interoperate well with library promises and to provide simple functionality.
Here is a correct polyfill of that proposal originally made by StefPanner.
Moreover, I disagree with the current now deleted answers adding it themselves because they're all doing it wrong (as an enumerable property - no fun). Even if we ignore what it does to the return values and error state of the returned promise. The intended way to extend native promises is by subclassing them, sadly, no browsers support this yet so we'll have to wait.
Instead of messing with native prototypes, we should use a different pattern:
openDb().then(foo).then(bar).finally(close).then(more);
Is susceptible to us forgetting to call close, even if we open it 100 times in our app, forgetting to close it even once can still be devastating. On the other hand - we can use the disposer pattern which some promise libraries provide built in ourselves:
openDb(function(db){
return foo(db).then(bar);// chain here
}).then(more);
Basically - this pattern means instead of having openDB return a promise - we have it take a function and return a promise, when the function is run, if it returns a promise we wait for that promise to resolve. It looks something like:
function openDb(withDb){
return justOpenWithoutCleanUp().
then(withDb).
then(clean, function(e){ clean(); throw e; }); // note the rethrow
}
Promise object supports 'always'.
For eg:
var oPromise= jQuery.ajax({
url:YOUR_URL
}).always(function(){
YOUR_CLEAN_UP_METHOD();
})
I have a function,
Edit1 - Updated function with real one because the previous one was simplified synchronous function and the code would have worked as correctly pointed by #AlexMA in the comments
'returnSuccessOrFailure': function () {
return driver.findElement(wd.By.css('div#button')).then(function (button) {
return button.getAttribute('class').then(function (status) {
return status;
});
});
}
In my node.js test, my assertion is failing because the assert is called before returnSuccessOrFailure finishes execution.
var value = returnSuccessOrFailure();
assert.equal(value,'success', 'Looks like something failed');
If I implement a promise in returnSuccessOrFailure and chain my assert then that works. My question is do I have to implement promises all the time for such situations to block the execution? I am new to Javascript and the async nature of it and any insight when to use promises and when not to would be useful.
you don't have to "implement a promise" in, just return the one you already have:
returnSuccessOrFailure': function () {
return driver.findElement(wd.By.css('div#button')).then(function (button) {
...
but then, yes, you do still need to put your assert in a done handler
returnSuccessOrFailure().done(function(value) {
assert.equal(value,'success', 'Looks like something failed');
}
Chaining you asserts will not only make it work but will also make for more readable code. Knowing what happens in what order can be useful when going back to refactor. Not only that but the structure of callbacks/promises also allow for easily written timer tests.
Also, since your test needs to have the current state of execution, it is more than likely that writing tests with asserts in callbacks is what you will need anyway.
My question is do I have to implement promises all the time for such situations to block the execution?
Notice that promises don't block the execution. They defer the execution of code that depends on the result, notice that you're still chaining callbacks on them.
I am new to Javascript and the async nature of it and any insight when to use promises and when not to would be useful.
Promises are useful wherever you have some code that might run asynchronously and needs to pass back an asynchronous result. Otherwise you would need to use callbacks, which are way more ugly than promises.
This is part of code contracts and representing preconditions (what holds before you execute), postconditions (what holds after you execute), and object invariants (what can not change). JavaScript does not have native support for this, but you can use third party libraries (Cerny.js, ecmaDebug, jsContract, or jscategory)
I think it depends on your coding style, is it EAFP(Easier to ask for forgiveness than permission) or LBYL(Look before you leap). Both are viable! In most compiled languages you would use LBYL. However in Python for example you would use EAFP.
Generally if you know you will fail you want to fail fast. If you like to use assertions to ensure code fails fast it is up to you.