This question already has answers here:
Is there a way to wait until a function is finished in React Native?
(3 answers)
Closed 3 years ago.
I have this submit function that runs two functions when clicked on. My issue is that props.updateValues() is executed before createAffiliation(values)
How can I change the order of the execution?
const onSubmit = () => {
createAffiliation(values)
props.updateValues();
}
createAffiliation:
export function createAffiliation(anknytning: Anknytning) {
return axios
.post(Endpoints.affiliationData, anknytning)
.then((response: AxiosResponse<any>) => {
console.table("Created affiliation", anknytning);
return Promise.resolve(response.data);
})
.catch(reason => {
console.error("Could not create affiliation", reason);
return Promise.reject(reason);
});
}
As first function is returning a promise you can use .then() to let the first one finish:
const onSubmit = () => {
createAffiliation(values)
.then(()=>props.updateValues());
}
Or if you would like to see about Promise.race().
This happens because createAffiliation function returns a promise so we don't know when it will be resolved or rejected. Javascript will continue to execute your code, so props.updateValues() executes first.
There are 2 common way to resolve this issue.
1-) async await:
An async function can contain an await expression that pauses the execution of the async function to wait for the passed Promise's resolution, then resumes the async function's execution and evaluates as the resolved value.
The purpose of async/await is to simplify using promises synchronously. It can only be used inside an async function.
const onSubmit = async () => {
try {
await createAffiliation(values)
props.updateValues();
} catch (err) {
console.log(err);
}
}
2-) with then catch block you can do like this:
const onSubmit = () => {
createAffiliation(values)
.then(() => props.updateValues())
.catch(err => console.log(err));
};
Related
I have a very simple code I'm trying to debug. I have an async function:
async function updateResult(event){
let result = db.fetchResult(event.ProcessId);
return result;
}
And I'm calling this from another simple async function:
exports.processEvent = async (event) => {
try {
let promises = [];
const controls = await db.fetchControls(event.EventCode);
if (controls) {
promises = controls.map((control) => {
console.log(control);
control.map(x => this.updateResult(event));
});
}
return Promise.allSettled(promises);
}
catch (err) {
console.error(err);
}
};
The problem is that in exports.processEvent, the content of db.fetchControls(event.EventCode) is executed to completion (a few logics and a db getItem call).
But the call to db.fetchResult(event.ProcessId) via this.updateResult(x.Id, x.Version, event) does NOT complete executing the fetchResult tasks (a few other logics a db GET call).
I feel like fetchResult returns prematurely. What am I doing wrong?
.map() is NOT promise-aware. It does not wait for any of the promises in your callback to complete. So, in this piece of code:
control.map(x => this.updateResult(x.Id, x.Version, event));
It just runs through the entire array ignoring all the promises that are returned and thus NOTHING waits for those promises.
You can fix that by returning those promises to the higher level .map() which you are then awaiting with Promise.all():
promises = controls.map((control) => {
console.log(control);
return Promise.all(control.map(x => this.updateResult(x.Id, x.Version, event)));
});
This question already has answers here:
Using async/await with a forEach loop
(33 answers)
Closed 2 years ago.
My code looks something like this:
(async () => {
try {
const results = await heavyCalculation();
saveResultsToFiles(results);
} catch (e) {
handleError(e);
} finally {
process.exit(0);
}
})();
const saveResultsToFiles = (results) => {
results.forEach(result => {
(async () => await saveResultFile(result));
})
}
const saveResultFile = (result) => {
return promiseToPreprocess(result)
.then(processedResult => saveToFile(processedResult))
}
const promiseToPreprocess = async (result) => {
// this function returns a promise to preprocess the data
}
const saveToFile = (data) => {
// this function synchronously saves data to a file
}
I thought this code would
perform calculations
wait for each piece of the results to be preprocessed and saved to a file
then exit
The first step works, as the program seems to await the heavy calculation results. However, it appears the finally clause is entered before the promise within the forEach-loop is resolved, causing the program to exit early. What am I doing wrong?
You have two problems here:
Your forEach loop in saveResultsToFiles does not return anything so you have no way to make other parts of your code "wait" for each item's promise to resolve.
saveResultFile returns a promise but this promise is not awaited in your try block.
The result of both of these issues is that the try block only "starts" the process of saving to files but doesn't wait for it to finish before yieling to the finally block.
Here are solutions you could try.
You need to be able to await each of the saveResultFile calls and for that you need to access the array of promises instanciated in saveResultsToFiles. With .map you will actually get an array of results (as opposed to .forEach):
const saveResultsToFiles = (results) => {
return results.map(result => saveResultFile(result));
}
Now that saveResultsToFiles actually returns an array of promises, you should await all of them before proceeding. This is exactly what Promise.all is for:
try {
const results = await heavyCalculation();
await Promise.all(saveResultsToFiles(results));
}
You are not awaiting saveResultsToFiles(results);
Try:
(async () => {
try {
const results = await heavyCalculation();
saveResultsToFiles(results);
} catch (e) {
handleError(e);
} finally {
process.exit(0);
}
})();
const saveResultsToFiles = async (results) => {
results.forEach(result => {
await saveResultFile(result);
})
}
const saveResultFile = (result) => {
return promiseToPreprocess(result)
.then(processedResult => saveToFile(processedResult))
}
const promiseToPreprocess = async (result) => {
// this function returns a promise to preprocess the data
}
const saveToFile = (data) => {
// this function synchronously saves data to a file
}
Imagine the following hypothetical, minimal implementation of a function that does a simple HTTP GET request using axios. This uses await/async as depicted in the post.
const axios = require('axios')
exports.getStatus = async id => {
const resp = await axios.get(
`https://api.example.com/status/${id}`
)
return resp
}
Is the promise not resolved using await? Is it required that the client uses await as depicted below? Is it safe to assume that anytime a client consumes an async function, that it also needs to use await when calling the function?
// client
const { getStatus } = require('./status')
const response = await getStatus(4)
Short answer, no.
Labelling a function async means 2 things:
1) you're allowed to use await inside the function.
2) The function returns a promise; i.e. you can use await on its return value, or you can use .then, or Promise.all, or even ignore the return value, or anything else you can do with promises.
E.g. you could do the following which (depending on your use case) could be more performant because it needs not wait for the response to continue.
// client
const { getStatus } = require('./status')
const response4 = getStatus(4);
const response5 = getStatus(5);
// do stuff that don't rely on the responses.
response4.then(response => myOutput.setFirstOutput(response));
response5.then(response => myOutput.setSecondOutput(response));
Btw, your first snippet is redundant with its usage of await; it's equivalent to
const axios = require('axios')
exports.getStatus = id =>
axios.get(`https://api.example.com/status/${id}`);
This is because return await promise is equivalent to return promise, both return the same promise. There is one exception regarding rejected promises. Awaiting a rejected promise will throw an exception (which you can catch with a try/catch block), whereas directly returning a rejected promise will not throw an exception but you should still handle the rejection (with a .catch clause). To illustrate:
let promise = new Promise(resolve => setTimeout(() => resolve('resolved after 2 seconds'), 2000));
let returnPromise = () => promise;
let returnAwaitPromise = async () => await promise;
returnPromise().then(value => console.log('returnPromise,', value));
returnAwaitPromise().then(value => console.log('returnAwaitPromise,', value));
No, you don't need to use await. An async function returns a Promise that should be resolved. Instead of using await, you can simply call .then() like so:
// client
const { getStatus } = require('./status');
getStatus(4).then((response) => {
//Do something with the response
}).catch((error) => {
//Handle possible exceptions
});
Below I'll address your question from the comments. The getStatus function could be rewritten like this:
exports.getStatus = (id) => {
return new Promise((resolve, reject) => {
axios.get(`https://api.example.com/status/${id}`).then((resp) => {
resolve(resp);
}).catch((error) => {
reject(error);
})
});
}
It would act exactly the same as in your version. When you call asynchronous functions in series, you have to resolve each of the returned Promises. In your case, there are two of them - the one returned by axios.get and the one returned by getStatus.
No you don’t have to. The async function executes and waits until it returns error or completes execution and return a response!
This question already has answers here:
How do I convert an existing callback API to promises?
(24 answers)
Closed 3 years ago.
In the following code the last line does not print ('end'):
function setTimeoutSync(fn, milli) {
return new Promise((resolve) => {
setTimeout(fn, milli);
});
}
async function run () {
console.log('start');
await setTimeoutSync(() => { console.log('setTimeoutSync'); }, 3000);
console.log('end');
}
run();
It is not that the script is exiting, because nothing I put after the await statement gets executed.
How can NodeJS just not execute statements in a function? Also, what's a good way to do a good old-fashioned synchronous wait in NodeJS? This question is actually more about the former than the latter.
Not sure if this is a typo but you forgot to call resolve so the next line after the await will execute:
function setTimeoutSync(fn, milli) {
return new Promise((resolve) => {
setTimeout(() => {
fn()
resolve()
}, milli);
});
}
async function run() {
console.log('start');
await setTimeoutSync(() => {
console.log('setTimeoutSync');
}, 3000);
console.log('end');
}
run();
As for your question:
Can you explain how not calling resolve can result in the rest of the
function not executing?...
Well async await uses generators and promise behavior:
The purpose of async/await functions is to simplify the behavior of
using promises synchronously and to perform some behavior on a group
of Promises. Just as Promises are similar to structured callbacks,
async/await is similar to combining generators and promises.
A generator can yield results thus not terminating the execution context, and can continue it where it left off.
Basically your example could be written with Promise.prototype.then() callback:
function setTimeoutSync(fn, milli) {
return new Promise((resolve) => {
setTimeout(() => {
fn()
resolve()
}, milli);
});
}
async function run() {
console.log('start');
setTimeoutSync(() => {
console.log('setTimeoutSync');
}, 3000)
.then((result) => {
console.log('end');
});
}
run();
So as you see, if we are not resolving, the callback to .then won't run.
I have a function which is making an async call
and upon sucess, its making another async call
ex:-
function1 = () => {
/*Some required Logic*/
return fetch("myurl")
.then((json) => {
function2(json)
})
.catch((error) => {
console.log(error)
})
}
function2 = () => {
/*Some required Logic*/
return fetch("myurl2")
.then((json) => {
callthirdfunction(json)
})
.catch((error) => {
console.log(error)
})
}
now here is the function 3
in which i am dependent on the success of function1
function3 = () => {
/*Some required Logic*/
function1().then(() => {
})
}
Issue is it is only waiting untill the function1's asyc call is succeeded its not waiting for function2's async call to succeed
I know i can write like chain of asyn call but its not possible because of unavoidable circumstances
Any suggesstion or leads in this regard would be of great help
If you used async await as you mentioned in your tags, you could just use it like that:
await function1();
await function2();
await function3();
while adding the async keyword to the function declarations:
function1 = async () => {
}
Another option is to promisify your functions, and then call them like that:
function1()
.then(function2)
.then(function3)
.catch() {}
If you want one promise to depend on another promise, you need to return the dependent promise from then.
Your code doesn't do anything with the result of function2:
return fetch("myurl")
.then((json) => {
function2(json)
})
That should read
.then((json) => {
return function2(json)
})
The whole point of then is that it allows chaining of subsequent promises by returning them from the callback.
Promises get chained only if you return them. In the below example
function resolveAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function add1(x) {
const a = await resolveAfter2Seconds(20);
const b = await resolveAfter2Seconds(30);
return x + a + b;
}
Notice the return keyword inside resolveAfter2Seconds. Make sure that when you call a method that fires a promise, you add the 'return' keyword