I have a function below that relies on another function as argument 1. The problem is it doesn’t wait until the function is done returning a record. It just runs into a conditional block and chooses “record not found.”
How do you solve an async issue like this when you are waiting for an argument to resolve?
Here is the code:
const lookup_shortlink = (long_link) => {
console.log("long link in parameter, before hitting the findOne ", long_link);
ShortLink.findOne({
long_link : long_link
},
function (error, object) {
if (error) {
console.log('error ', error);
} else if (object) {
console.log("object found during lookup", object);
return object
}
}
)
};
// pass current record or create new and pass back
const return_record_or_create_new = (found_record, long_link) => {
if (found_record) {
// record is found >> return it to the front end
console.log("found record during return step ", found_record);
return found_record
} else {
console.log("record not found");
}
};
// API Endpoints
router.get('/result', function (req, res, next) {
const long_url = req.query.long_url;
return_record_or_create_new(lookup_shortlink(long_url), long_url);
// render the data to the result page
res.render('result', { title: "Results", short_url : long_url });
});
EDIT: Also tried this variation with the same results:
const return_record_or_create_new = (long_link) => {
const found_record = lookup_shortlink(long_link);
if (found_record) {
// record is found >> return it to the front end
console.log("found record during return step ", found_record);
return found_record
} else {
console.log("record not found during return/create step");
}
};
I tried adding async/await, but the issue remains:
// pass current record or create new and pass back
const return_record_or_create_new = async (long_link) => {
const found_record = await lookup_shortlink(long_link);
if (found_record) {
// record is found >> return it to the front end
console.log("found record during return step ", found_record);
return found_record
} else {
console.log("record not found during return/create step");
}
};
Hi @SeanDez,
The issue with your code is that lookup_shortlink
is an asynchronous function since it queries the database. So it executes and since you don’t have a return statement in this function it implicitly returns undefined
which is a falsy value. Therefore, found_record
is always a falsy value too and thats why it logs console.log("record not found");
.
There are couple of ways you could solve this problem with:
- Callbacks: I’ve taken some liberty with your code and modified some bits, see if it works:
const lookup_shortlink = (long_link, callback, render) => {
console.log("long link in parameter, before hitting the findOne ", long_link);
ShortLink.findOne({
long_link : long_link
},
function (error, object) {
if (error) {
console.log('error ', error);
callback(null);
} else if (object) {
console.log("object found during lookup", object);
callback(object, render);
}
}
)
};
// pass the returned object and render callback to this function
const return_record_or_create_new = (found_record, render_callback) => {
if (found_record) {
// record is found >> return it to the front end
console.log("found record during return step ", found_record);
//execute render callback if the record found
return render_callback();
} else {
console.log("record not found");
}
};
router.get('/result', function (req, res, next) {
const long_url = req.query.long_url;
const to_render = () => {
res.render('result', { title: "Results", short_url : long_url });
}
lookup_shortlink(long_url, return_record_or_create_new, to_render);
});
- Promises:
I believe mongoose doesnt support promises but it does support Promises like syntax.
You can use .then()
with mongoose function. Example
ShortLink.findOne({
//if found then resolve
}).then(() => {
//Do something after successful resolution
});
- async-await:
You’d have to convert other functions including the middleware function to async await too.
router.get('/result', async function (req, res, next) {
const long_url = req.query.long_url;
await return_record_or_create_new(lookup_shortlink(long_url), long_url);
// render the data to the result page
res.render('result', { title: "Results", short_url : long_url });
});
1 Like
Thanks @ezioda004! I’ve been working on this all day but can’t get it to work. I tried using callbacks as that is a pattern I need to practice. But then I mixed in async too. Even that is not working. Here is my API endpoint:
router.get('/result', async function (req, res, next) {
const long_url = req.query.long_url;
// check if the long url is in the db already
// if yes then set short_url to the collection.short_url
// if no then generate new record
const short_url = await lookup_shortlink(long_url, create_new_shortlink_record);
await console.log("index.js:74 ", short_url);
// pass the short_url to index.pug
// render the data to the result page
res.render('result', { title: "Results", short_url : short_url });
});
and the callbacks:
const lookup_shortlink = async (long_link, not_found_callback) => {
console.log("long_link from parameter, before hitting the findOne ", long_link);
await ShortLink.findOne({
long_link : long_link
},
function (error, object) {
if (error) {
console.log('error ', error)
} else if (object.long_link) {
console.log("object found during lookup", object);
console.log("object short_link", object.short_link);
return object.short_link;
} else {
console.log("not found during lookup");
not_found_callback(long_link);
}
}
)
};
const create_new_shortlink_record = (long_link) => {
const short_link_record = new ShortLink({
long_link : long_link,
short_link : process.env.ROOT_DOMAIN + '/r/' + shortid.generate()
});
short_link_record.save((error) => {
if (error) {
console.log(error);
}
});
console.log('index.js:55', short_link_record.short_link);
return short_link_record.short_link;
};
await console.log("index.js:74 ", short_url);
I know that line doesn’t need to be awaited but I was just testing await on it because it returns undefined. The previous steps return the correct information though so something is awry here.
Here is the console output:
[nodemon] starting `node ./bin/www start`
(node:15682) DeprecationWarning: current URL string parser is deprecated, and will be removed in a future version. To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect.
Connected to MongoDB
long_link from parameter, before hitting the findOne testval14
object found during lookup { _id: 5bd4ad739804951700595dd1,
long_link: 'testval14',
short_link: 'localhost:3000/r/gvwsUGejQ',
__v: 0 }
object short_link localhost:3000/r/gvwsUGejQ
index.js:74 undefined
GET /result?long_url=testval14 304 1722.137 ms - -
GET /stylesheets/style.css 304 1.971 ms - -