REST API calls using async await - javascript

Here's my code snippet
var clients = require('restify-clients');
async function callApi(val){
const client = clients.createJsonClient({ url: apiUrl });
await client.get('/my/url', (err, req, res, obj) => {
if (err) {
return err;
} else {
return obj;
}
});
}
I've tried a few ways of calling it, but they all aren't working
First way:
var results = await callApi(val);
Second way:
var results = callApi(val).then(data => {
console.log(data);
})

client.get doesn't return a promise, you can't use await on a function which doesn't return a promise (honestly you can, but it doesn't make sense). The correct solution here is to promisify client.get and return a promise:
function callApi(val) {
const client = clients.createJsonClient({ url: apiUrl });
return new Promise((resolve, reject) => {
client.get('/my/url', (err, req, res, obj) => {
if (err) {
reject(err);
} else {
resolve(obj);
}
});
});
}
// Usage
let results = await callApi(val);

Try to remove await from results
var results = callApi(val);

Related

Async and promise function do not display anything in my res.json call

I have a controller in javascript which should get a given user, and then the pets that are associated with the user. The related pets are stored in an array of object refs within the user schema. At the minute, when I try to res.json the resulting array containing the related pets, it outputs as an empty array '[]'. Following the Mozilla docs and tutorials I have tried to implement a promise on this function to combat my previous issue of the res.json outputting an empty array. I'm not sure where I am going wrong as I am a newbie to JS/express/node/mongo
Problem code:
export const getPetsForAUser = (req, res)=>
{
function getter(){
return new Promise(resolve =>{
User.findOne({_id: req.params._id}, (err, users) =>{
let petlist = users.pets;
for(var i = 0; i < petlist.length; i++){
Pet.findOne({_id:petlist[i]}, (err, pet) =>{
var t = pet
return Promise.resolve(t)
});
}
})
});
}
async function asyncCall(){
const result = await getter();
res.json(result);
}
asyncCall();
};
Using Aync/Await and Promise all
export default async (req, res) => {
const promises = [];
let result = null;
const petlist = await new Promise((resolve, reject) => {
User.findOne({ _id: req.params._id }, (err, users) => {
if (err) {
reject(err);
} else {
resolve(users.pets);
}
});
});
if (petlist && petlist.length) {
for (let i = 0; i < petlist.length; i++) {
// eslint-disable-next-line no-loop-func
const promise = new Promise((resolve, reject) => {
Pet.findOne({ _id: petlist[i] }, (err, pet) => {
if (err) {
reject(err);
} else {
resolve(pet);
}
});
});
promises.push(promise);
}
result = await Promise.all(promises).then((data) => {
console.log('all promises resolved!');
console.log(data);
return data;
});
}
console.log(result);
};
You can implement promises like this in your code:
export const getPetsForAUser = (req, res) => {
return new Promise((resolve, reject) =>{
User.findOne({_id: req.params._id}, (err, users) => {
if (err) reject(err);
let petlist = users.pets;
for(var i = 0; i < petlist.length; i++) {
Pet.findOne({_id:petlist[i]}, (err, pet) =>{
if (err) reject(err);
var t = pet
resolve(t)
});
}
})

How to use callback in .then() function in Nodejs?

I have nodejs module to fetch data from mongodb database using mongodb driver. Callback is passed to given function which return a promise, but instead of returning result in .then() function, it is passing value to callback function. How can I call this function from other module or function since it is not returning it in .then()? I tried to console the result of .then(), but it is showing undefined.
const MongoClient = require('mongodb').MongoClient;
const Db = require('../model/db');
Db.findUser = (details, callback) => {
return dbconnection().then(db => {
if (db) {
return db.collection('users').findOne({
email: details.email,
pass: details.password
}).then(data => {
if (data) {
console.log('Found one');
callback(true);
} else {
let err = new Error();
callback(err);
}
})
}
I have used following function to call the promise. I am new to promises.
var getUser = function(callback) {
db.findUser().then(result => {
console.log(result) // undefined
})
}
You can easily do it using async/await. Something like this:
Db.findUser = async (details, callback) => {
const db = await dbconnection();
const data = await db.collection('users').findOne({
email: details.email,
pass: details.password
});
if (data) {
console.log('Found one');
callback(true);
} else {
let err = new Error();
callback(err);
}
return data;
}
and consume it like:
const getUser = async (details, callback) => {
const data = await Db.findUser();
// do whatever you need with data
return data;
}

While call rest api in Javascript (NodeJS) and return response Undefined

When i call rest API and return response it show undefined but i console.log this response it return
var request = require("request");
function initialize() {
// Setting URL and headers for request
var options = {
url: 'http://postalpincode.in/api/pincode/400605',
json: true
};
// Return new promise
return new Promise(function (resolve, reject) {
// Do async job
request.get(options, function (err, resp, body) {
if (err) {
reject(err);
} else {
resolve(JSON.stringify(body));
}
})
})
}
function main() {
var initializePromise = initialize();
initializePromise.then(function (result) {
return result;
})
}
console.log('', main())
But when i console log this response it show output correct
var request = require("request");
function initialize() {
// Setting URL and headers for request
var options = {
url: 'http://postalpincode.in/api/pincode/400605',
json: true
};
// Return new promise
return new Promise(function (resolve, reject) {
// Do async job
request.get(options, function (err, resp, body) {
if (err) {
reject(err);
} else {
resolve(JSON.stringify(body));
}
})
})
}
function main() {
var initializePromise = initialize();
initializePromise.then(function (result) {
console.log('', result)
})
}
console.log('', main())
I want When i call rest API and return response it show correct output
The return inside the thenscope is not returning for the function main, but only for the thenscope of the promise. You need to return the promise like so:
function main() {
var initializePromise = initialize();
return initializePromise.then(function (result) {
return result;
})
}
main().then((result) => console.log('',result));
you can't make a sync function call an async method and expect to have get its result.
use async/await
async function main() {
var initializePromise = await initialize();
console.log(initializePromise)
}
My question is, why are you wrapping in a new Promise something that's already from a return type of Promise?
You could just do:
request.get(endpoint, options).then((response) => console.log(response)).catch((error) => console.log(error));
Let me know what's the output in that case.
The then resolution of initializePromise method resolves at a later stage when a response is fetched from REST HTTP call i.e. it does not get returned when you call main() method due to the fact it is async. To handle such code, you should either use a callback
function main(completionHandler) {
var initializePromise = initialize();
initializePromise.then(function (result) {
completionHandler(result);
})
}
main((result) => { console.log(result)})
or a promise
function main() {
// Return new promise
return new Promise(resolve => {
var initializePromise = initialize();
initializePromise.then(function (result) {
resolve(result);
})
}
}
main().then(result => console.log(result));
return new Promise(function (resolve, reject) {
// Do async job
request.get(options, function (err, resp, body) {
if (err) {
reject(err);
} else {
try {
resolve(JSON.stringify(body));
} catch(e) {
reject(e);
}
}
})
})
in main function:
function main() {
initialize().then((result) => {
console.log(result);
return result;
}).catch((err) => {
console.log(err);
return err;
})
}

Javascript how the better way to code nested callback?

I have 3 layer callbacks like this :
app.post('/', (req, res) => {
var filename = `outputs/${Date.now()}_output.json`;
let trainInput = req.files.trainInput;
let trainOutput = req.files.trainInput;
let testInput = req.files.trainInput;
//first
trainInput.mv(`inputs/${req.body.caseName}/train_input.csv`, function (err) {
if (err) return res.status(500).send(err);
//second
trainOutput.mv(`inputs/${req.body.caseName}/train_output.csv`, function (err) {
if (err) return res.status(500).send(err);
//third
testInput.mv(`inputs/${req.body.caseName}/test_input.csv`, function (err) {
if (err) return res.status(500).send(err);
res.send('success');
});
});
});
});
In this case, there are only 3 file uploads. In another case, I have more than 10 file uploads, and it makes 10 layer callbacks. I know it because of JavaScript asynchronous.
Is there any way, with this case, to make a beautiful code? This is because when it 10 layer callbacks, the code looks horizontally weird.
Thanks
You can use the following code to make you code look better and avoid callback hell
app.post('/', async (req, res) => {
var filename = `outputs/${Date.now()}_output.json`;
let trainInput = req.files.trainInput;
let trainOutput = req.files.trainInput;
let testInput = req.files.trainInput;
try {
var result1 = await trainInput.mv(`inputs/${req.body.caseName}/train_input.csv`);
var result2 = await trainInput.mv(`inputs/${req.body.caseName}/train_output.csv`);
var result2 = await testInput.mv(`inputs/${req.body.caseName}/test_input.csv`);
res.send('success');
}
catch (error) {
res.status(500).send(error);
}
});
You can make the functions return a Promise
I advice to make one function because you do the same thing 3 times. In this case I called the function 'save' but you can call it what ever you want. The first parameter is the file end the second the output filename.
function save(file, output) = return new Promise((resolve, reject) => {
file.mv(`inputs/${req.body.caseName}/${output}`, err =>
if (err) return reject(err)
resolve()
})
Promise.all([
save(req.files.trainInput, 'train_input.csv'),
save(req.files.trainInput, 'train_output.csv'),
save(req.files.trainInput, 'test_input.csv')
])
.then(_ => res.send(200))
.catch(err => res.send(400);
What version of Node you using? If async/await is available that cleans it up a bunch.
const moveCsv = (file, dest) => {
return new Promise((resolve, reject) => {
//third
file.mv(dest, function (err) {
if (err) reject(err);
resolve();
});
})
}
app.post('/', async(req, res) => {
try {
var filename = `outputs/${Date.now()}_output.json`;
const {
trainInput,
trainOutput,
testInput
} = req.files;
const prefix = `inputs/${req.body.caseName}`;
await moveCsv(trainInput, `${prefix}/train_input.csv`);
await moveCsv(trainOutput, `${prefix}/train_output.csv`);
await moveCsv(testInput, `${prefix}/test_input.csv`);
res.send('success');
} catch(err) {
res.status(500).send(err);
}
});
I'm also assuming here that your trainInput, trainOutput, testOutput weren't all meant to be req.files.trainInput.
Just be careful since the synchronous nature of the await calls are thread blocking. If that writer function takes ages you could also looking at putting those calls onto a worker thread. Won't really matter if your requests to that server endpoint are fast and non-frequent.
You can add RXJS to your project and use Observables.forkJoin()
Solution with Observables(assuming that trainInput.mv() returns Observable):
/* Without a selector */
var source = Rx.Observable.forkJoin(
trainInput.mv(`inputs/${req.body.caseName}/train_input.csv`),
trainInput.mv(`inputs/${req.body.caseName}/train_output.csv`),
trainInput.mv(`inputs/${req.body.caseName}/test_input.csv`)
);
var subscription = source.subscribe(
function (x) {
// On success callback
console.log('Success: %s', x);
},
function (err) {
// Error callback
console.log('Error');
},
function () {
// Completed - runs always
console.log('Completed');
});
// => Success: [result_1, result_2, result_3] or Error
// => Completed

Post request to wait a function

I know the question is asked really often, and I might get downvoted for that. But I really struggle to understand how I can wait for a function to process data before returning a value.
I had a look on many popular posts (such as here), but I cannot achieve what I want.
Here is my code:
app.post("/video_url", function (req, res) {
videoProcessed = videoScraper(req.body.videoURL.videoURL);
res.send(videoProcessed);
});
It does not wait for this function to process the data:
function videoScraper(url) {
console.log("URL to Scraper: " + url);
const options = {
uri: `${url}`,
transform: function(body) {
return cheerio.load(body);
}
};
var videoProcessed;
rp(options)
.then(($) => {
videoProcessed = $("body").find("iframe").attr("src");
return videoProcessed;
})
.catch((err) => {
console.log(err);
});
}
I tried using callbacks but it gets really messy, and I don't know were to put the promise (if any) in my code.
Add await and async (if you have node 8+):
app.post("/video_url", async function (req, res) {
const videoProcessed = await videoScraper(req.body.videoURL.videoURL);
res.send(videoProcessed);
});
And in your videoScraper function, you need to return rp! :
function videoScraper(url) {
console.log("URL to Scraper: " + url);
const options = {
uri: `${url}`,
transform: function(body) {
return cheerio.load(body);
}
};
return rp(options)
.then($ => $("body").find("iframe").attr("src"))
.catch((err) => {
console.error(err);
});
}
That would depend on the videoScrapper working fine, I've no idea what rp is so I can't tell.
Don't forget to handle videoProcessed === undefined (error case) in the first code snippet. It can also be abstracted using express-promise-router that will even catch async errors... That's further down the road.
Don't hesitate to read up on await & async, it's really wonderful to write asynchronous code in the same manner as synchronous code.
Use async/await
app.post("/video_url", async (req, res)=> {
try{
let videoProcessed = await videoScraper(req.body.videoURL.videoURL);
res.send(videoProcessed);
}
catch(ex){
// handle the exception
}
});
const videoScraper = async fuction(url) {
console.log("URL to Scraper: " + url);
let options = {
uri: `${url}`,
transform: function(body) {
return cheerio.load(body);
}
};
try{
let temp = await rp(options);
let videoProcessed = $("body").find("iframe").attr("src");// you can return it directly
return videoProcessed;
}
catch(ex){
// handle the exception
}
}
if you your node is < 8 then use promises (bluebird module)
const bluebird = require('bluebird');
function videoScraper(url){
return new bluebird(function(resolve,reject){
let options = {
uri: `${url}`,
transform: function(body) {
return cheerio.load(body);
}
};
rp(options)
.then(($)=>{
resolve($("body").find("iframe").attr("src"));
})
.catch(err=>{
return err;
})
})
}
app.post("/video_url", (req, res)=> {
videoScraper(req.body.videoURL.videoURL)
.then(result=>{
res.send(result)
})
.catch(err=>{
// handle the error
})
});
Do not use const for variable declaration unless its value is constant, and usually use let instead of var
You can try the following:
app.post("/video_url", function (req, res) {
videoScraper(req.body.videoURL.videoURL)
.then(videoProcessed => {
res.send(videoProcessed);
})
.catch(err => {
// render proper response with error message
})
});
And change the function to something as below, so as to return a promise from the same:
function videoScraper(url) {
console.log("URL to Scraper: " + url);
const options = {
uri: `${url}`,
transform: function(body) {
return cheerio.load(body);
}
};
return rp(options)
.then(($) => {
videoProcessed = $("body").find("iframe").attr("src");
return videoProcessed;
});
}

Categories