I would like to make an Ajax request to my MongoDB server and use the data along with other async tasks using a standalone function so that I can modularize my code as much as possible. I am not very experienced with async programming so I might be doing some basic mistake. In my code, I used another function (doubleAfter2Seconds) returning a promise, which works fine. The result variable from await getMongoData("mydb", url) is outputted as undefined instead of the actual data.
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://{MyServer}:27017/";
function getMongoData(dboArg, urlArg) {
var myPromise = () => {
return new Promise(resolve => {
MongoClient.connect(urlArg, { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
var dbo = db.db(dboArg);
dbo.collection(myCollection).find({}).toArray(function(err, result) {
if (err) throw err;
console.log(result);
db.close();
resolve(result[0]);
});
})
})
}
}
function doubleAfter2Seconds(x) {
return new Promise(resolve => {
console.log("v");
setTimeout(() => {
resolve(x * 2);
}, 1000);
});
}
async function addAsync(x) {
// This works
const a = await doubleAfter2Seconds(10);
console.log(a);
// This doesn't work
result = await getMongoData("mydb", url);
console.log(result);
return x;
}
addAsync(10).then((sum) => {
console.log(sum);
});
Here is the corrected getMongoData function based on the comments
function getMongoData(dboArg, urlArg) {
return new Promise(resolve => {
MongoClient.connect(urlArg, { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
var dbo = db.db(dboArg);
dbo.collection(myCollection).find({}).toArray(function(err, data) {
err
? reject(err)
: resolve(data[0]);
});
})
})
}
Related
This question already has answers here:
How do I convert an existing callback API to promises?
(24 answers)
Closed 9 months ago.
async function bringData() {
return new Promise(resolve => {
setTimeout(() => {
resolve(con.query('SELECT company, industry FROM my_table', function (err, result) {
if (err) {
console.error(err);
throw 'Error getting data from google_businesses table';
} else {
return result;
}
}));
}, 1000);
});
}
(async () => {
// return data from brindData function
let getData = await bringData();
console.log(getData);
})();
I am calling this function in main but I am always getting error. I am kind of new to node so if please anybody can guide me or is there any other way to do this such that when I have got my result I can return it.
Call resolve with the result from your query.The con.query looks like another asynchronous thing:
con.query('...', (err, result) => {
if (!err)
resolve(result);
});
You can try this.
async function bringData() {
return new Promise((resolve, reject) => {
con.query('SELECT company, industry FROM my_table', (err, result) => {
if (err) {
reject(err);
}
resolve(result);
})
});
}
(async () => {
// execute bringData function
try {
let getData = await bringData();
console.log(getData);
} catch (e) {
console.error(e);
}
})();
Edited with timeout
async function bringData() {
return new Promise((resolve, reject) => {
con.query('SELECT company, industry FROM my_table', (err, result) => {
if (err) {
reject(err);
}
resolve(result);
})
});
}
function timeout(seconds = 10) {
return new Promise(resolve => {
setTimeout(resolve, 10 * 1000)
})
}
(async () => {
// execute bringData function
try {
await timeout();
let getData = await bringData();
console.log(getData);
} catch (e) {
console.error(e);
}
})();
I am trying to get the result back from function where the result is in the callback. However the result always come back as undefined even though I set it to be the res. Can someone explain why this does not work?
function getData(id, callback) {
return dataModel.findOne({ id }).exec((err, res) => {
if (err) return callback(err)
return callback(null, res)
})
}
async function middleware(req.body) {
try {
let result
await getData(req.body, (err, res) => {
if (err) throw err
result = res
})
await nextFunc(result)... // result is undefined
} catch (err) {
console.log(err)
}
}
You are using callbacks, but in order to make things work with async await you are supposed to use promises
Try
function getData(id, callback) {
return new Promise((resolve, reject) => {
dataModel.findOne({ id }).exec((err, res) => {
if (err) reject(err);
resolve(res);
});
});
}
async function middleware(req.body) {
try {
let result = await getData(req.body);
await nextFunc(result);
} catch (err) {
console.log(err);
}
}
i am trying to pass the sql query parameters to data access js file. i have imported the function in current file, but still am getting the below error.
current file
const tcount = async (value) => {
const sql = 'trainingcount';
const result = await query.findAll(sql);
return result;
}
data access file
const query = (results) => {
findAll: async (sql, result) => {
connection.query(`SELECT * FROM trainingcount`, (err, rows) => {
if (err) {
return results(null, err);
} else {
return results(rows);
}
});
};
};
export { query };
(node:11132) UnhandledPromiseRejectionWarning: TypeError:
query.findAll is not a function
EDIT: Check #rid solutions on the comments for the specific problem of calling the proper function. My answer solves a different problem in OP code.
you call return inside the callback function, so you are returning THAT function, not findAll. You need to return a Promise:
const query = (results) => {
findAll: (sql, result) => {
return new Promise((resolve, reject) => {
connection.query(`SELECT * FROM trainingcount`, (err, rows) => {
if (err) {
reject(err);
} else {
resolve(rows);
}
});
});
};
};
export { query };
I'm trying to return the result from this function :
var YouTube = require('youtube-node');
var youTube = new YouTube();
youTube.setKey('XXXXXXXXXXXX');
var result = youTube.search('World War z Trailer', 2,
function(error, result) {
if (error) {
console.log(error);
} else {
return result
}})
console.log(result)
But then I only get undefined as a result in console.
You need to wait for the response.
This simple answer is to place the console.log(result) inside the callback.
Or you can wrap the search method in a promise:
var promise = new Promise((resolve, reject) => {
youTube.search('World War z Trailer', 2, function (error, result) {
if (error) {
reject(error);
}
else {
resolve(result);
}
})
});
Then you can wait for the response using .then() like this:
promise.then((result) => {
console.log(result);
}).catch((err) => {
console.error(err);
});
Or using await/async:
(async () => {
try {
const result = await promise;
console.log(result);
} catch (err) {
console.error(err)
}
})();
Last function here is a callback function, it doesn't return value in way you want. Read how it works somewhere, for example here.
Your issue could be resolved with Promise in this way
var search = new Promise(
(resolve, reject) => {
youTube.search('World War z Trailer', 2, function(error, result) {
if (error) { reject(error); }
resolve(result);
})
}
);
(async () => {
var result = await search;
console.log(result);
})()
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