Rejected promises in Protractor/WebDriverJS - javascript

WebDriverJS and Protractor itself are entirely based on the concept of promises:
WebDriverJS (and thus, Protractor) APIs are entirely asynchronous. All
functions return promises.
WebDriverJS maintains a queue of pending promises, called the control
flow, to keep execution organized.
And, according to the definition:
A promise is an object that represents a value, or the eventual
computation of a value. Every promise starts in a pending state and
may either be successfully resolved with a value or it may be rejected
to designate an error.
The last part about the promise rejection is something I don't entirely understand and haven't dealt with in Protractor. A common pattern we've seen and written is using then() and providing a function for a successfully resolved promise:
element(by.css("#myid")).getAttribute("value").then(function (value) {
// do smth with the value
});
The Question:
Is it possible that a promise returned by any of the Protractor/WebDriverJS functions would not be successfully resolved and would be rejected? Should we actually worry about it and handle it?

I've experienced a use-case of promise rejection while using browser.wait(). Here is an example:
var EC = protractor.ExpectedConditions;
function isElementVisible() {
var el = element(by.css('#myel'));
// return promise
return browser.wait(EC.visibilityOf(el), 1000)
.then(function success() {
return true; // return if promise resolved
}, function fail() {
return false; // return if promise rejected
});
}
expect(isElementVisible()).toBe(true);
expect(isElementVisible()).toBe(false);
Here, if element is on a page, success will be executed, otherwise, if it is not found when 1 second passes, then fail will be called. My first point is that providing a callback for rejection gives an ability to be consistent with what one should expect. In this case I am kinda sure that promise will always resolve to true or false, so I can build a suite relying on it. If I do not provide a fail callback, then I'll get an Uncaught exception because of timeout, which will still fail my particular spec and still run the rest of the specs. It won't be uncaught by the way, Protractor is gonna catch it, but here I want to bring a second point, that Protractor is considered a tool which you use to write and run your code, and if an exception is caught by Protractor, then this exception has left your code unhandled and your code has a leak. But ... at the same time I do not think that one should waste time to catch everything in tests: if there is no element on a page or click has failed, then a respective spec will obviously fail too, which is fine in most of the cases. Unless you want to use the result of failure to build some code on top of it like it is in my sample.

That is the great thing about promises you are going to get a response, either an response of data or an error message. That extended to a series of promises like Webdriver uses you are going to get an array of responses or a failure response of the first one that fails. How you handle the failed response is up to you I usually just dump it into a log for the console to see what failed. The only thing you need to figure out is do you abort the rest of your tests or do you continue on.
Here is a decent article on the subject as well. http://www.toolsqa.com/selenium-webdriver/exception-handling-selenium-webdriver/
Fyi what you are doing is fine you are just never bothering to catch any of the errors though, I am not sure if that matters to you or not, you could also abstract the call in a function to auto handle the errors for you if you wanted to log them somewhere.

Related

What happens to callBack passed to .then()? [duplicate]

I observe deferring of completeness notifications in Firefox's promises. Following assertion fails, because onFullfilled() is called too late*.
var resolved = false;
function onFullfilled() {
resolved = true;
log("Completed");
}
Promise.resolve(true).then(onFullfilled);
assert(resolved, "Promise completed promise should call resolution hook immediately.");
When exactly onFullfilled() is guaranteed to be called on Promise resolution?
* In my case "Completed" log message appears after test framework report assertion failure.
Promise resolution hooks are always called after all sync code is executed. This is by design - and is done in order to prevent race conditions.
Since promises sometimes resolve asynchronously the specification mandates they are always resolved asynchronously so the same code path executes. Promises guard you against Zalgo.
It is specified here:
onFulfilled or onRejected must not be called until the execution context stack contains only platform code.
A lot of test frameworks - namely Mocha support testing promises directly with promise-syntax - by returning a promise.
it("does something", function(){
return aPromise; // if aPromise resolves the test passes
})
You should always give a function to "then". So you should use "onFullfilled()" instead of "onFullfilled" as parameter of "then".
So it should be like:
Promise.resolve(true).then(onFullfilled());

Difference of exception and unfulfilled promise?

I am reading about error handling, and the book says
If a throw occurs inside a Promises then handler function, then it is
a rejection. One way to think about exceptions and rejections is that exceptions are synchronous errors and rejections are asynchronous errors.
Questions
The book writes rejection, but shouldn't that be unfulfilled?
I were under the impression that a throw is always an exception?
And why are exceptions only for synchronous code?
An unfulfilled Promise is simply one that hasn't been fulfilled, which is quite possible even if it doesn't reject. For example:
const prom = new Promise((resolve) => {
// code here that never calls `resolve`
});
That is a Promise that will never be fulfilled - but it won't reject either.
An "unfulfilled" Promise may not necessarily hang forever like the above - it's simply a Promise that hasn't resolved yet.
a throw is always an exception?
To an extent, yes, though it behaves somewhat differently when inside a Promise - it won't cause an error event. Instead, it'll try to find a .catch handler in the Promise chain it's currently in, and if it doesn't find one, an unhandledrejection event will be fired.
And why are exceptions only for synchronous code?
That's just how the language was designed.
I think you need to understand the reason why rejections are needed, the problem it tries to solve. Consider a simple example such as this:
try {
iDoNotExist
} catch(e) {
//e ReferenceError
}
Pretty simple, the code is being evaluated and "tried" for errors. There's such an error and well, it gets caught.
Now, what if we changed it a little bit into this:
try {
setTimeout(function(){ iDoNotExist }, 1000);
} catch(e) {
//e ReferenceError
}
If you run this code, you'll notice that your javascript environment will emit an Error. It's not being caught here. Why? Because the code that throws the error is not being run when it's being "tried", only the setTimeout function which simply schedules the function and well, that function is doing it's job, it's scheduling it correctly. This in turn is the same reason why so many people get it wrong when they try accessing a value that's product of an async function. E.g.
let result;
setTimeout(() => result = true, 1000);
console.log(result) // Why is this undefined? - Famous last question
Enter promises to the rescue, which not only allow you to recover a value which you would typically return, it also lets you recover any potential error that happens on said async operations. Providing you with semantics that are similar with synchronous code (even more when paired with async functions).
So, I would say rejections are complementary to exceptions. To preserve your program flow when there's an asynchronous boundary (just like fulfilled promises do to preserve function composition).
As for what you would call unfulfilled well... there's a couple of things. unfulfilled means "not fulfilled". A rejected promise IS, technically, an unfulfilled promise. However, an unresolved promise (a promise in a pending state) is ALSO an unfulfilled promise. Technically.
Most people will think of the latter but really it could be either. Mostly because it comes from a time where "fulfill" and "resolve" where used interchangeably (which is wrong). But also because that's typically the expected result of a promise resolution.
Under no circumstance is an unresolved promise a rejected promise. An unresolved promise is in a transitory state in which it could transit into either a fulfilled promise or a rejected one.

Promises cancel method. Why it's not implemented yet as opposed to other Promises frameworks?

I'm new to promises in JavaScript but straight out the bat I've noticed that if you use a promise implementation other than the one the browser provides, there are some differences ... and the one that stands out is Promise.cancel method.
This is visible when you're using chained promises:
myPromise.then(this.view.firstMethod)
.then(this.view.secondMethod) // Fails, need to cancel the chain!
.then(this.view.thirdMethod)
.then(this.view.fourthMethod);
Using WinJS.Promise to cancel chained promises: https://jsbin.com/tuviqi/edit?js,console correctly shows in console:
0
1
Using Promise object from browser to cancel the same chained promises: https://jsbin.com/morazi/edit?js,console and shows
0
1
"Error in secondMethod."
undefined
I've also tried using catch which is ES6 sugar: https://jsbin.com/goqixal/edit?js,console - but is get:
0
1
"Error in secondMethod."
undefined <--- jsbin doesn't show this, look into console
I browsed into https://github.com/promises-aplus/cancellation-spec/issues/1 - but it's not clear to me why this is stalled.
When using Promise from browser am I suppose to chain 1, 2 and inside 2 chain 3, 4?
You're confusing "cancelling" a promise and "rejecting" a promise.
The two cases you show are different in one important regard, which is that in the second, you are providing a rejection handler on the third branch:
.then(this.view.thirdMethod, function(error) { console.warn(error); })
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Once this rejection handler executes, unless you re-throw the error, the chain goes back onto the "success" path, which causes the fourth branch to execute. However, since you return nothing from the rejection handler, the value passed to the fourth handler is undefined, which is why that is being logged to the console.
"Cancelling" promises is a separate, complex topic which is under consideration by the ES language group. One proposal is here.

Avoid forgotten promise returns

When I'm using promises to express dependencies between jobs, where the resolved value becomes unimportant, there is some danger that I might be forgetting a return somewhere. Example:
startSomething().then(function() {
Q.all(tasks.map(function(task) { return task.startIt(); }))
}).then(collectOutput).done();
Here the Q.all returns a promise, and I should have returned that. Not doing so means that by the time collectOutput gets called, all tasks have been started, but there are no guarantees that they finished.
This kind of error results in a race condition, and may be extremely hard to reproduce and track down. So I wonder, is there some tool to help detect and avoid this kind of problem? Perhaps some promise library which warns when a function along the way returns undefined? Or detects promises with no listeners, the way Bluebird does for unhandled rejections?
Actually, bluebird will warn you if you created a promise in a handler but did not return it. If you're willing to drop Q.
Here's a more in-depth explanation about bluebird's warnings
Warning: a promise was created in a handler but none were returned from it This usually means that you simply forgot a return statement
somewhere which will cause a runaway promise that is not connected to
any promise chain.
For example:
getUser().then(function(user) {
getUserData(user);
}).then(function(userData) {
// userData is undefined
});

Promise: What happens to other async request when errors are thrown?

I am using Bluebird promise. This isn't really explicit in the documentation. Suppose the following, assuming all the instances are appropriate promises:
FindSomeDBModel.then(function(model) {
return [
model.getOtherModels(),
aHTTPRequest('https://google.com')
];
}).spread(function(otherModels, httpResponse) {
// some code
}).catch(function(err) {
res.status(500).send(err);
});
If both model.getOtherModels and aHTTPRequest throws an error, what will be inside the err variable in the catch?
Also, what if model.getOtherModels throws an error first, will it cause a response to be sent out to the client or will it wait for aHTTPRequest to complete? Subsequently, aHTTPRequest throws, then what happens?
Can I respond back to the client as soon as one of the request throws an error? Because it is no longer material whether the other response completes and succeeds.
If an error occurs in one of those two promise executions, what will happen is
the error is received in your catch callback
the other promise is unaffected, its execution goes on
the callback you gave to spread isn't called
If both model.getOtherModels and aHTTPRequest throw an error, only the first one will be received by catch, the other one will just be ignored.
The callback you gave to catch is called as soon as possible, when the first error is thrown, your code doesn't wait for the execution of the other one.
If both model.getOtherModels and aHTTPRequest throws an error, what will be inside the err variable in the catch?
An AggregateError would have been optimal but because of compatibility with .all which is specified outside of Bluebird and bluebird is compatibly with - it'll resolve with the first rejection up the chain.
In its own methods that don't need ES6 compatibility bluebird will return all the errors.
Also, what if model.getOtherModels throws an error first, will it cause a response to be sent out to the client or will it wait for aHTTPRequest to complete? Subsequently, aHTTPRequest throws, then what happens?
It'll reject (and immediately enter the catch) as soon as the error is thrown, bluebird has a special case with .spread which checks if the argument passed is an array and calls .all on it - this is a special case, if you .thend it instead of .spreading it that would not have happened.
Can I respond back to the client as soon as one of the request throws an error? Because it is no longer material whether the other response completes and succeeds.
Yes, but consider using typed (or predicate) exceptions to have more meaningful error handling - if an error that's not an OperationalError happened you might want to restart the server.

Categories

Resources