I'm trying to merge the data from two JSON files (customer list) and then, create as many task as customers there are to be executed later via async.js
In order to have all the tasks array full, I'm controlling the end of the forEach with a promise:
var parsePromise = new Promise(function (resolve, reject) {
mongoDB.MongoClient.connect(dbURL, (error, db) => {
originalData.forEach(function (element, index) {
var restoredCustomer = Object.assign(element, backupData[index]);
tasksArray.push((function (db, element) {
return function () {
db.collection('customers').insert(element, function (error) {
if (error) {
console.error("Error: ", error);
}
})
}
})(db, restoredCustomer));
forEachCounter--;
if (forEachCounter === 0) {
resolve(tasksArray);
}
});
});
});
Then, when the promise ends, I execute the async.js parallel method:
parsePromise.then(function (tasksArray) {
async.parallel(tasksArray, (err, results) => {
console.log("Operation completed successfully: ", results);
})
})
The weird thing is that the code is working fine, and the inserts are being done on MongoDB, however, the console.log("Operation completed successfully: ", results); placed in the parallel callback is never shown.
Try this
var parsePromise = new Promise(function (resolve, reject) {
mongoDB.MongoClient.connect(dbURL, (error, db) => {
originalData.forEach(function (element, index) {
var restoredCustomer = Object.assign(element, backupData[index]);
tasksArray.push(function(cb){
db.collection('customers').insert(restoredCustomer, function (error) {
if (error) return cb(err);
cb();
})
});
forEachCounter--;
if (forEachCounter === 0) {
resolve(tasksArray);
}
});
});
});
According to async js documentation
https://caolan.github.io/async/global.html
tasks arguments in parallel function in async.js must be AsyncFunction.
AsyncFunction requires to have the final parameter which is a callback. The structure of the callback follows the standard of function(err, args).
Also you can try simplifying your code to:
new Promise((resolve, reject) => {
mongoDB.MongoClient.connect(dbURL, (error, db) => {
if (error) return reject(error);
resolve(db);
});
}).then(db => {
var tasksArray = [];
for (let i = 0; i != originalData.length; ++i){
var restoredCustomer = Object.assign(originalData[i], backupData[index]);
tasksArray.push(function(cb){
db.collection('customers').insert(restoredCustomer, function (error) {
if (error) return cb(err);
cb();
})
});
}
return new Promise((resolve, reject) => {
async.parallel(tasksArray, (err, results) => {
if (err) return reject(err);
resolve(results)
})
})
})
Related
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);
})()
Unable to resolve or come out of inner promise so I can send the data back to client side.
Code:
function getAmazonData(url, isRatings) {
var parsedData;
var ItemLookup;
var ratingsURL;
return new Promise(function (resolve, reject) {
request(url, function (err, response, body) {
if (err) return reject(err);
parseXML(body, function (err, parsedData) {
if (err) return reject(err);
ItemLookup = parsedData['ItemLookupResponse'];
if (ItemLookup && ItemLookup.Items && ItemLookup.Items.length > 0) {
// Request Error
if (ItemLookup.Items[0].Request && ItemLookup.Items[0].Request.length > 0 && ItemLookup.Items[0].Request[0].Errors) {
return reject(ItemLookup.Items[0].Request[0].Errors);
} else if (ItemLookup.Items[0].Item) {
ratingsURL = ItemLookup.Items[0].Item[0].CustomerReviews[0].IFrameURL[0];
if (isRatings) {
console.log('getting ratings...');
return new Promise(function (resolve2, reject2) {
request(ratingsURL, { json: true }, function (ratingsError, ratingBody) {
if (ratingsError) {
return reject2('Error in getting ratings');
}
ItemLookup.Items[0].Item[0].CustomerReviews.push({ Ratings: ratingBody });
resolve2(ItemLookup.Items[0].Item[0].CustomerReviews);
});
});
}
console.log('Resolved: ', ItemLookup.Items[0].Item);
resolve(ItemLookup.Items[0].Item);
}
}
});
});
});
}
amazon_router.get('/getDetails', function (req, res, next) {
var amazonApi = decodeURIComponent(req.query.url);
var amzonApiUrl = amazonApi.replace(/\+/g, '%2B');
var isRatings = decodeURIComponent(req.query.ratings);
console.log('');
console.log('amazon api url ', amzonApiUrl);
console.log('');
getAmazonData(amzonApiUrl, isRatings).then(function (err, response) {
if (err) res.send(err);
res.send(response);
});
});
The data is never sent after resolving promise here at LINE 10: res.send(response); There's something wrong with inner promise but I am not sure not do I fix it?
It looks like you are mixing callbacks and Promises. I would recommend wrapping all calls to Promises.
From the looks of it you don't need inner Promise because request expects a callback so you can easily resolve the outer one.
You also have some branches of if statements that don't call resolve or reject (marked in the code bellow with comments). You have to call resolve/reject in all branches otherwise the Promise 'might do nothing' in case it hits the branch.
function getAmazonData(url, isRatings) {
var parsedData;
var ItemLookup;
var ratingsURL;
return new Promise(function (resolve, reject) {
request(url, function (err, response, body) {
if (err) return reject(err);
parseXML(body, function (err, parsedData) {
if (err) return reject(err);
ItemLookup = parsedData['ItemLookupResponse'];
if (ItemLookup && ItemLookup.Items && ItemLookup.Items.length > 0) {
// Request Error
if (ItemLookup.Items[0].Request && ItemLookup.Items[0].Request.length > 0 && ItemLookup.Items[0].Request[0].Errors) {
reject(ItemLookup.Items[0].Request[0].Errors);
} else if (ItemLookup.Items[0].Item) {
ratingsURL = ItemLookup.Items[0].Item[0].CustomerReviews[0].IFrameURL[0];
if (isRatings) {
console.log('getting ratings...');
request(ratingsURL, { json: true }, function (ratingsError, ratingBody) {
if (ratingsError) {
reject(new Error('Error in getting ratings'));
return;
}
ItemLookup.Items[0].Item[0].CustomerReviews.push({ Ratings: ratingBody });
resolve(ItemLookup.Items[0].Item[0].CustomerReviews);
});
} else {
console.log('Resolved: ', ItemLookup.Items[0].Item);
resolve(ItemLookup.Items[0].Item);
}
} else {
// resolve or reject here
}
} else {
// resolve or reject here
}
});
});
});
}
Wrapping call to Promise:
function requestAsPromised(url) {
return new Promise(function (resolve, reject) {
request(url, function (err, response, body) {
if (err) {
reject(err);
} else {
resolve(body)
}
});
})
}
You need to use return to return the inner promise(s),
function getAmazonData(url, isRatings) {
var parsedData;
var ItemLookup;
var ratingsURL;
return new Promise(function (resolve, reject) {
return request(url, function (err, response, body) {
if (err) return reject(err);
return parseXML(body, function (err, parsedData) {
if (err) return reject(err);
ItemLookup = parsedData['ItemLookupResponse'];
if (ItemLookup && ItemLookup.Items && ItemLookup.Items.length > 0) {
// Request Error
if (ItemLookup.Items[0].Request && ItemLookup.Items[0].Request.length > 0 && ItemLookup.Items[0].Request[0].Errors) {
return reject(ItemLookup.Items[0].Request[0].Errors);
} else if (ItemLookup.Items[0].Item) {
ratingsURL = ItemLookup.Items[0].Item[0].CustomerReviews[0].IFrameURL[0];
if (isRatings) {
console.log('getting ratings...');
return new Promise(function (resolve2, reject2) {
request(ratingsURL, { json: true }, function (ratingsError, ratingBody) {
if (ratingsError) {
return reject2('Error in getting ratings');
}
ItemLookup.Items[0].Item[0].CustomerReviews.push({ Ratings: ratingBody });
resolve2(ItemLookup.Items[0].Item[0].CustomerReviews);
});
});
}
console.log('Resolved: ', ItemLookup.Items[0].Item);
return resolve(ItemLookup.Items[0].Item);
}
}
});
});
});
}
Since it looks like Node.js code, try using async/await
I have the question, below code:
The problem is:
How can I send each line from response promiseGetCitiesData to promiseGetInformationDataPerCity.
Can I do it in one async.each functions?
Now, I created multiple Promise functions. One general function, which one start the program - getDataAndCloseDb().
Also I used async.each to call promise function with array parameter - locationArray.
Now, I would like to send each line from json response to next promise function (create get url), and collect the general response.
const MongoClient = require("mongodb").MongoClient;
const request = require("request");
const async = require("async");
var locationsArray = [
'location1',
'location2',
'location3'
];
function promiseConnectToDatabase(urldb) {
return new Promise(function(resolve, reject) {
MongoClient.connect(urldb, (err, db) => {
if (err) {
console.log("MongoDb connection error.");
reject(err);
}
console.log("Connected to MongoDb.");
resolve(db);
});
});
}
function promiseGetCitiesData(location) {
return new Promise(function(resolve, reject) {
request({
url: `https://example.com/${location}`,
json: true
}, (error, response, body) => {
if (error) {
console.log("Error connection to url.");
reject();
}
console.log("location: " + location);
console.log({location: location, cities: body.result.cities});
resolve({location: location, cities: body.result.cities});
});
});
}
/*
Example response from promiseGetCitiesData:
Location: location1
{ location: 'location1',
cities:
[ 'information1',
'information2',
'information3',
'information4'' ] }
*/
function promiseGetInformationDataPerCity(location, cities) {
return new Promise(function(resolve, reject) {
request({
url: `https://example.com/${location}/${cities}`,
//f.e https://example.com/location1/information1 etc.
json: true
}, (error, response, information) => {
if (error) {
console.log("Error connection to url.");
reject();
}
console.log(information);
resolve(information);
});
});
}
function promiseSaveDataToDatabase(db, body) {
return new Promise(function(resolve, reject) {
db.collection("testlocation").insert(body, function(dbError) {
if (dbError) {
reject(dbError);
}
resolve()
});
});
}
function promiseDisconnectDatabase(db) {
return new Promise(function(resolve, reject) {
db.close((err) => {
if (err) {
console.log("MongoDb disconnect error.");
reject(err);
}
console.log("MongoDb disconnected.");
resolve();
});
});
}
function promiseProvideDataFromEach(locationsArray, db) {
return new Promise(function(resolve, reject) {
async.each(locationsArray, function(loc, locProcessedCb) {
promiseGetcitiesData(loc).then(function(resultscities) {
promiseGetInformationDataPerCity(loc, resultscities).then(function(resultDetails) {
promiseSaveDataToDatabase(db, resultDetails).then(function() {});
locProcessedCb();
});
});
}, function(err) {
if (err) {
locProcessedCb(err);
reject(err);
}
console.log("All locations have been processed.");
resolve();
});
});
}
function getDataAndCloseDb() {
return new Promise(function(resolve, reject) {
promiseConnectToDatabase("mongodb://127.0.0.1:27017/testApp").then(function(db) {
promiseProvideDataFromEach(locationsArray, db).then(function() {
promiseDisconnectDatabase(db).then(function() {});
});
});
});
}
getDataAndCloseDb();
I think this is a lot simpler than the code in the question makes it appear. In particular, new Promise(...) can be completely avoided by :
using require('async-request') instead of require('request').
allowing MongoDb methods to return Promise, as many of them will do if no callback is passed.
Also
by using the Promise.all(array.map(...)) pattern the need for require('async') disappears.
https://stackoverflow.com/a/28915678/3478010 - provides a great little reusable disposer utility, which is useful here.
Remember to return a promise/value from every .then() callback that is itself asynchronous and/or should deliver data.
With some guesswork, I think you want something like this :
const MongoClient = require('mongodb').MongoClient;
const request = require('async-request'); // just like `request()` but returns a promise
var locationsArray = [
'location1',
'location2',
'location3'
];
function promiseGetCitiesData(loc) {
return request({
url: `https://example.com/${loc}`,
json: true
}).then(body => body.result.cities);
}
function promiseGetInformationDataPerCity(loc, cities) {
return Promise.all(cities.map(city => {
return request({
'url': `https://example.com/${loc}/${city}`,
'json': true
}).then(cityInfo => ({ 'name':city, 'info':cityInfo }));
}));
}
function promiseProvideDataFromEach(locationsArray, db) {
return Promise.all(locationsArray.map(loc => {
return promiseGetCitiesData(loc)
.then(cities => promiseGetInformationDataPerCity(loc, cities)
.then(citiesWithCityInfo => ({ 'location':loc, 'cities':citiesWithCityInfo }));
}))
.then(resultDetails => db.collection('testlocation').insertMany(resultDetails));
}
// disposer utility - credit: https://stackoverflow.com/a/28915678/3478010
function withDb(work) {
var _db;
return MongoClient.connect("mongodb://127.0.0.1:27017/testApp")
.then((db) => {
_db = db; // keep reference
return work(db); // perform work on db
}).finally(() => {
if (_db)
_db.close();
});
}
withDb(db => promiseProvideDataFromEach(locationsArray, db))
.then(() => {
// connection released here
});
The guesswork centres mainly around what is to be inserted at db.collection('testlocation').insertMany(resultDetails). The code in the question gives no more than a clue. My attempt seems reasonable but may not be exactly what you want. Be prepared to make some changes in promiseProvideDataFromEach() and promiseGetInformationDataPerCity().
you can do something like this. Its a simpler code but I think you can map it to your current code.
const Promise = require('bluebird')
const cities = ['citya', 'cityb', 'cityc']
function resolveCities() {
return new Promise(function(resolve, reject) {
resolve(cities)
})
}
function logCity(city) {
console.log('city ', city)
}
return resolveCities()
.then(function(cities) {
return Promise.mapSeries(cities, function(city) {
logCity(city);
});
})
I want to execute four blocks of code sequentially and conduct test during this sequential execution. The challenge is that the blocks contain calls of asynchronous functions.
I cannot seem to wrap my head around promises to use them properly and I just seem stuck after spending several hours on the block of code.
// Store current log of drone visits for DJI Phantom 4
db.query("SELECT COUNT(drone_id) FROM Drone_Visits WHERE drone_id = 2;", function(err, rows) {
if (err) {
console.log(err);
}
else {
current_drone_visits = rows[0]['COUNT(drone_id)'];
}
});
it("should return status 200 (OK) when requesting route for this function", function(done) {
request.get("/product/DJI/Phantom_4").query({brand: dji_brand, model: dji_model}).end(function(err, res) {
assert.equal(res.status, 200);
done();
});
});
db.query("SELECT COUNT(drone_id) FROM Drone_Visits WHERE drone_id = 2;", function(err, rows) {
if (err) {
console.log(err);
}
else {
updated_drone_visits = rows[0]['COUNT(drone_id)'];
}
});
it("should increment the value of drones visited in the database", function(done) {
console.log("A - " + current_drone_visits);
console.log("B - " + updated_drone_visits);
assert.equal(current_drone_visits + 1, updated_drone_visits);
done();
});
What should I do here if I want to chain my callbacks such that they execute only after the previous function has finished?
Use async.js if you are comfortable with callbacks, or use Promises, you can chain them in order to synchronise you functions.
just wrap your callback in a function that return a promise like this
function P(){
return new Promise((resolve, reject)=>{
db.query("SELECT COUNT(drone_id) FROM Drone_Visits WHERE drone_id = 2;", function(err, rows) {
if (err) reject(err);
else {
resolve(rows[0]['COUNT(drone_id)'])
}
});
})
}
function T1(data){
//data in this case will be the result of the P() function
return new Promise((resolve, reject)=>{
request.get("/product/DJI/Phantom_4").query({brand: dji_brand, model: dji_model}).end(function(err, res) {
if(err || res.status==200) reject(err);
resolve();
});
})
}
P()
.then(T1)
.then(...)
.catch((err)=>{
//handle error here
})
sry for the bad formatting
What should I do here if I want to chain my callbacks such that they
execute only after the previous function has finished.
This code does it for you:
describe ('test', function () {
this.timeout(6000);
it ('test', function(done) {
var query1 = new Promise (function (resolve, reject) {
db.query("SELECT COUNT(drone_id) FROM Drone_Visits WHERE drone_id = 2;", function(err, rows) {
if (err) {
reject(new Error('rejected query1'));
} else {
var current_drone_visits = rows[0]['COUNT(drone_id)'];
resolve(current_drone_visits);
}
});
})
.then(function(current_drone_visits){
var request1 = new Promise (function (resolve, reject) {
request.get("/product/DJI/Phantom_4").query({brand: dji_brand, model: dji_model}).end(function(err, res) {
if (err) {
reject(new Error('rejected request'));
} else {
resolve(res);
}
})
})
.then(function (res) {
try {
assert.equal(res.status, 200);
return current_drone_visits;
}
catch (err) {
done (err);
}
})
.catch(function(err) {
return Promise.reject(err);
})
return request;
})
.then(function(current_drone_visits) {
var query2 = new Promise (function (resolve, reject) {
db.query("SELECT COUNT(drone_id) FROM Drone_Visits WHERE drone_id = 2;", function(err, rows) {
if (err) {
reject(new Error('rejected query2'))
} else {
resolve();
}
})
})
.then(function () {
var updated_drone_visits = rows[0]['COUNT(drone_id)'];
var bundled = [current_drone_visits, updated_drone_visits];
return bundled;
})
.catch(function(err) {
return Promise.reject(err);
})
return query2;
})
.then(function(bundled) {
var current_drone_visits = bundled[0];
var updated_drone_visits = bundled[1];
console.log('A - ' + current_drone_visits);
console.log('B - ' + updated_drone_visits);
assert.equal(current_drone_visits + 1, updated_drone_visits);
done()
})
.catch (function (err) {
done(err);
})
})
})
You can use the this.timeout(6000) to determine how long Mocha will wait for your asynchronous code to complete. The default value is 2000 ms which may or may not be sufficient in your case.
after uploading image from client as Base64 to server i want to
save file to disk
get result of save (first chain) and pass it to next chain
check result on next function on that(), if its true, update database and return result
check result and print
this below code is my implementing chain, but that seems is not correct, because i cant get result from first chain as savePhotoOnDisk
savePhotoOnDisk(filenameWithPath)
.then(updateUserPhoto(result, userId, filename))
.then(FUNCTION(GET RESULT)
.then(OTHER FUNCTION() {
PRINT RESULT
});
or this chain:
savePhotoOnDisk(filenameWithPath)
.then(function(result){
updateUserPhoto(result, userId, filename);
})
.then(function (result) {
OTHER FUNCTION
})
.then(function (result) {
PRINT RESULT
})
.catch(function (v) {
log.info('error chaining');
});
function updateUserPhoto(result, userId, filename) {
log.info(result);
return new Promise(function (resolve, reject) {
var query = "UPDATE users SET userPhoto = ? WHERE userId = ? ;";
connection.query(query, [filename, userId], function (err, results) {
if (err) return reject(false);
if (results.length === 0) return reject(false);
resolve(true);
});
});
}
function savePhotoOnDisk(filename) {
return new Promise(function (resolve, reject) {
require("fs").writeFile(filename, base64Data, 'base64', function (err) {
log.info(err + "upload");
if (err == null)
resolve(true);
else
reject(false);
});
});
}
I'm newbie to use this nodejs feature, please help to fix that, Thanks
UPDATE
savePhotoOnDisk(filenameWithPath)
.then(function (result) {
return {success:result};
})
.then(updateUserPhoto(success, userId, filename),function (result) {
console.log(result);
})
.catch(function (v) {
log.info('error chaining');
});
You are calling promise inside promise which is the issue. Try changing the code to something like this.
savePhotoOnDisk(filenameWithPath)
.then(function () {
return {
userId : userId,
fileName : fileName
}
})
.then(updateUserPhoto)
.then(FUNCTION(GET RESULT)
.then(OTHER FUNCTION() {
PRINT RESULT
});
function updateUserPhoto(data) {
var userId = data.userId;
var fileName = data.fileName;
// call your function as it was earlier.
}
EDIT
savePhotoOnDisk(filenameWithPath)
.then(updateUserPhoto.bind(true, userId, filename))
.then(function (result) {
OTHER FUNCTION
})
.then(function (result) {
PRINT RESULT
})
.catch(function (v) {
log.info('error chaining');
});
function updateUserPhoto(result, userId, filename) {
log.info(result);
return new Promise(function (resolve, reject) {
var query = "UPDATE users SET userPhoto = ? WHERE userId = ? ;";
connection.query(query, [filename, userId], function (err, results) {
if (err) return reject(false);
if (results.length === 0) return reject(false);
resolve(true);
});
});
}
function savePhotoOnDisk(filename) {
return new Promise(function (resolve, reject) {
require("fs").writeFile(filename, base64Data, 'base64', function (err) {
log.info(err + "upload");
if (err == null)
resolve(true);
else
reject(false);
});
});
}
Problem Resolved
this is my correct code to create simple chain and pass result to next method as Promise
I hope to helpful this post
savePhotoOnDisk(filenameWithPath, photoBase64).then(function (result) {
return updateUserPhoto(result, userId, filename);
}).then(function (save) {
if (save)
socket.emit('uploadUserPhoto', true);
else
socket.emit('uploadUserPhoto', false);
}).catch(function (v) {
socket.emit('uploadUserPhoto', false);
});