if I have something like:
var x = await retrieveData()
if (x!= nil){
do stuff
}
where retrieveData() does an http request.
The question is: Does the if condition wait for the retrieving data or not? (In a better way, does the if condition always return false or not?)
Yes, the if condition "waits".
The code only continues to execute after the Future returned from retrieveData completed.
Without async/await it would be
return retrieveData().then((x) {
if(x!= null) {
do stuff
}
})
Related
So I think this is probably me mixing up sync/async code (Mainly because Cypress has told me so) but I have a function within a page object within Cypress that is searching for customer data. I need to use this data later on in my test case to confirm the values.
Here is my function:
searchCustomer(searchText: string) {
this.customerInput.type(searchText)
this.searchButton.click()
cy.wait('#{AliasedCustomerRequest}').then(intercept => {
const data = intercept.response.body.data
console.log('Response Data: \n')
console.log(data)
if (data.length > 0) {
{Click some drop downdowns }
return data < ----I think here is the problem
} else {
{Do other stuff }
}
})
}
and in my test case itself:
let customerData = searchAndSelectCustomerIfExist('Joe Schmoe')
//Do some stuff with customerData (Probably fill in some form fields and confirm values)
So You can see what I am trying to do, if we search and find a customer I need to store that data for my test case (so I can then run some cy.validate commands and check if the values exist/etc....)
Cypress basically told me I was wrong via the error message:
cy.then() failed because you are mixing up async and sync code.
In your callback function you invoked 1 or more cy commands but then
returned a synchronous value.
Cypress commands are asynchronous and it doesn't make sense to queue
cy commands and yet return a synchronous value.
You likely forgot to properly chain the cy commands using another
cy.then().
So obviously I am mixing up async/sync code. But since the return was within the .then() I was thinking this would work. But I assume in my test case that doesn't work since the commands run synchronously I assume?
Since you have Cypress commands inside the function, you need to return the chain and use .then() on the returned value.
Also you need to return something from the else branch that's not going to break the code that uses the method, e.g an empty array.
searchCustomer(searchText: string): Chainable<any[]> {
this.customerInput.type(searchText)
this.searchButton.click()
return cy.wait('#{AliasedCustomerRequest}').then(intercept => {
const data = intercept.response.body.data
console.log('Response Data: \n')
console.log(data)
if (data.length) {
{Click some drop downdowns }
return data
} else {
{Do other stuff }
return []
}
})
}
// using
searchCustomer('my-customer').then((data: any[]) => {
if (data.length) {
}
})
Finally "Click some drop downdowns" is asynchronous code, and you may get headaches calling that inside the search.
It would be better to do those actions after the result is passed back. That also makes your code cleaner (easier to understand) since searchCustomer() does only that, has no side effects.
you just need to add return before the cy.wait
here's a bare-bones example
it("test", () => {
function searchCustomer() {
return cy.wait(100).then(intercept => {
const data = {text: "my data"}
return data
})
}
const myCustomer = searchCustomer()
myCustomer.should("have.key", "text")
myCustomer.its("text").should("eq", "my data")
});
I'm dipping my toe into the waters of Axios and async/await at the same time, and am trying to understand something about the control flow. Is the following legitimate?
let loading=true;
(async() => {
let response = null;
try {
response = await axios.get('https://whatever.com/api');
} finally {
loading=false;
}
if(response){
//do something with response here
}
})();
That is, can I count on the request to have returned at the point I am accessing the response variable? I appreciate I could guarantee it is by moving it into the 'try' immediately after the axios get, but then I would have to have the loading=false line before it, as well as in 'finally' (or 'catch'). I need to ensure that loading is set to false before any further actions, whether the request succeeds or fails, and I don't want to repeat myself. Maybe there's a better way of doing this?
Edit
Now that you have changed the question, the previous solution will not be working correctly. The issue is that the code inside the IIFE will be executed after everything else is finished, so loading will never be set to false from the perspective of the outside code. (the other code will be executed, and thеn the IIFE. That's because of the event loop). Your best bet is to make the outside code async and await the axios promise.
If you provide the problem details I might be able to help you refactor it.
Previous answer
I need to ensure that loading is set to false before any further actions
Any code after the await is guaranteed to NOT be loading:
(async() => {
let response = await axios.get('https://whatever.com/api');
// the request is finished, the await guarantees that
})();
If you need error handling, you can wrap it in a try/catch:
(async() => {
try {
let response = await axios.get('https://whatever.com/api');
// definitely not loading
}
catch (e) {
// definitely not loading, but an error occurred
}
})();
In my angular application, I have a scenario where i need to make looping over ajax calls. Scenario is as follows:
Based upon response from first request i need to initiate 2nd request, response from 2nd request would trigger 3rd request and so on. Wherever response does not meet certain criteria, loop needs to be broken.
Loop can go several times 10 or 20 based upon configured value.
Just want to make it synchronous. Anyone who can suggest approach to implement it ?
someList.forEach(async (value,index,arr)=> {
if(!isPrintingError)
{
let out = await this.someService.Print(someBuffer);
if(!out)
{
isPrintingError = true;
}
else {
console.log("Print successful");
}
}
}
Just have a look at Promises or async/await.
I'm not sure about how you want to do your ajax calls, and it would be great to have a small chunk of code.
But the idea is to do something like that
try {
const response1 = await this.apiCall1();
if (!response1) {
throw new Error('error1');
}
const response2 = await this.apiCall2();
if (!response2) {
throw new Error('error2');
}
// etc...
} catch (e) {
// logic in case of error
}
Also you can do it in a loop. But in order to give better help, i'll need some code
Try using RxJS Library, it will help you also in other different async stuff issues.
Using RxJS operators I'd take advantage of the Merge Operator.
More info here: RxJS Merge Operator
Thanks for your snippet. Here is how to break the loop in case of bad output
try {
someList.forEach(async (value, index, arr) => {
let output = await this.someService.Print(someBuffer);
if(!output) {
// break the loop
throw new Error('error');
} else {
console.log("Print successful");
}
}
} catch (e) {
// what to do if failed ?
}
Currently I use promises in the store actions but want to convert it into async/await. This is an example of the store action with promises:
fetchActiveWorkspace (context, workspaceID) {
if (workspaceID) {
return this.$axios.get(`#api-v01/workspaces/workspace/${workspaceID}`)
.then(response => {
context.commit('setActiveWorkspace', response.data)
})
.catch(err => {
throw err
})
} else {
return Promise.resolve(true)
}
},
This fetchActiveWorkspace action is resolved in components because it returns promise. How can I convert this code snippet into a async/await structure and use it in components?
This is how I would try to translate it; take into account that as I have no access to the original code in full context, I cannot try it first-hand to make sure it works; but still, this is how you can use async/await with promises.
// 1. Mark the function as `async` (otherwise you cannot use `await` inside of it)
async fetchActiveWorkspace(context, workspaceID) {
if (workspaceID) {
// 2. Call the promise-returning function with `await` to wait for result before moving on.
// Capture the response in a varible (it used to go as argument for `then`)
let response = await this.$axios.get(`#api-v01/workspaces/workspace/${workspaceID}`);
context.commit('setActiveWorkspace', response.data);
}
// 3. I don't think this is necessary, as actions are not meant to return values and instead should be for asynchronous mutations.
else {
return true;
}
}
You can surround the function's body with try/catch in case you want to capture and handle exceptions. I didn't add it in order to keep things simple and because your promise-based code will just capture and re-throw the exception, without doing anything else.
I would like to write one nice, tidy "login" function that I use to log in to a bluetooth peripheral by sending it a password.
public async Task<bool> LogIn()
{
bool result = false;
//First log in
var connectHook = Device.ConnectHook(BleService.Control, new Guid[] { BleCharacteristic.PasswordResult });
connectHook.Subscribe(ScanResult =>
{
ScanResult.Characteristic.DisableNotifications();
//Succesful Login
if (ScanResult.Data[0] == 1)
{
result = true;
}
//Failed Login
else
{
result = false;
}
});
await Device.WriteCharacteristic(BleService.Control, BleCharacteristic.Password, PasswordBytes);
await connectHook.FirstAsync();
return result;
}
I basically want to subscribe to the password result characteristic, then write the password, and then wait for the password result before returning from the function.
Is this the place to use a promise? I have read about them but do not understand their use.
Thanks
EDIT:
Did some digging and think I've found a good answer. I've updated the code above to reflect it, let me know what you think.
EDIT 2:
So, the code above doesn't work because nothing is returned from
await connectHook.FirstAsync();
If I comment that line out it all works....but it seems like it's working because I have a race condition and the notification gets back before the other code completes.
Does the function magically await for that notification? I thought the observable implemented IEnumerable and would return the first item as long as it had come back? I'm new to this reactive stuff so please be kind.
In you code:
The method connectHook.Subscribe is a asynchronous method. Before you added the two await methods, the function login will directly return false without wait the callback in connectHook.Subscribe,so the return value in login will not be changed whether you login successfully or failed to login.
After you add the two await methods,I guess the callback in connectHook.Subscribe has been triggered after these two methods finished running. So the return value will be changed with the return value in the callback.