I am using json-server to create a simple api server. I am mainly doing this to practice my understanding of async/await and asynchronous code in javascript.
Here is my json-server start code that seeds the db and api code:
module.exports = () => {
const data = { documents: [] }
for (let i = 0; i < 4000; i ) {
data.documents.push({ id: i })
}
console.log('done');
return data
}
Now I can hit GET - /documents (returns all documents) GET - /documents/id (returns a document via the ID).
I want to practice Error handling with async await, so I made middleware to return an error if the id is 4:
module.exports = (req, res, next) => {
if(req.originalUrl.split('/')[2] === '4') {
return res.status(429).send({error: 'rate limit exceeded'})
};
next()
}
The API structure appears to work as I intended. However, when I use Promise.allSettled to process an array of promises, the promise that returns an error has the error object as a value, but the status still says "fulfilled".
const getById = async (id) => {
try {
const data = await axios.get(`http://localhost:3000/documents/${id}`);
return data;
} catch (e) {
return e
}
}
( async () => {
try {
const resolvedPromises = await Promise.allSettled([
await getById(1),
await getById(2),
await getById(3),
await getById(4)
]);
console.log(resolvedPromises);
} catch(e) {
// THIS RUNS IF I THROW THE ERROR FROM getById, however Promise.allSettled stops
// execution.
console.log(e);
console.log('error');
}
})()
Here is the output when I run the code above:
[
{
status: 'fulfilled',
value: {
status: 200,
statusText: 'OK',
headers: [Object],
config: [Object],
request: [ClientRequest],
data: [Object]
}
},
{
status: 'fulfilled',
value: {
status: 200,
statusText: 'OK',
headers: [Object],
config: [Object],
request: [ClientRequest],
data: [Object]
}
},
{
status: 'fulfilled',
value: {
status: 200,
statusText: 'OK',
headers: [Object],
config: [Object],
request: [ClientRequest],
data: [Object]
}
},
{
status: 'fulfilled',
value: [AxiosError: Request failed with status code 429] {
message: 'Request failed with status code 429',
name: 'AxiosError',
code: 'ERR_BAD_REQUEST',
config: [Object],
request: [ClientRequest],
response: [Object]
}
}
]
Why is the status fulfilled
vs rejected
when I call await getById(4)
?
Is this an issue with the value I'm returning from the express server?
CodePudding user response:
Promise.allSettled
is not being passed an array of promises: the await
operators in
const resolvedPromises = await Promise.allSettled([
await getById(1),
await getById(2),
await getById(3),
await getById(4)
]);
are executed when the parameter value to pass to allSettled
is being evaluated before making the call. Since await
throws if its promise operand becomes rejected, the catch
clause of the surrounding try/catch
is executed as noted in the question.
Leaving out the await
operators before the calls to getById
may solve the issue, assuming getById
calls never synchronously throw before returning a promise.
Looking more closely getData
is an async function and so won't throw on being called. It is also fullfilling its return promise with an error object if the axios
call rejects. If you want the promise to be settled in a rejected status, you could
rethrow the error in the
catch
clause withingetById
,remove the
try/catch
block from withingetById
.return the axios promise from
getById
by reducing it toconst getById=id=>await axios.get(`http://localhost:3000/documents/${id}`);
CodePudding user response:
About this part here:
THIS RUNS IF I THROW THE ERROR FROM getById, however Promise.allSettled stops execution.
Because your code is equivalent to this here:
try {
const tmp = [];
tmp[0] = await getById(1);
// wait for getById(1) to finish
tmp[1] = await getById(2);
// wait for getById(2) to finish
tmp[2] = await getById(3);
// wait for getById(3) to finish
tmp[3] = await getById(4);
// wait for getById(4) to finish
// and if any one of these promises is rejected,
// the code goes straight to `catch` and never even reaches the following line.
const resolvedPromises = await Promise.allSettled(tmp);
console.log(resolvedPromises);
} catch (e) {
// THIS RUNS IF I THROW THE ERROR FROM getById, however Promise.allSettled stops
// execution.
console.log(e);
console.log('error');
}
These await
s are not only slowing your requests down, by running them in series instead of paralell, they are breaking your code.
CodePudding user response:
catch (e) { return e }
Catching the error and returning a value turns that rejected promise into a fulfilled one.
Remove the try..catch
and everything will work as expected (once you also implement the suggestions in traktor's answer)
const getById = (id) => axios.get(`http://localhost:3000/documents/${id}`);
If you did want to catch failures (eg for logging), ensure you keep the resulting promise rejected
// async / await
try {
return await axios.get(url);
} catch (err) {
console.error("request failed", err.toJSON());
throw err; // re-throw
}
// .then() / .catch()
return axios.get(url)
.catch((err) => {
console.error("request failed", err.toJSON());
return Promise.reject(err);
});
See also