How to prevent POSSIBLE nested promises? [duplicate] - javascript

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.

Related

returning to caller from deep nested dependent promise in javascript [duplicate]

This question already has answers here:
What is the explicit promise construction antipattern and how do I avoid it?
(3 answers)
Closed 2 years ago.
I have common scenario but I don't find solution anywhere without a callback method. Here is what I have:
from a service method, calling a helper method in another file, from helper calling a worker method which performs a sequence of async operations. they are dependent one another.
let myParentPromise = MyService.serviceFunc(in1, in2);
myParentPromise.then(res => console.log(res));
myParentPromise.catch(exc => console.log(exc));
service layer:
serviceFunc: (in1, in2) => {
return new Promise((resolve, reject) => {
//some processing
let helperPromise = MyHelper.helperFunc(in1,in2);
helperPromise.then(res =>
//processing
{resolve(res)});
helperPromise.catch(exc => {reject(exc)})
})
Helper Layer:
helperFunc: (in1, in2) => {
return new Promise((resolve, reject) =>{
resolve(async1);
}).then(res1 =>{
return async2;
}).then(res2 => {
return res2;
})
}
I want to pass res2 to my service layer then to myParentPromise.
how to do that?
Is it possible to do that without using callbacks? I like to purely use promises to do this job.
any help would be appreciated.
I suggest you take a look at the docs here.
If you return the promises you can chain them. You're already returning res2, the only thing left in order to be able to get that value in myParentPromise is the following:
const finalResult = serviceFunc(in1, in2)
.then(res => res)
.catch(exc => exc);
const serviceFunc = (in1, in2) =>
helperFunc(in1, in2)
.then(res => res)
.catch(exc => exc);
const helperFunc = (in1, in2) => async1.then(res1 => async2).then(res2 => res2);
Finally, I would suggest you read about async/await which is, in my opinion, an improvement to the codebase.
[Edit] Updated the code and add examples of reducing nesting.
So the problem with nesting is that you make the asynchronous code obscure, may catch error incorrectly and even worse, may create a new chain of promises that races against the first one, this is explained very well here.
So, how to avoid all of this: return the promises as you can see in the example above. This other post is a good explanation of why it's an antipattern and how to fix it :).
Now, how to reduce nesting? There are two ways that I can think of:
Just chain promises at the top level, which is not always the most simple, elegant, and better code, but plausible. You can see a good example here.
Use async/await.
Just keep always in mind that you're working with async code and as a rule of thumb, always return the promises. Happy coding!

How to use Axios async/await properly to get variable out of response?

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).

Vue.js async/await with then function not executing

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)
})
}
})();
},

Handling errors in an async/await Angular HttpClient method

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

How to execute function for each observable in RXJS

I have a situation with observable like this:
//pseudo code
var agreement:Observable<agreement[]> = Observable.ajax({...})
.flatMap(agreements:[] => {
agreements.forEach(agreement =>{
//server request for each agreement
//If request fail throw exception
});
return agreements;
})
.map(agreements => agreements);
Here, I need to check each agreement if it`s exist in other server and throw exception if not. Main question is how wait while all all agreements are checked adn only then get result.
The answer to this depends on what's happening inside the forEach loop – if agreements (the one going into the flatMap) is an Observable that you're using forEach to emit, the strategy is a bit different altogether because you don't necessarily know how many agreement objects will be emitted.
Assuming though the forEach is just running some other basic http calling, say, one that returns promises... The answer is then going to involve collecting the promises and allowing them all to complete the Promise-way, so that the completion can then be converted into an Observable for the next operator in the chain.
Some pseudo code:
var agreement: Observable<agreement[]> = Observable.ajax({...})
.flatMap(agreements:[] => {
const promises = agreements.map(agreement => doHttpThing()
.then(...)
.catch(err => Observable.throw(err))
);
return Observable.fromPromise(Promise.all(promises));
})
.map(agreements => agreements);

Categories