Selenium with async/await in JS, find and click on element - javascript

I'm trying to refactor my tests using Selenium webdriver and Mocha to ES7 with async/await functionality. I have got following piece of code:
await loginPage.loginAsAdmin()
/* THIS DOES NOT WORK */
//await layout.Elements.routePageButton().click()
/* THIS DOES WORK */
let a = await layout.Elements.routePageButton()
await a.click()
I don't understand why the particular does not work - I get:
TypeError: layout.Elements.routePageButton(...).click is not a function
Function before click method returns webElement, as you can see:
Layout:
routePageButton: async () => await findVisibleElement('#route_info a')
const findVisibleElement = utils.Methods.Element.findVisible
Method:
findVisible: async (cssSelector) => {
let elm = await driver().findElement(by.css(cssSelector))
return elm
}

The problem here is misunderstanding that await is a language keyword in ES2017 that allows you to block execution of the calling async function until a Promise returned by an invoked function resolves.
routePageButton() returns a Promise, and this is why the second syntax above works, as execution is blocked until the Promise resolves to a WebElement object.
However in the syntax you are using in the first example, the function that it is attempting to await on (click()) is never called, because a Promise does not have a click() function. Note that you have two awaits in your second syntax, but only one in your first.
To do what you are attempting to do in one line, you would have to do something like:
await (await layout.Elements.routePageButton()).click()

Related

Waiting for another async function to finish before running

I have two functions that I want to run one after the other finishes. I'm using webdriver.IO so I need to wait for one function to log into the page, and then I want another function to run.
Other questions here on SOF are telling me to use a promise, however I need to use a promise, however when I wrap my functions in a promise I get the error SyntaxError: await is only valid in async function.
I have a login function
const Login = async () => {
const browser = await remote({
capabilities: {
browserName: 'chrome'
}
})
const userName = await browser.$('#username')
const password = await browser.$('#password')
await userName.setValue(process.env.NAMEUSERNAME)
await password.setValue(process.env.PASSWORD)
const loginButton = await browser.$('button[type="submit"]')
await loginButton.click()
}
Once this completes and the page loads I want to run another function.
I have gotten this to work with a setTimeout however I don't want to just have a chain of setTimeout in my application
It's hard to give you precise code without seeing where this is called from: but presuming it's inside some other function, you just have to mark that one async and use await inside it:
async theOuterFunction() {
await Login();
doTheOtherThing();
}
(If doTheOtherThing also returns a promise you could await that too - and would need to if you want to run other code inside theOuterFunction after that. But there's no need if it's the last statement inside the function.)

Difference between JavaScript and Python Promise / Awaitable Evaluation

I've been trying to learn the JS concurrency model given a background in Python's.
Running the following in Python:
async def myFunction():
print("abc")
myFunction()
will print nothing. Running the following in JavaScript:
async function myFunction() {
console.log("abc")
}
myFunction()
will print "abc". In both cases I did not await myFunction, yet the evaluations were different.
I understand why the Python program prints nothing. myFunction is a coroutine function that resolves to nothing, more specifically it returns an Awaitable[None]. But to actually have this awaitable's side effect executed, I must await it.
I have also read Are promises lazily evaluated? with an answer of no, talking about how the eager evaluation of promises is guaranteed.
Even though I've looked at both concurrency models separately, their difference is still confusing. While general clarity about the contrast here would be very helpful, I also do have a specific question: Is there ever a point to awaiting a Promise in JavaScript that resolves to nothing, and should only execute for its side effect? In other words, do await myFunction() and myFunction() possibly have a difference in behavior in a different case, even though they both gave the same output here?
async def myFunction():
print("abc")
awaitable = myFunction()
In Python, myFunction() returns an awaitable. The code hasn't run yet. It only runs when you execute a method (.send()) on the awaitable:
awaitable.send(None)
You typically don't use .send(), these mechanics are hidden. You will just use await myFunction() or asyncio.get_event_loop().run_until_complete(myFunction()), and this will execute the function's code behind the scenes.
In JavaScript, an async function returns a promise. The MDN JS reference says:
Promises in JavaScript represent processes that are already happening
So when you call the async function in JavaScript, the function's code starts running, and at some point a promise is returned. Here's what the MDN JS reference says about it:
The body of an async function can be thought of as being split by zero or more await expressions. Top-level code, up to and including the first await expression (if there is one), is run synchronously. In this way, an async function without an await expression will run synchronously. If there is an await expression inside the function body, however, the async function will always complete asynchronously.
Given all this, I don't see why you would await an async JS function if you only wanted its side effect (unless the caller needs the side effect to have occurred before proceeding).
Inside an async function all the asynchronous operations are done by using an await in front of it.
If there is an await in front of a promise, then the we wait until the async operation is completed and then continue the remaining code execution.
If we don't use await infront of an async operation, we don't resolve the promise and continue.
Await in front of a synchronous operation has no effect.
// Case I
const test = async() => {
let res = fetch("http://localhost:3000");
console.log(res);
console.log("It is working");
// We dont wait for the http request to complete,
// We print res as a <<promise>>
// and continue to the next line and print "It is working".
}
test();
// Case II
const test = async() => {
let res = await fetch("http://localhost:3000");
console.log(res);
console.log("It is working");
// We wait for the http request completion.
// After that we print the result.
// Then print ===> It is working
}
test();
()=> {} // stands for an anonymous function also know as Fat arrow function.
You use await in front of a function only if it returns a promise.
If it returns a promise, we wait for the promise to resolve and continue code execution.
If it doesn't returns a promise, default behavior is expected.
const test = () => {
return fetch("http://localhost:3000");
}
let testingAsynFunc = async() => {
let a = await test();
//Here we wait until the promise is resolved and then continue the code execution.
console.log(a);
console.log("working");
}
testingAsyncFunc()

What is the difference between using and omitting keyword "await" in these two code blocks?

I'm following along an MDN article on async/await, and I understand the purpose of using the async keyword before functions, but I'm a little more confused about the await keyword. I've read up on on "await" as a result, and I get the general concept, but I'm still unsure when it comes to examples. For instance, here is a trivial piece of code (as shown in the MDN article) using async/await.
async function hello() {
return greeting = await Promise.resolve("Hello");
};
hello().then(value => console.log(value));
As you might expect, this logs "Hello" to the console. But even if we omit "await", it has the exact same output.
async function hello() {
return greeting = Promise.resolve("Hello"); // without await
};
hello().then(value => console.log(value));
Can someone help me understand exactly what the await keyword before Promise.resolve is doing? Why is the output the same even if it's omitted? Thanks.
In up-to-date JavaScript engines, there's no difference, but only because the return await somePromise is at the top level of the function code. If it were within a control block (for instance, try/catch), it would matter.
This is a recent-ish change. Until ES2020 the return await version would have introduced an extra async "tick" into the process. But that was optimized out in a "normative change" (rather than proposal) in ES2020 when the promise being awaited is a native promise. (For a non-native thenable, there's still a small difference.)
For the avoidance of doubt, there's an important difference between:
async function example() {
try {
return await somethingReturningAPromise();
} catch (e) {
// ...do something with the error/rejection...
}
}
and
async function example() {
try {
return somethingReturningAPromise();
} catch (e) {
// ...do something with the error/rejection...
}
}
In the former, a rejection of the promise from somethingReturningAPromise() goes to the catch block, because it's been awaited. In the latter, it doesn't, because you've just returned the promise, which resolves the promise from the async function to the promise somethingReturningAPromise() returns, without subjecting it to the catch.
But in your example, where it's not in any control structure, the await is largely a matter of style at this point.
Keep in mind that async/await is just a fancy API for working with promises.
An async function always and implicitly returns a promise (as soon as the first await or return statement is reached) that will be resolved when the function code itself finally reaches its end. The resolved promise value will be equal to the function's returned value.
async allows you to use await inside of the asynchronous scope. Await only makes sense when preceding a promise (could be another async function call, a promise created in place with new Promise(), etc.). Using await before non promise values will do no harm though.
await indicates, just that, wait. It tells the engine to 'stop' code execution on that scope and to resume it whenever the promise is fulfilled. await will then give you or 'return' the promise resolved value.
Just to give you some practical examples:
Using your first code:
async function hello() {
const greeting = await Promise.resolve("Hello"); // HERE I CAN USE await BECAUSE I'M INSIDE AN async SCOPE.
console.log('I will be printed after above promise is resolved.');
return greeting;
};
hello().then(value => console.log(value));
Using your second code, async could be completely removed:
function hello() {
return Promise.resolve("Hello");
};
hello().then(value => console.log(value));
Please let me know if there is any doubt.

Angular & async / await

I am using the following in my angular app:
let getName = greenlet( async username => {
let url = `https://api.github.com/users/${username}`
let res = await fetch(url)
let profile = await res.json()
return profile.name
})
console.log(await getName('developit'));
Angular seems to be changing this 'await' into 'yield':
How do i use this within my Angular5 application? Thanks.
Babel changes async/await in the browser into generator functions, which utilize the yield keyword. Yields only work when they are in the correct context; specifically the next stage will be ensuring that the yield appears inside a generator function (you can read more about generators, but basically they are identified by a * in the function signature, e.g. function *doSomething().
A generator function will not get created if you have not correctly managed your async keywords. Babel will convert that await into a generator, but because the outer function is not async, it won't create the generator wrapper, thus the error. Some IDEs will report this error before you compile, but that's what the error you are seeing means.
You mentioned that this console.log is sitting inside the ngOnInit lifecycle hook; that function call is not by itself asynchronous. You can decorate it with async, but I wouldn't recommend that (it works, but what does it actually mean?). Instead you can try calling an explicitly async function and just ignore the returned promise:
ngOnInit() {
this.someFunction();
}
async someFunction() {
console.log(await getName('developit'));
}
We'll assume for now that greenlet knows what to do with the async function it has been handed (does it resolve the promise?)

Is it legitimate to omit the 'await' in some cases?

I am using async/await in several places in my code.
For example, if I have this function:
async function func(x) {
...
return y;
}
Then I always call it as follows:
async function func2(x) {
let y = await func(x);
...
}
I have noticed that in some cases, I can omit the await and the program will still run correctly, so I cannot quite figure out when I must use await and when I can drop it.
I have concluded that it is "legitimate" to drop the await only directly within a return statement.
For example:
async function func2(x) {
...
return func(x); // instead of return await func(x);
}
Is this conclusion correct, or else, what am I missing here?
EDIT:
A small (but important) notion that has not been mentioned in any of the answers below, which I have just encountered and realized:
It is NOT "legitimate" to drop the await within a return statement, if the called function may throw an exception, and that statement is therefore executed inside a try block.
For example, removing the await in the code below is "dangerous":
async function func1() {
try {
return await func2();
}
catch (error) {
return something_else;
}
}
The reason is that the try block completes without an exception, and the Promise object returns "normally". In any function which calls the outer function, however, when this Promise object is "executed", the actual error will occur and an exception will be thrown. This exception will be handled successfully in the outer function only if await is used. Otherwise, that responsibility goes up, where an additional try/catch clause will be required.
If func is an async function then calling it with and without await has different effects.
async function func(x) {
return x;
}
let y = await func(1); // 1
let z = func(1) // Promise (resolves to 1)
It is always legitimate to omit the await keyword, but means you will have to handle the promises in the traditional style instead (defeating the point of async/await in the first place).
func(1).then(z => /* use z here */)
If your return statements use await then you can be sure that if it throws an error it can be caught inside your function, rather than by the code that calls it.
await just lets you to treat promises as values, when used inside an async function.
On the other hand, async works quite the opposite, it tags the function to return a promise, even if it happens to return a real, synchronous value (which sounds quite strange for an async function... but happens often when you have a function that either return a value or a promise based on conditions).
So:
I have concluded that it is "legitimate" to drop the await only directly within a return statement.
In the last return statement of an async function, you just are returning a Promise, either you are return actually a directly a promise, a real value, or a Promise-as-value with the await keyword.
So, is pretty redundant to use await in the return statement: you're using await to cast the promise to a value -in the context of that async execution-, but then the async tag of the function will treat that value as a promise.
So yes, is always safe to drop await in the last return statement.
PS: actually, await expects any thenable, i.e. an object that has a then property: it doesn't need a fully spec compliant Promise to work, afaik.
PS2: of course, you can always drop await keyword when invoking synchronous functions: it isn't needed at all.
An async function always returns a Promise.
So please keep in mind that these writing of an async function are all the same:
// tedious, sometimes necessary
async function foo() {
return new Promise((resolve) => resolve(1)))
}
// shorter
async function foo() {
return Promise.resolve(1)
}
// very concise but calling `foo` still returns a promise
async function foo() {
return 1 // yes this is still a promise
}
You call all of them via foo().then(console.log) to print 1. Or you could call them from another async function via await foo(), yet it is not always necessary to await the promise right away.
As pointed out by other answers, await resolves the promise to the actual return value statement on success (or will throw an exception on fail), whereas without await you get back only a pending promise instance that either might succeed or fail in the future.
Another use case of omitting (i.e.: being careful about its usage) await is that you might most likely want to parallelize tasks when writing async code. await can hinder you here.
Compare these two examples within the scope of an async function:
async function func() {
const foo = await tediousLongProcess("foo") // wait until promise is resolved
const bar = await tediousLongProcess("bar") // wait until promise is resolved
return Promise.resolve([foo, bar]) // Now the Promise of `func` is marked as a success. Keep in mind that `Promise.resolve` is not necessary, `return [foo, bar]` suffices. And also keep in mind that an async function *always* returns a Promise.
}
with:
async function func() {
promises = [tediousLongProcess("foo"), tediousLongProcess("bar")]
return Promise.all(promises) // returns a promise on success you have its values in order
}
The first will take significantly longer than the last one, as each await as the name implies will stop the execution until you resolve the first promise, then the next one.
In the second example, the Promise.all the promises will be pending at the same time and resolve whatever order, the result will then be ordered once all the promises have been resolved.
(The Bluebird promise library also provides a nice Bluebird.map function where you can define the concurrency as Promise.all might cripple your system.)
I only use await when want to work on the actual values. If I want just a promise, there is no need to await its values, and in some cases it may actually harm your code's performance.
I got a good answer above, here is just another explanation which has occurred to me.
Suppose I have this:
async function func(x) {
...
return y;
}
async function func2(x) {
...
return await func(x);
}
async function func3(x) {
let y = await func2(x);
...
}
The reason why I can safely remove the await in the return statement on func2, is that I already have an await when I call func2 in func3.
So essentially, in func3 above I have something like await await func(x).
Of course, there is no harm in that, so it's probably better to keep the await in order to ensure desired operation.

Categories