if statement not running which includes an async function - javascript

I have an async function inside an if/else statement. I just want to check whether there is a file in the request body before even using it otherwise it throws an error. Why is this happening? And is there anything better I could do to handle the problem ?
//save image to cloudinary
if (typeof(banner) !== undefined) {
//this piece of code is always executing even if the image is not present in request or is undefined
cloudinary.uploader.upload(banner,
async function(error, result) {
if (error) {
return res.status(409).send("Something went wrong while uploading image")
console.log(error)
}
article.banner = result.url
const savedArticle = await article.save()
return res.status(201).send(JSON.stringify({
redirect_uri: "/articles/" + savedArticle.slug
}))
})
} else {
return res.status(400).send("Missing file")
}

Your outer if does nothing, because typeof banner will never be the value undefined (it could be the string "undefined" though).
typeof returns a string with the type name in it, so if (typeof banner !== 'undefined') should work.
However, most likely you don't even need typeof here - if (banner !== undefined) or even just if (banner) should work too in your case.

Related

Javascript catch error within console and callback message

I have this function which is a search query. This works fine unless the query string name is not detailed enough which then throws up an error message
request( url, function (error, data) {
errorText = arguments['0'];
if (error) {
callback('Unable to connect to location services!', undefined)
} else if (errorText.includes('Cannot read property')) {
callback ('Unable to find location, try another search.', undefined)
}
else {
callback( {
data = data.body,
namePlace: data.suggestions[1].entities[0].name,
idPlace: data.suggestions[1].entities[0].destinationId,
})
}
})
The error message from the console is
namePlace: data.suggestions[1].entities[0].name,
^
TypeError: Cannot read property 'name' of undefined
I want to be able to capture this error message and callback 'Unable to find location, try another search.'
It sounds like what you want to check for is whether data.suggestions[1].entities has any entries. Perhaps something like:
else if (data.suggestions[1].entities.length === 0) {
callback('Unable to find location, try another search.', undefined)
}
Depending on how reliable the resulting data structure is, you might also check for null or undefined values. But for this particular error it looks like entities is defined and an array but just has no entries.
You need to change the logical order of the first if, otherwise else if (error) will prevent if (errorText.includes('Cannot read property')).
Have you previously defined let request = new Request(<uri>)?
What is it that you're trying to do?
request( url, function (error, data) {
errorText = arguments['0']; // What are you trying to do here?
if (errorText.includes('Cannot read property')) {
callback ('Unable to find location, try another search.', undefined)
} else if (error) {
callback('Unable to connect to location services!', undefined)
} else {
callback( {
data = data.body, // What are you trying to do here?
namePlace: data.suggestions[1].entities[0].name, // What are you trying to do here?
idPlace: data.suggestions[1].entities[0].destinationId,
})
}
}); // <- semicolon here

Using nested try-catch blocks

tl;dr If a task can fail at multiple events e.g. API fetch, division, parsing etc, does it make sense to have multiple try-catch blocks or a single one to catch 'em all?
I have a function in which I perform two tasks.
Fetch two numbers from an API, a and b.
Perform a/b
This is a simplified version of the actual problem. I wanted to ask how to handle for exceptions as the task can fail on either of the two steps:
The fetch itself failed.
a/b resulted in an error because b = 0.
I can think of two approaches.
Option I
try {
const data = getFromAPI();
const result = data[0] / data[1];
return result;
} catch (err) {
// Catch all errors here...
}
Option II
try {
try {
const data = getFromAPI();
} catch(err) {
// Catch all API errors here..
}
const result = data[0] / data[1];
return result;
} catch (err) {
// Catch division errors here...
}
You should start with checking the data you are working with (as far as reasonably possible). After that you should only try/catch the code which can fail / when it's out of your control, nothing else. So I will give you another option.
And to answer your other question, never make nested try catch statements. It simply doesn't make sense. If different type exceptions can occur, try identifying the type of the exception (i.e. with the instanceOf method or properties on the error object) and handle it.
Option III
try {
var data = getFromAPI();
} catch (err) {
// Catch errors from the API request here...
}
if(Array.isArray(data) && !isNaN(data[0]) && !isNaN(data[1]) && data[0] > 0 && data[1] > 0) {
const result = data[0] / data[1];
return result;
}
return 0;
This is a question that the answer depends on the system, whether you want to tell the user or want to know what kind of exception was thrown instead of doing several try / catch advise you to use a switch or an if inside the catch instead of multiple nested try / catch.
try{
//your code
}catch(ex){
if(ex instanceof ReferenceError){
//handle error
}
}
you can simply use:
try {
const data = getFromAPI(); //wait for request to finish
if(typeof data !== 'object') throw('fetch error');
if(data[0] === 0 ||
data[1] === 0 ||
typeof data[0]!== 'number' ||
typeof data[1]!== 'number'
) throw('your error here');
const result = data[0] / data[1];
return result;
} catch (err) {
// Catch all errors here...
}

define outer scope variable inside function

i am building validation for one of form's field serverside (expressjs) and doing following actions for that:
Read data from json file
Get property from it (Array)
Check if it contains every single element of user generated array and nothing more, for example:
[1,2,3,4,5]; (json array)
[1,2,3,4,5,6] (user generated array) //must return false
[1,2,3,4,5];
[1,3,4] //must return true;
[1,2,3,4,5];
[1,2,7] //must return false;
so i am using this code for that:
const contains = (arr1, arr2) => {
arr2.every(v => arr1.indexOf(v) !== -1)
}
var match;
fs.readFile('../tags.json', 'utf8', (err, data)=>{
var JsonData = JSON.parse(data);
var tagsArray = JsonData.tags;
console.log(tagsArray)
console.log(tags)
if(tagsArray instanceof Array){
console.log('tagsArray is array')
}
if(!contains(tagsArray, tags)){
match = false
}
else{
match = true
}
console.log(match + ' blah1')
});
console.log(match + ' blah2')
if(match == false){
return res.status(409).send({
message: 'Do not provide your own tags'
});
}
but it always returns false inside fs.readFile block because it returns undefined outside fs.readFile block, so this means that contains function return undefined (i tested it)
so what is the clue for this?
Thanks!
fs.readFile is asynchronous, so any code that depends on its result (the file being read) needs to go within your callback function. (The callback function is the (err, data) => { ... } part.)
Move the console.log(match + 'blah2') and if(match == false) { ... } parts inside of the callback (after the blah1 line).
You could also look into async or use fs.readFileSync which would allow you to avoid using callback functions.
Another side point, you will want to make sure you always reach a res.send() line, i.e. when match == true in your case. Otherwise your http request will not return when match is true.
Edit:
Here's a really basic structure for express, mostly pseudocode & comments, just to illustrate callbacks:
app.post('/tags', (req, res) => {
// your setup code here
fs.readFile('../tags.json', 'utf8', (err, data) => {
console.log('readFile has finished')
// now you have heard back from readFile
// check the err and send 500 if there was a problem
// otherwise work with the file in the var data
// any other db-related stuff also goes in here, which probably
// has its own callback you need to use
db.save(data, (err) => {
// db call is done, potentially with an error
// Here you can use `res` to send http response
})
// !! again here the db is still doing its work
})
// !! anything you add here will be executed before readFile is done
console.log('readFile is probably still at work')
})
I should also point out that you want contains to return the bool value, i.e. return arr2.every(...)
You can use async/await :
async function read(){
let data = await fs.readFile(<path>);
console.log(data); //You can use data everywhere within this scope
}

Callback not returning

I am for some reason seeing the last callback hit even though it does get inside that first if statement. Shouldn't it return in that first if when I do response(res)? Well it's not. For some reason it still continues on and hits the second response(res):
function find(response){
var res = {};
res.statusCode = 200;
var data = _gateway.find();
if(!data){
res.statusCode = 204;
res.body = null;
console.log("SETTING RESPONSE BODY TO NULL");
response(res);
}
console.log("SHHHHHHHHHHHHHOULD NOT GET HERE");
res.body = data;
response(res);
};
Calling the callback doesn't mean that the current function will cease execution at that point. It is the same as calling some other function. In your case, once the callback function returns a value, the control gets transferred to the find function only and it continues to execute the rest of the statements.
To fix this, you need to return from the find immediately after the callback returns, like this
...
if(!data){
res.statusCode = 204;
res.body = null;
console.log("SETTING RESPONSE BODY TO NULL");
response(res);
return; // return from the `find` function
}
...
You can even return the result of response, like this
return response(res);
so that the control will be transferred immediately from the find function.
After response(res) put return statement, otherwise it will try to execute remaining statements or put it in else part.

require() not running synchronously?

I'm trying to load a module I created, and when it's loaded use some of it's methods. But it seems the code after the require gets executed before the require actually finishes.
Here is some code :
console.log(db);
var db = require('../../models')(true, v);
console.log(db);
Both console.log output undefined.
Here is the code inside ../../models :
module.exports = function(sync, v) {
if(sync === true && typeof v !== 'undefined') {
var db = null;
async.series([function(callback) {
require('../lib/db_connect.js').connect(require('../lib/config.js').vars.db, function(res) {
db = res;
callback();
});
}], function() {
return loadModels(db, v).models; // this returns the db instance with the models loaded
});
} else { /* other stuff */ }
});
However, I see the connection debug popping in the console some time after (so it gets loaded, just not synchronous). What am I doing wrong? async.series should return only when everything is finished? And most of all, not return undefined?

Categories

Resources