How to determine which promise throws error (in a promise chain) - javascript

How can I determine which of the three promises caused the error?
The code below is pseudo, but in my actual code I am working with third-party libraries (Stripe and Firebase) and therefore I cannot modify the actual Promises themselves.
I thought that I could check to see if the error argument in the catch contains a specific value (e.g. in Stripe there's a very detailed error object), but surely there must be a better way.
return async.func.one.doIt()
.then(() => {
return async.func.two.doIt();
})
.then(() => {
return async.func.three.doIt();
})
.then(() => {
return { success: true };
})
.catch((error) => {
// How do I know which 'then' caused
// the catch to invoke?
});

You can put '.catch' between each '.then' method to catch error. It should catch closest error that was thrown by any of previous promises, up to next '.catch' method.

you can change your code to this
return func.one.doIt()
.then(() => {
return async.func.two.doIt().then(() => {
return async.func.three.doIt().then(() => {
return { success: true };
}).catch((error) => {
// How do I know which 'then' caused
// the catch to invoke?
});;
}).catch((error) => {
// How do I know which 'then' caused
// the catch to invoke?
});
}).catch((error) => {
// How do I know which 'then' caused
// the catch to invoke?
});

Related

Can't call fetch api multiple times

I want to call this api multiple times in my project and when I am calling it , It continues giving an error which is
TypeError: Failed to execute 'json' on 'Response': body stream already
read at main.js:Line number
My Code is as Follows
let thisIsUrl = 'https://api.covid19api.com/summary';
let a = fetch(thisIsUrl)
a.then((data) => {
return data.json()
}).then((apidata) => {
console.log(apidata)
return apidata
}).catch((error) => {
console.log(error)
})
a.then((fetchdata) => {
return fetchdata.json()
}).then((readingData) => {
console.log(readingData)
}).catch((err) => {
console.log(err)
})
You're not calling fetch multiple times. You're calling it once, and then trying to read the response body multiple times. That's why the error says you're trying to read the body when the stream is already closed — it was closed when you were done reading it the first time.
If you want to use the data twice, store it somewhere and use it twice.
let thisIsUrl = 'https://api.covid19api.com/summary';
let a = fetch(thisIsUrl)
a.then((data) => {
return data.json()
}).then((apidata) => {
// **************** Use it twice here
}).catch((error) => {
console.log(error)
})
If you want to fetch it again because it may have been updated, call fetch again:
let thisIsUrl = 'https://api.covid19api.com/summary';
fetch(thisIsUrl)
.then((data) => {
return data.json();
}).then((apidata) => {
console.log(apidata);
return apidata;
}).catch((error) => {
console.log(error);
});
// Presumably this is later (in time), not immediately after the above
fetch(thisIsUrl)
.then((fetchdata) => {
return fetchdata.json();
}).then((readingData) => {
console.log(readingData);
}).catch((err) => {
console.log(err);
});
Finally, this seems unlikely, but if you really want to fetch it once and use that one result in multiple places via the promise chain, keep the promise from then rather than the promise from fetch:
let thisIsUrl = 'https://api.covid19api.com/summary';
let a = fetch(thisIsUrl)
.then((data) => {
return data.json()
});
a.then((apidata) => {
// ***** Use it here
}).catch((error) => {
console.log(error)
})
a.then((readingData) => {
// ***** And then again here
}).catch((err) => {
console.log(err);
});
Side note: Your code is falling prey to a footgun in the fetch API; I've written about it in this blog post. fetch only rejects its promise on network errors, not HTTP errors. You have to check for those yourself in the first fulfillment handler, by checking for ok on the response object:
fetch("/your/resource")
.then(response => {
if (!response.ok) {
throw new Error("HTTP error " + response.status); // Or better, use an Error subclass
}
return response.json();
})
// ...
fetch returns Promise, generally, promises have something like state inside themself;
pending: initial state, neither fulfilled nor rejected.
fulfilled: meaning that the operation was completed successfully.
rejected: meaning that the operation failed.
(source)
So when we call them and get the value from them with then, catch and etc. then they change the state after that call. So here, when you read the value with a.then(…, the promise changes its state to fulfilled and you are not able to call it again, you need a new and fresh Promise, actually a new instance of the fetch.
I want to recommend you to use Promise.all().
let thisIsUrl = 'https://api.covid19api.com/summary';
let a = fetch(thisIsUrl)
.then((data) => {
return data.json()
}).then((apidata) => {
console.log(apidata)
return apidata
}).catch((error) => {
console.log(error)
})
Promise.all([a,a,a]);
.then(results => {
// Results are here.
});

How do I handle an error and then immediately break out of a promise chain?

So I have an Express app that uses middleware to parse JSON POST requests and then populate a req.body object. Then I have a promise chain that validates the data against a schema using Joi, and then stores it in a database.
What I would like to do is check if an error was thrown after one of these processes, handle it appropriately by sending a status code, then COMPLETELY ABORT the promise chain. I feel like there should be some EXTREMELY CLEAN AND SIMPLE way to do this, (perhaps some sort of break statement?) but I can't find it anywhere. Here is my code. I left comments showing where I hope to abort the promise chain.
const joi = require("joi");
const createUserSchema = joi.object().keys({
username: joi.string().alphanum().min(4).max(30).required(),
password: joi.string().alphanum().min(2).max(30).required(),
});
//Here begins my promise chain
app.post("/createUser", (req, res) => {
//validate javascript object against the createUserSchema before storing in database
createUserSchema.validate(req.body)
.catch(validationError => {
res.sendStatus(400);
//CLEANLY ABORT the promise chain here
})
.then(validatedUser => {
//accepts a hash of inputs and stores it in a database
return createUser({
username: validatedUser.username,
password: validatedUser.password
})
.catch(error => {
res.sendStatus(500);
//CLEANLY ABORT the promise chain here
})
//Only now, if both promises are resolved do I send status 200
.then(() => {
res.sendStatus(200);
}
)
});
You can't abort a promise chain in the middle. It's going to either call a .then() or a .catch() later in the chain (assuming there are both and assuming your promises resolve or reject).
Usually, the way you handle this is you put one .catch() at the end of the chain and it examines the type of error and takes appropriate action. You don't handle the error earlier in the chain. You let the last .catch() handle things.
Here's what I would suggest:
// helper function
function err(status, msg) {
let obj = new Error(msg);
obj.status = status;
return obj;
}
//Here begins my promise chain
app.post("/createUser", (req, res) => {
//validate javascript object against the createUserSchema before storing in database
createUserSchema.validate(req.body).catch(validationError => {
throw err("validateError", 400)
}).then(validatedUser => {
//accepts a hash of inputs and stores it in a database
return createUser({
username: validatedUser.username,
password: validatedUser.password
}).catch(err => {
throw err("createUserError", 500);
});
}).then(() => {
// success
res.sendStatus(200);
}).catch(error => {
console.log(error);
if (error && error.status) {
res.sendStatus(error.status);
} else {
// no specific error status specified
res.sendStatus(500);
}
});
});
This has several advantages:
Any error propagates to the last .catch() at the end of the chain where it is logged and an appropriate status is sent in just one place in the code.
Success is handled in just one place where that status is sent.
This is infinitely extensible to more links in the chain. If you have more operations that can have errors, they can "abort" the rest of the chain (except the last .catch() by just rejecting with an appropriate error object).
This is somewhat analogous to the design practice of not having lots of return value statements all over your function, but rather accumulating the result and then returning it at the end which some people consider a good practice for a complicated function.
When debugging you can set breakpoints in one .then() and one .catch() to see the final resolution of the promise chain since the whole chain goes through either the last .then() or the last .catch().
.catch returns a resolved Promise by default. You want a rejected Promsise. So, you should return a rejected promise from inside the .catch, so that future .thens won't execute:
.catch(validationError => {
res.sendStatus(400);
return Promise.reject();
})
But note that this will result in a console warning:
Uncaught (in promise) ...
So it would be nice to add another .catch to the end, to suppress the error (as well as catch any other errors that come along):
const resolveAfterMs = ms => new Promise(res => setTimeout(() => {
console.log('resolving');
res();
}), ms);
console.log('start');
resolveAfterMs(500)
.then(() => {
console.log('throwing');
throw new Error();
})
.catch(() => {
console.log('handling error');
return Promise.reject();
})
.then(() => {
console.log('This .then should never execute');
})
.catch(() => void 0);
If you want to avoid all future .thens and future .catches, I suppose you could return a Promise that never resolves, though that doesn't really sound like a sign of a well-designed codebase:
const resolveAfterMs = ms => new Promise(res => setTimeout(() => {
console.log('resolving');
res();
}), ms);
console.log('start');
resolveAfterMs(500)
.then(() => {
console.log('throwing');
throw new Error();
})
.catch(() => {
console.log('handling error');
return new Promise(() => void 0);
})
.then(() => {
console.log('This .then should never execute');
})
.catch(() => {
console.log('final catch');
});
A cleaner solution for what you are trying to accomplish might be to use express-validation, which is a simple wrapper around joi that provides you with express middleware for validation of the body, params, query, headers and cookies of an express request based on your Joi schema.
That way, you could simply handle any Joi validation errors thrown by the middleware within your "generic" express error handler, with something like:
const ev = require('express-validation');
app.use(function (err, req, res, next) {
// specific for validation errors
if (err instanceof ev.ValidationError)
return res.status(err.status).json(err);
...
...
...
}
If you don't want to use the express-validation package, you could write your own simple middleware that does more or less the same thing, as described here (see example here).
One strategy is to separate your error handling in subpromises which have their individual error handling. If you throw an error from them, you'll bypass the main promise chain.
Something like:
return Promise.resolve().then(() => {
return createUserSchema.validate(req.body)
.catch(validationError => {
res.sendStatus(400);
throw 'abort';
});
}).then(validatedUser => {
// if an error was thrown before, this code won't be executed
// accepts a hash of inputs and stores it in a database
return createUser({
username: validatedUser.username,
password: validatedUser.password
}).catch(error => {
// if an error was previously thrown from `createUserSchema.validate`
// this code won't execute
res.sendStatus(500);
throw 'abort';
});
}).then(() => {
// can put in even more code here
}).then(() => {
// it was not aborted
res.sendStatus(200);
}).catch(() => {
// it was aborted
});
You can skip the Promise.resolve().then() wrapping, but it's included for illustrative purposes of the general pattern of subdividing each task and its error handling.

axios.all spread and catch all

I'm using .all method of popular library 'axios' for handling my ajax requests.
But how can I handle errors in case all requests got 404?
for example:
axios.all([
axios.get('http://some_url'),
axios.get('http://another_url'),
])
.then(axios.spread((someUrl, anotherUrl) => {
// ... boring stuff goes there
}))
.catch(() => {
//... error goes there
});
So, seems only one error has ben "catched".
How can I catch them all? Or maybe there any kinda .finally?
The problem (as you already know) is that you will get into catch block as soon as the first promise rejects, making it impossible to collect all failed responses in the same catch. However, you still can handle failed promises manually to aggregate errors and throw afterwards.
Check it this will work for you:
const promises = [
axios.get('http://some_url'),
axios.get('http://another_url'),
]
const promisesResolved = promises.map(promise => promise.catch(error => ({ error })))
function checkFailed (then) {
return function (responses) {
const someFailed = responses.some(response => response.error)
if (someFailed) {
throw responses
}
return then(responses)
}
}
axios.all(promisesResolved)
.then(checkFailed(([someUrl, anotherUrl]) => {
console.log('SUCCESS', someUrl, anotherUrl)
}))
.catch((err) => {
console.log('FAIL', err)
});
You will get into catch block if at least one of the promises fails. You can find one which one by checking err array of responses.
I don't think this is possible due to the fail fast behaviour of Promise.all. If any of your requests fail, they will automatically be the culprit and the result in the catch.
Promise.all([
Promise.reject(Error('1')),
Promise.reject(Error('2')),
Promise.reject(Error('3'))
]).then((results) => {
console.log(results)
}, (error) => {
console.log(error.message)
})
This resulting code will always print 1 as it is the first to fail.I think a similar feature was requested on the repo and they said it wasn't possible.
I was going to leave this as a comment but don't have a high enough reputation yet.
The solution from #dfsq did not work for me because it throws all requests when one has an error. I changed his code so every request either gets resolved or throws an error. #dfsq please review this answer if the code is correct, since I built it on your solution.
const promises = [
axios.get('http://some_url'),
axios.get('http://another_url'),
]
const promisesResolved = promises.map(promise => promise.catch(error => ({ error })))
function checkFailed (then) {
return function (responses) {
responses.forEach(response => {
if (response.error)
throw response;
return then(response);
})
}
}
axios.all(promisesResolved)
.then(checkFailed(response => {
console.log('SUCCESS', response)
}))
.catch((err) => {
console.log('FAIL', err)
});

Firebase Promise chain terminate in catch block

I need to exit http function if first catch block is reached.
I'm doing following
.then() <- start work related with database
.then()
.then()
.catch(error => { <- Here I want to catch errors with firebase database and exit function.
console.log(error)
response.status(500).send(error)
})
.then() <- Here I want to send FCM message if there was no database errors
.catch(error => {
console.log(error)
response.status(200).send("Success") <- Main work with database was finished. I still want to send http 200 and don't care about FCM errors.
})
.then(() => {
response.status(200).send("Success")
}) <-This catch block should be fired if there was an issue with FCM
Problem is that function continue to run after first catch block. How to stop this chain properly in first catch block? Thanks
Something like this should work, using a combination a propagated values and a top-level sentinel:
let bail = false
doWork()
.then(result => {
console.log(result)
return true // indicate success
})
.catch(error => {
console.error(error)
return false // indicate error
})
.then(isPriorSuccessful => {
if (!isPriorSuccessful) {
bail = true
return null
}
else {
// do more stuff here, return a promise
return doMoreWork()
}
})
.catch(error => {
console.error(error)
})
.then(() => {
if (bail) {
res.status(500).send("NOT OK")
return
}
console.log("Just before the end")
res.send("OK")
})

Return from .catch(error) not returning in Node / Express

I'm writing a REST API and trying to correctly handle any errors.
When the API call succeeds, the the success object is returned to the calling function and the response is send to the client. But if an error occurs, I want to return the error to the calling function so I can send an error message to the client.
router.delete('/project', (req, res) => {
return DeleteProject(userId, projectId)
.then((response) => {
//handle response
});
});
DeleteProject: (userId, projectId) => {
return deleteProject(userId, projectId)
.then((response) => {
return response
})
.catch((error) => {
console.log('Error in DeleteProject:', error) // This happens.
return error; // this doesn't happen.
})
},
function deleteProject(userId, projectId) {
return Project.deleteOne( ... delete the project... )
.then((response) => {
return response
})
.catch((error) => {
return error
})
}
The .catch(error) in the middle function above, DeleteProject(), gets triggered when an error occurs (ie, the console log happens), but the return doesn't make it's way back to the router.
How can I return the error to be handled by the router?
You can simply remove catch methods from the other two functions, and put the catch function in the router itself. Then the error will itself propagate to your router function
router.delete('/project', (req, res) => {
return DeleteProject(userId, projectId)
.then((response) => {
//handle response
}).catch(() => {
// Add catch function here. Any error in "DeleteProject" and "deleteProject" will propagate to here
})
});
DeleteProject: (userId, projectId) => {
return deleteProject(userId, projectId)
.then((response) => {
return response
});
// Remove catch function
},
function deleteProject(userId, projectId) {
return Project.deleteOne( ... delete the project... )
.then((response) => {
return response
});
// Remove catch function
}
To propagate errors through promise chains you need to throw them. In your catch handler, when you return the error rather than throwing it, you'e setting the (successfully) resolved value of the promise to be the error.

Categories