I'm attempting to use async/await with axios.then() within a for of loop. The function fails to run or even attempt the axios call. I have a feeling using the then() function is part of the problem.
I need the for loop to wait until the then() function has run before continuing on to the next array item. Any ideas how I can alter my code to get the axios.then() functions to run asynchronously?
accounts = [{a},{b},{c}, ...] // Example of accounts array
async function get_user_data (accounts) {
// For of loop
for (let [index, user] of accounts.entries()) {
// Currently using await before axios call
await axios.get(url, headers)
.then(function(data) {
console.log(data)
})
.catch(function(error) {
console.log(error)
})
}
}
UPDATE:
Issue was ultimately being caused by the Vue frontend compiling my application. Resolved by following the stack overflow solution posted here. Note, that the code now functions as expected. The solution offered by Dacre Denny helped me decide the issue must be located somewhere else as his should have worked but did not until the problem with Vue was solved. Takeaways:
Use simple tests to confirm issue not with code
Check webpack, babel, and other compiler configs if above doesn't work
In general, it is considered an anti-pattern to mix the promise interface (ie .then()) with await/async semantics.
Seeing that the get_user_data function is defined async, consider a revised implementation based on a try/catch block for clearer program flow and greater predictability in the asynchronous behaviour of your loop:
async function get_user_data (accounts) {
for (let [index, user] of accounts.entries()) {
/* try/catch block allows async errors/exceptions to be caught
without then need for a .catch() handler */
try {
/* Using await, the response data can be obtained in this
way without the need for a .then() handler */
const data = await axios.get(url, headers)
console.log(data);
}
catch(error) {
/* If an error occurs in the async axios request an exception
is thrown and can be caught/handled here */
console.log(error)
}
}
}
Thanks to Dacre Denny for the quick help. His test code helped me determine the problem was not with the code.
The Issue was ultimately being caused by the Vue frontend compiling my application which does not at this date support async/await out of the box. Resolved by following the stack overflow solution posted here. Note, that the code now functions as expected. Takeways:
Use simple tests to confirm issue not with code
Check webpack, babel, or other compiler configs if above doesn't
work
A lack of errors when function fails to run may indicate a compilation error. Check configs.
async get_user_data(accounts) {
// For of loop
(async() => {
for (let [index, user] of accounts.entries()) {
// Currently using await before axios call
await axios.get(url, headers)
.then(function(data) {
console.log(data)
})
.catch(function(error) {
console.log(error)
})
}
})();
},
Related
I'm trying to create a discord bot with the help of node.js
In which I need to call a function that may return discord API errors I handle them like this
interaction.user.send("Hi")
.catch(() => {return interaction.reply("...");
console.log("shouldnt run if an error accured")
However whenever that API error accurse the return statement unlike normally does not stop the code execution.
How do I stop the console.log statement in this code from executing when the exception accurse ?
the js is asynchronous so it puts the request of API in execution queue(not wait for response from api) and continue its execution that's why your console statement is running even if the error occurs.
interaction.user.send("Hi")
.then(() => {
// do whatever you want when it succeed
})
.catch((error) => {
// handle error
});
you can also checkout async await for the same.
As #ParthPatel indicates, interation.user.send() is returning a Promise which may not be rejected immediately upon error. Statements such as your console.log() will run before an error has a chance to occur and be caught.
However, these days there is the async await syntax you can use, which may help you simplify your code a bit depending on what you're trying to do.
try {
await interaction.user.send("Hi");
console.log('Shouldn\'t run if error occurred.');
} catch(e) {
interaction.reply('...');
}
Note that you can only use await inside of an async function, declared like this:
async function doSomething() {
// Do something here...
}
You can find more information here: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await
I've been using #zzzzBov's fantastic loadScript() function to load JS libraries using Promises.
However, it becomes tedious and messy to chain together Promises for all of the loaded libraries. I'd like to switch to an async/await solution, but—since loadScript() function doesn't return the usable library objects I need—this isn't as straightforward as using .then() at a first glance.
The function returns a load Event, which I await. So my initial approach would be to just define some useless constants as the Promise returns, and then reference them in an if statement:
let loadedGlobalObject: any;
const lib1Loaded: Event = await loadScript(lib1URL);
const lib2Loaded: Event = await loadScript(lib2URL);
if (lib1Loaded && lib2Loaded) { // Makes this block dependent upon the await statements
loadedGlobalObject = (window as any).globalObject;
console.log(loadedGlobalObject);
}
Is this correct form? I don't know how to "invoke" the await statements; an if statement was my first guess. So I'm wondering if this is the best solution, or if there is a more efficient/accepted technique (I know that I could substitute the libXLoaded constants into the if statement, but I'm going for readability).
If I understand what you are trying to do - you want to await the loading of your libraries, and access the global objects only once they are loaded. What about something like this:
try {
async function loadLibs () {
const lib1Event: Event = await loadScript(lib1Url);
let loadedGlobalObject: any;
loadedGlobalObject = (window as any).globalObject;
console.log(loadedGlobalObject);
}
loadLibs();
catch (err) {
console.log('Error loading script: ', err);
}
The loadScript() function rejects if there was an error while loading the script, so you can wrap your code in a try...catch. Otherwise, it resolves and you can access it's global object.
Note that you can only call await within a function marked as async.
So it turns out I didn't understand async/await at all. I had thought that the awaited constants were reactive, and lines referencing them were run only after their Promises resolved; instead, it's just the rest of the function that waits.
For error-handling, as #dwosk answered, you can use a try-catch, although I wasn't concerned about this. As #Rezaa91 commented, I could have used an await Promise.all([]), except for that I'm loading dependencies in order. And, now that I understand await statements better and as #dwosk and #Bergi noted, I don't need to define those constants.
In summary, my modified code (with Promise.all() added for fun) looks like this:
let loadedGlobalObject: any;
await Promise.all([loadScript(dependency1), loadScript(dependency2)]);
await loadScript(library1);
// Will have been loaded by now.
loadedGlobalObject = (window as any).globalObject;
console.log(loadedGlobalObject);
I'm trying to create saving feature for my little game (Javascript, Axios, Express, NodeJS MongoDB). Problem is that I don't really understand axios promises and async/await features or more accurately I don't know how to implement them into my code. I want to get data from my mongodb and use it in variable/method so I can change stats of player etc. later as needed. I have been reading all possible guides and similiar posts, but I have no idea why they don't work for me. Any help would be appreciated!
After messing around and trying everything I found on web, here's currently part of my code:
case "Load":
function getMySave () {
return axios.get("http://localhost:3000/save", {
params: {
name: username
}
})
.then(response => {
console.log(response.data)
return response.data
})
.catch(error => {
console.log(error);
return Promise.reject(error);
});
}
const waitIgetMySave = async () => {
const playerSave = await getMySave();
return playerSave;
};
playerSave = (waitIgetMySave());
console.log(playerSave)
player = new Player(username, playerSave.class, playerSave.health, playerSave.mana, playerSave.strength, playerSave.agility, playerSave.speed, playerSave.maxhp);
break;
}
But this code just returns following:
Promise { : "pending" }
Object { _id: "5e9945f238a82e084c7cb316", name: "Jesse", class: "Rogue", ..... }
So object itself is working fine, but I can't apply it to anything outside of the axios.get function. It always gives me pending, promise[Object], undefined or such that I can't use in my player object or variables. I'm quite sure that I'm doing something wrong with async/await features where, but after spending few days trying to solve this problem, I'm really running out of options.
And yes, I looked at [How do I return the response from an asynchronous call?
[1]: How do I return the response from an asynchronous call? but that seems to be for Ajax and I just fail to understand and implement those in my own code.
The key principles of async code
You cannot make asynchronous code synchronous.
Promises are tools to manage asynchronous code in a consistent way
The async and await keywords are tools to manage Promises in a way that looks synchronous (but really just does clever stuff with functions going to sleep while they wait and allowing the rest of the program to keep running)
The specific issue with your code
waitIgetMySave is defined as async so it returns a Promise
When you call it (playerSave = (waitIgetMySave());) you do not await it, so you get the promise and not the resolved value from the promise.
(And waitIgetMySave will have gone to sleep while it waits for getMySave to resolve. This allows the rest of the program to keep going, assign the promise to playerSave and log that promise in the meantime).
This question already has an answer here:
avoiding nested callbacks with promises
(1 answer)
Closed 4 years ago.
[ITS NOT DUPLICATED]
the problem is different, but my question is answered below
I'm creating an ionic project where I have to do many promises some of then MAY have anothers promises like this example
this.Promise1()
.then((data) => {
if(logicTest){
return this.Promise2()
.then((data) => {
// ...
})
.catch(error => {
// Present error message to the user
});
}
// ...
})
.catch(error => {
// Present error message to the user
})
I saw an example where it puts a then down the other, like this
this.Promise1()
.then(() => { ... })
.then(() => { ... })
but how about my example where some times it doesn't return a promise and I need to catch different erros?
When do I make these nested promises?
Get the user from the storage to get the API_TOKEN to create the request
Then I make a request to update an item from a sale list
Then if two columns of this item have a certain value I make another request to update the sale
As you mentioned in your edit, chaining your promises is the classic way of addressing nested promises. In fact, one of the key purposes of the Promise API was to provide a solution for "callback hell".
If you'd like to take this a step further, you can also use async/await. In regards to the previously-deleted answer on compatibility for async/await, Ionic uses TypeScript and TypeScript transpiles code based on the configured compatibility target. This means that you should be able to use async/await to simplify your nested or chained promises without issue.
For example:
async myFunction() {
try {
const data = await this.Promise1();
if (logicTest) {
try {
return await this.Promise2();
}
catch (error) {
// Present error message to the user
}
}
// ...
}
catch (error) {
// Present error message to the user
}
}
Depending on your use case, you may be able to use async/await simplify your code even further to prevent excessive nesting.
If you do decide to use async/await then you should be sure to read up on how the functionality works. Misuse of this feature can cause race conditions and other unexpected behavior that can be difficult to diagnose. Many blog posts and tutorials exist that describe the functionality better than I can here. A quick Google search popped up this one for example: https://javascript.info/async-await.
I am attempting to use an async/await pattern in order to handle a scenario that might be considered "callback hell" if implemented otherwise.
Here is an extremely dumbed down version of the code. The real code has about 5 conditional HttpClient calls based on the data from the first call (not my api...) which is the reason why I am using the async/await pattern in the first place.
async blah(): Promise<boolean> {
try {
let resp = await this.http.get("https://httpstat.us/500").toPromise();
console.warn("you should not see this");
// the real code will logically call the api multiple times based on conditonal data from resp
// hence the attempted usage of async/await to avoid "callback hell"
// blah() will eventually return an object.
return true;
}
catch (err) {
console.error("caught inside blah()");
throw err;
}
}
ionViewDidLoad() {
this.blah().then(data => {
console.warn('okokokok');
}).catch(error => {
console.error(error)
});
}
What happens, I can see the call actually 500, but the code continues and the following is printed to the console:
polyfills.js:3 GET https://httpstat.us/500/ 500 (Internal Server Error)
main.js:927 you should not see this
main.js:940 okokokok
As you can see, it isn't catching the 500 (or any other http status I have tested with)
The device I am testing with is a Pixel 2 running P and the console data is coming from a Chrome device inspector session.
Any advice would be greatly appreciated.
** Edit: This is clearly an issue with the combination of ionic and angular... It should work...
** Edit: it turns out to 100% be an Angular issue... Not the framework itself but how an interceptor was implemented. I will leave this here instead of deleting the question in the rare case someone else requires it.
If i uderstood your question correctly, you want to do cascade calls, so you make the http request and based on the response you want to do another http call. If that is the case, then you should consider using switchMap operator:
this.http.get("https://httpstat.us/500").pipe(
switchMap( result => {
if(result.a === 5) {
return this.http.get("some server api url");
}
return return this.http.get("another server api url");
})
)
You handle the errors then in rxjs way.
See cascading calls