I want my program to execute the code in the second else statement when reponse equals to 0.
However, it seems like it's not waiting for the function to complete and always executes what's in the true branch.
I'm not really familiar with async/await and would appreciate some guidance here.
async function fetchInvoice(currentValue, callback) {
let requestData = basePayload;
requestData.body = //requestbody;
let productData = await fetch(baseUrl, requestData);
let jsonData = await productData.json();
if (await jsonData.result.records.length !== 0) {
//code
} else {
return false;
}
};
if (fetchInvoice(myParameter)) {
//code
} else {
//code
}
There are (at least) two issues...
First, myFunction never returns true. For any logical condition where you want the function to return true, add a return statement:
return true;
Otherwise, if the function doesn't return anything, its result is undefined which is falsy.
Second, you forgot to await the async function:
if (await fetchInvoice(myParameter)) {
//code
} else {
//code
}
Or, if this code is executing in a context which can't use await (such as top-level code in a browser) then you can follow up the Promise with .then:
fetchInvoice(myParameter).then(x => {
if (x) {
//code
} else {
//code
}
});
As an aside, you don't need await here:
if (await jsonData.result.records.length !== 0) {
Don't just randomly sprinkle keywords around the code. await is used for a specific purpose, to await the result of a Promise. There's no Promise in this operation.
You need to actually call myFunction and await/.then. If you just call it, it will return a Promise (because it's async) which is truthy. awaiting it will give you the actual value
if (await myFunction()) {
// True
} else {
// False
}
Related
My function below has await statements inside an if else.
If i do this function, it will return [] an empty array since it isnt waiting for the if else statement to finish.
How do I wait for the if else statement to finsih?
const [imageArray, setImageArray] = useState();
const [loading, setLoading] = useState(true);
let a = 1
var array = [];
if(a === 2){
array = await getArray();
} else if (a === 3){
array = await getArray2();
}
setImageArray(array);
setLoading(false);
render(
{(!loading) ? (
......
If i do this function, it will return [] an empty array since it isnt waiting for the if else statement to finish.
No, it won't. That's the point of async/await syntax, it lets you write asynchronous code using familiar flow control structures. The async function this code appears in will return a promise, and the settlement of that promise will depend on what happens in the function's logic. Your code will wait for the promise from getArray or getArray2 to settle, or will continue immediately if a isn't 1 or 2 since there's nothing to wait for.
How do I wait for the if else statement to finsih?
You don't have to, having await in the body of the if/else blocks is sufficient. The function's logic won't progress until the promise from getArray or getArray2 is settled, or (if a isn't 1 or 2) it won't wait because there's nothing to wait for.
Here's an example:
function delay(ms, value) {
return new Promise(resolve => {
setTimeout(resolve, ms, value);
});
}
async function getArray() {
await delay(400);
return [1];
}
async function getArray2() {
await delay(400);
return [2];
}
async function example(a) {
let array;
if (a === 1) {
console.log("Calling `getArray`");
array = await getArray();
} else if (a === 2) {
console.log("Calling `getArray2`");
array = await getArray2();
} else {
console.log("Not calling anything, a is not 1 or 2");
array = [3];
}
// Notice that this doesn't happen until/unless
// `getArray` or `getArray2`'s promise is fulfilled
console.log(`array is: ${JSON.stringify(array)}`);
}
const value = Math.floor(Math.random() * 3) + 1;
console.log(`Calling \`example(${value})\`...`);
example(value)
.then(() => {
console.log("`example` function's promise was fulfilled");
})
.catch(() => {
// (No code above rejects, but...)
console.log("`example` function's promise was rejected");
});
Run the snippet multiple times to see all three conditions occur (a = 1, 2, and 3).
I have a problem with if(.isDisplayed()) else if (.isDisplayed()) else condition.
The isDisplayed() function doesn't work for this condition, it always enters in the first if block even the powderRinseStartButton is not in the DOM.
if (powderRinseStartButton != null && powderRinseStartButton.isDisplayed() && powderRinseStartButton.ispresent()) {
powderRinseStartButton.click();
} else if (brewerRinseStartButton != null && brewerRinseStartButton.isDisplayed() && brewerRinseStartButton.ispresent()) {
brewerRinseStartButton.click();
} else {
fn.click();
}
if I put the first or second piece of code, it works fine
browser.wait(() => {
return brewerRinseStartButton.isDisplayed().then(() => {
browser.wait(EC.visibilityOf(brewerRinseStartButton), delay).then(() =>
{
browser.wait(EC.elementToBeClickable(brewerRinseStartButton), delay).then(() =>
{
expect(EC.elementToBeClickable(brewerRinseStartButton)).toBeTruthy();
brewerRinseStartButton.click().then(() =>
{
browser.wait(EC.visibilityOf(maintenanceText), 240000,
'The Maintenance Text should be visible within 240s');
expect(maintenanceText.isDisplayed()).toBeTruthy();
});
});
});
});
// return powderRinseStartButton.isDisplayed().then(() =>
// {
// browser.wait(EC.visibilityOf(powderRinseStartButton), delay).then(() =>
// {
// browser.wait(EC.elementToBeClickable(powderRinseStartButton), delay).then(() =>
// {
// expect(EC.elementToBeClickable(powderRinseStartButton)).toBeTruthy();
// powderRinseStartButton.click().then(() =>
// {
// browser.wait(EC.visibilityOf(maintenanceText), 240000,
// 'The Maintenance Text should be visible within 240s');
// expect(maintenanceText.isDisplayed()).toBeTruthy();
// });
// });
// });
// });
}, 5000)
.then(() => {
console.log('return true')
return true;
}, () => {
console.log('false');
browser.wait(EC.visibilityOf(fn), delay).then(() => {
browser.wait(EC.elementToBeClickable(fn), delay).then(() => {
expect(EC.elementToBeClickable(fn)).toBeTruthy();
fn.click();
});
});
});
I want to make a condition if brewerRinseStartButton is showed than click on brewerRinseStartButton, elseif powderRinseStartButton is showed than click on powderRinseStartButton else dosomething.
I solved this problem.
The isDisplayed()
#return
A promise that will be resolved with whether this element is currently visible on the page.
Wait for all promises to be resolved, or for any to be rejected.
let failHandler = ()=>
{
browser.wait(EC.visibilityOf(fn), delay).then(() =>
{
browser.wait(EC.elementToBeClickable(fn), delay).then(() =>
{
expect(fn.isDisplayed()).toBeTruthy();
expect(EC.elementToBeClickable(fn)).toBeTruthy();
fn.click();
});
});
};
brewerRinseStartButton.isDisplayed().then(()=>
{
fnBrewer();
},()=>
{
powderRinseStartButton.isDisplayed().then(()=>
{
fnPowder();
},()=>
{
failHandler();
});
});
IsDisplayed() function returns promise, so there are two ways to handle it. One as you also attempted to use then on returning promise object and perform action afterwards.
Second one is most correct and clean way to use async/await for handling promises
async clickStartButton() {
if(powderRinseStartButton != null && await
powderRinseStartButton.isDisplayed() && await
powderRinseStartButton.ispresent())
{
await powderRinseStartButton.click();
}
else if(brewerRinseStartButton != null && await
brewerRinseStartButton.isDisplayed() && await
brewerRinseStartButton.ispresent())
{
await brewerRinseStartButton.click();
}
else
{
await fn.click();
{}
}
Note: I've used my mobile keypad for typing so please ignore the code identation
I would probably attempt to do this by using the or functionality available in xpath locators. The logic is that you provide a single locator which contains the locators required to locate both objects. If the first locator is found it will be used, if the second is found it will be used. If neither are found then you can use your ele.isDisplayed() check and run your else condition on it.
let requiredEle = element(by.xpath('//button[#class="locator1" or #class="locator2"]');
requiredEle.isDisplayed().then(function(wasDisplayed){
if(wasDisplayed){
requiredEle.click();
} else {
//Your else actions
}
})
* not tested
I would also advise using isPresent instead of isDisplayed if you can as isDisplayed throws an exception if the element is not present and I'm not exactly sure how that will behave in the above if statement.
I have tried with this code. I hope this should solve your problem. I have refactored WaitForElement code seperately so that it can be used in both the cases.
async WaitForElement(element, timeout = 30000) {
try {
await browser.wait(function () {
return element.isDisplayed()
.then((isDisplayed) => isDisplayed, () => false);
}, timeout);
await browser.wait(function () {
return element.isPresent()
.then((isPresent) => isPresent, () => false);
}, timeout);
return true;
}
catch (e) {
console.log(e.message);
return false;
}
}
async clickStartButton() {
// powderRinseStartButton = element(define locator)
//brewerRinseStartButton = element(define locator)
if (await this.WaitForElement(this.powderRinseStartButton)) {
await powderRinseStartButton.click();
} else if (await this.WaitForElement(this.brewerRinseStartButton)) {
await brewerRinseStartButton.click();
} else {
//await fn.click();
}
}
Explanation:
First element.isDisplayed() function returns Promise object so if we put it into If condition then it will always return true that's why it will always pass through first If loop.
Second point is that browser.wait understand true/false, hence we have to resolve element.isDiplayed promise so that it never through exception and we can wait until timeout.
Try the below one. This should definitely work for you.
const powderRinseStartButton= element(by.id('start_powder_rinse');
const brewerRinseStartButton = element(by.id('start_brewer_rinse');
if (await powderRinseStartButton.ispresent()) {
await powderRinseStartButton.click();
} else if (await brewerRinseStartButton.ispresent()) {
await brewerRinseStartButton.click();
} else {
await fn.click();
}
if the button takes some time to load. try adding sleep before if.
Hope it helps you. Please share the error if the approach fails.
One hack you can try for this scenario is using .then() .catch() chain of promises here instead of if else. So in then you try for clicking first element button And in catch() you do not reject or throw error but instead you click on the second element button.
So in the function you can return like below:
return brewerRinseStartButton.isDisplayed()
.then(() => brewerRinseStartButton.click())
.catch(() => {
return powderRinseStartButton.isDisplayed()
.then(() => powderRinseStartButton.click());
});
Note: I've also used my mobile keypad for typing so please ignore the code identation. Also I assumed that first you want to try clicking (if found) on brewerRinseStartButton and then you want to try clicking on powderRinseStartButton.
Here is one example function.
async function getRandomBig() {
let result;
result = await randomModule.getRandom();
if (result > 0.9 ) {
return getRandomBig();
} else {
return result;
}
}
So Obviously, I would like that execution to randomModule.getRandom() happens asynchronously. Is this example the way to go?
Also, I would like to know how to make the first call to getOutput be asynchronous.
Thank you
It's a little hard to answer your question given the info you've provided, but maybe this will help:
The async function will return a promise to the outside caller. Than means that results obtained from await will happen asynchronously. For example consider this code:
// normal synchronous function
function changeRes() {
return "after"
}
// sync function that `awaits` it's result
async function getget() {
res = await changeRes()
}
let res = "before"
// p will be a pending promise
var p = getget()
// what is res at this point?
console.log(res) // still "before" because await doesn't return until next tick
// now res is 'after'
p.then(() => console.log(res))
But be careful because the call to changeRes is not asynchronous — it is called in the current loop. Compare this with the first code. We only change the behavior of changeRes():
function changeRes() {
res = "after"
return res
}
async function getget() {
res = await changeRes()
}
let res = "before"
// p is still a pending promise
var p = getget()
// but res was changed in changeRes synchronously
console.log(res)
p.then(() => console.log(res))
EDIT based on comment:
With the recursive function, everything important is happening inside the async function, so it all should work as expected. For example, you can substitute your randomModule.getRandom with the regular Math.random() which is synchronous, but the await will make it work in the context of an async function. So this function will return a promise that resolves to a random float less than 0.25:
async function getRandomBig() {
let result;
result = await Math.random();
if (result > 0.25) {
return getRandomBig();
} else {
return result;
}
}
getRandomBig().then(console.log)
So will this even though it's truly async:
function asyncRandom(){
return new Promise(resolve => {
setTimeout(() => resolve(Math.random()), 500)
})
}
async function getRandomBig() {
let result;
result = await asyncRandom();
if (result > 0.25 ) {
console.log("too big recurse")
return getRandomBig();
} else {
return result;
}
}
getRandomBig().then(console.log)
I am using Ionic 3 with Angular.
I have multiple asyn functions:
async buildNewsItemsViaHttp(){
let result = await this.http.get()....
}
async buildNewsItemsViaLocalJSON(){
return await this.http.get()....
}
async getNewsItems(){
return await this.storage.get()...
}
I would like to run the async functions conditionally based on if they return a value or not. This means:
if getNewsItems() would return data, return it
if not, run buildNewsItemsViaHttp and see if it returns data
if not, run buildNewsItemsViaLocalJSON
Suggestion:
async getItems(){
let result = await.this.getNewsItems();
if (result) {
return result;
} else {
result = await.this.buildNewsItemsViaHttp();
if (result) {
return result;
} else {
result = await.this.buildNewsItemsViaLocalJSON();
return result;
}
}
}
Is this solution correct?
Thanks in advance
You should use sort of chaining to achieve what you want, this below is a bit naive but should work:
getNewsItems().then((result)=>{
if (result === "what you need") {
// do here what you want with the result data
} else {
buildNewsItemsViaHttp().then((result)=>{
if (result === "what you need") {
// do here what you want with the result data
} else {
buildNewsItemsViaLocalJSON.then((result)=>{
// do here what you need with result
})
}
})
}})
And of course you should also have logic to catch errors (.catch(err)=>{})
I have a conditional statement in which I need to perform one of two operations, then continue after whichever operation has resolved. So my code currently looks as follows:
if (shoud_do_thing_a) { //should_do_thing_a is just a variable that determines which function to call. it is not a promise
do_thing_a()
} else {
do_thing_b()
}
// more code
The issue is that both do_thing_a and do_thing_b return promises, and I can't move on until whichever gets executed has resolved. The best way I've come up with to solve this is like this:
var more_code = function () {
// more code
}
if (shoud_do_thing_a) {
do_thing_a().then(more_code)
} else {
do_thing_b().then(more_code)
}
I don't like this structure. It's difficult to follow because you need to jump around to find where more_code is defined (imagine I have this type of control flow in several locations), rather than simply being able to continue reading.
Is there a better way to deal with this type of thing in javascript?
If you can use async/await
async function someFunc() {
var more_code = function () {
// more code
}
if (shoud_do_thing_a) {
await do_thing_a()
} else {
await do_thing_b()
}
more_code()
}
Or if you can't, use then():
var more_code = function () {
// more code
}
var do_thing;
if (shoud_do_thing_a) {
do_thing = do_thing_a()
} else {
do_thing = do_thing_b()
}
do_thing.then(more_code)
If you're stuck with raw Promises and can't use async/await (You usually should have no trouble, what with babel/typescript etc), the following is a bit more elegant than storing the promise in a variable:
function something() {
return Promise.resolve()
.then(() => {
if (should_do_thing_a) {
return do_thing_a();
}
else if (should_do_thing_b) {
return do_thing_b();
}
})
.then(some_more_code);
}
Note that when you start working with Promises, your functions should always return a Promise that other functions can work with. Leaving an asynchronous action without any way to handle it means bad things, especially when it comes to error handling.
In a more general sense, it means that when you use Promises, more of your code is "uplifted" into being executed and returned as Promises.
How I want to improve on other answers:
keep it clean and simple
no unneeded variables
return promise asap
in js we use camelCase
put it in a function and name that function to keep it readable
let then execute moreCode so it's called after the thing is done.
function doTheThing () {
if (shouldDoA) return doThingA()
else return doThingB()
}
doTheThing().then(moreCode)
Simple working example:
The scope it's defined in must be async.
const createUser = async (data) => {
if (await isUsernameTaken(username)) { return 'username-taken' }
}
The isUsernameTaken func:
const isUsernameTaken = async (username) => {
const request = await API.SomeRequest
return !request.isEmpty
}
Save the promise and add the then after the if statement:
var promise;
if (shoud_do_thing_a) {
promise = do_thing_a();
} else {
promise = do_thing_b();
}
promise.then(more_code);
var promise = shoud_do_thing_a? do_thing_a: do_thing_b
promise().then(function () {
// more code
})
Similar to other answers here, but you can self execute the async and clean up the condition a bit.
(async () => {
const should_do_thing_a = true
const do_thing_a = function() {
return new Promise(function(resolve, reject) {
resolve('a')
})
}
const do_thing_b = function() {
return new Promise(function(resolve, reject) {
resolve('b')
})
}
const result = (should_do_thing_a) ? await do_thing_a() : await do_thing_b()
console.log(result)
})()
The way I would do it would be to put the if check into another function that returns a promise. The promise gets resolved with the resolve of the other function calls in the if-else statement.
Example:
function performCheck(condition) {
var defer = $q.defer();
if (condition) {
doThingA().then(function(response) {
defer.resolve(response);
});
} else {
doThingB().then(function(response) {
defer.resolve(response)
});
}
return defer.promise;
}
performCheck(condition).then(function(response) {
//Do more code.
});
In my opinion, I would prefer this method because this function can now be used in multiple places where you have a check on the condition, reducing code duplication, and it is easier to follow.
You could reduce this down further with
function performCheck(condition) {
var defer = $q.defer();
var doThisThing = condition ? doThingA : doThingB;
doThisThing().then(function (response) {
defer.resolve(response);
});
return defer.promise;
}
performCheck(condition).then(function(response) {
//Do more code.
});
You can use async/await
async function fn() {
let p, result;
if (shoud_do_thing_a) {
p = await do_thing_a()
} else {
p = await do_thing_b()
}
if (p) {
result = more_code();
}
return result
}
more_code = miFunc() => return new Promise((resolve, reject) => ... });
Solution 1
const waitFor = should_do_thing_a ? do_thing_a() : do_thing_b();
waitFor.then(...).catch(...)
Solution 2
let waitFor = Promise.resolve();
if (do_thing_a) {
waitFor = do_thing_a();
} else {
waitFor = do_thing_b();
}
waitFor.then(...).catch(...);