Nested promise in a for loop not behaving as expected - javascript

I am having trouble extending a Promise inside a .then(). I am trying to perform DB updates in a for-loop and then close the database after all records are processed. However the application exits with process.exit() right away which means that process.exit() was executed even before all db updates were finished. I am pretty sure I am doing something wrong with the nested promise.
var myDB;
function doSomething() {
return MongoClient.connect(DB_CONNECTION).then(function(db) {
myDB = db;
var collection = db.collection(COLLETION_NAME);
for (var i = 0; i < 10; i++) {
promise.then(function{
collection.update({
symbol: items[i].symbol
}, {
$set: {
value: 123
}
}, {
upsert: true
});
});
}
})
}
var promise = doSomething();
promise.then(function(){
console.log("DONE");
myDB.close();
process.exit();
});

It looks like you are getting a promise back from the MongoClient.connect method so why not use that to chain together. I've put a quick sample together below based on your code:
function doSomething(db) {
return new Promise(function(resolve, reject){
var collection = db.collection(COLLETION_NAME);
for (var i = 0; i < 10; i++) {
collection.update({
symbol: items[i].symbol
}, {
$set: {
value: 123
}
}, {
upsert: true
});
}
resolve(db);
})
}
function connectToDB() {
return MongoClient.connect(DB_CONNECTION);
}
function closeDB(db) {
return new Promise(function(resolve, reject){
db.close();
resolve();
});
}
connectToDB().then(function(db){
return doSomething(db);
}).then(function(db){
return closeDB(db);
}).then(function(){
console.log("DONE");
process.exit();
}).catch(function(error){
console.log('Something went wrong: ' + error);
});

Updated code as per #RayonDabre 's suggestion
function doSomething() {
return MongoClient.connect(DB_CONNECTION).then(function(db) {
myDB = db;
var collection = db.collection(COLLECTION_NAME);
var promises = [];
for (var i = 0; i < 10; i++) {
var innerPromise = collection.update({
symbol: items[i].symbol
}, {
$set: {
value: 123
}
}, {
upsert: true
});
promises.push(innerPromise);
}
return Promise.all(promises);
});
}
var promise = doSomething();
promise.then(function(){
console.log("DONE");
myDB.close();
process.exit();
});

Related

How to do a callback in nodeJS

I'm working on a nodeJS script and I would like to know how to execute a function after an another one.
Because actually i need to save in my database some data and then retrieve them. However for the moment my retrieve is executed before my save :/
Have already looked on internet there is a lot of example I tried them but for the moment no one worked ... I should probably do something wrong if somebody could help me on it :)
function persistMAP(jsonData, callback) {
console.log(jsonData);
//Deck persistance
for (var i = 0; i < 1; i++) {
(function(i) {
var rowData = new DeckDatabase({
_id: new mongoose.Types.ObjectId(),
DeckNumber: Number(jsonData.Deck[i].DeckNumber),
x: Number(jsonData.Deck[i].x),
y: Number(jsonData.Deck[i].y),
});
rowData.save(function(err) {
if (err) return console.log(err);
for (var i = 0; j = jsonData.Units.length, i < j; i++) {
(function(i) {
var unit = new MapDatabase({
//UnitID: mongoose.ObjectId(jsonData.Units[i].UnitID),
UnitID: jsonData.Units[i].UnitID,
TypeID: Number(jsonData.Units[i].TypeID),
x: Number(jsonData.Units[i].x),
y: Number(jsonData.Units[i].y),
_id: mongoose.Types.ObjectId(jsonData.Units[i].Code + 'dd40c86762e0fb12000003'),
MainClass: jsonData.Units[i].MainClass,
Orientation: jsonData.Units[i].Orientation,
Postion: jsonData.Units[i].Postion,
Deck: String(rowData._id)
});
unit.save(function(err) {
if (err) return console.log(err);
console.log('save');
});
})(i);
}
});
})(i);
}
callback();
};
app.get("/Map", function(req, res) {
console.log("got");
var urlTempBox = 'http://localhost:3000/MapCreate';
DeckDatabase.find(null, function(err, data) {
if (err) {
throw (err);
}
if (data.length != 0) {
MapDatabase.find()
.populate('Deck')
.exec(function(err, finalData) {
res.send(finalData);
});
} else {
request(urlTempBox, data, function(error, response, body) {
if (error) {
throw (error);
} else {
var jobj = JSON.parse(response.body);
console.log("persist begin");
persistMAP(jobj, function() {
console.log('retrieve Done');
});
}
});
}
});
You can use javascript callbacks like this:
User.findById(user_id,function(err,data){
//Inside this you can call another function.
});
console.log("Hello"); //this statement won't wait for the above statement
The more good approach would be use promises.
You can also use async await function to handle asynchronus tasks.
Async Await Style
async function getData(){
let user_data=await User.findById(user_id);
let user_videos=await Videos.findById(user_data._id); //user_data._id is coming from the above statement
}
But you can only use await in async methods.
Hope it helps.

Javascript Execute at end of Async Loop

I'm awful with Async code in Javascript and have been stuck on something for a while now.
I'm working with WebSql and just going through database initialization steps but one of the loops is not executing in the way I expect it to.
$(document).ready(function() {
initdatabase();
});
function initdatabase() {
var db = window.openDatabase("nothing", "1.0", "nothing", 2 * 1024 * 1024);
db.transaction(function(trans) {
trans.executeSql("CREATE TABLE", [], function(trans, result) {
// success
defaultdata(db);
}, function(error) {
// failed
});
});
}
function defaultdata(db) {
db.transaction(function(trans) {
var lo_data = [
{code:"CODE01", desc:"Code 01 Desc"},
{code:"CODE02", desc:"Code 02 Desc"},
{code:"CODE03", desc:"Code 03 Desc"}
];
for(i = 0; i < lo_data.length; i++) {
trans.executeSql("INSERT", [lo_data[i].code, lo_data[i].desc], function(trans, result) {
// success
console.log("INS : " + i);
}, function(error) {
// failed
});
}
console.log("END");
});
}
But the log to indicate the end is executing before the for loop has finished. If I try validate that the data has been inserted I always get fails because the loop hasn't completed the inserts.
Google says that async code should be handled with promises but I can't find examples of promises being used in an instance like this.
Any help would be greatly appreciated
Convert each callback into a promise, and then use Promise.all
const loDataPromises = lo_data.map(({ code, desc }) => {
return new Promise((resolve, reject) => {
trans.executeSql(
"INSERT",
[code, desc],
function(trans, result) {
console.log('success');
resolve();
},
function(error) {
console.log('failed');
reject();
}
);
});
});
Promise.all(loDataPromises)
.then(() => {
console.log('all done');
});
I haven't been able to find any clear code examples on the internet so I wanted to post the working version here as the answer. Hopefully it can benefit someone also trying to understand promises and promise loops.
After a complete overhaul I've managed to get it working in a way that makes sense. I've change it to be executed as a promise chain and then the function with the for loop is utilizing the promise all logic.
$(document).ready(function() {
////
// promise chain
////
console.log("BEGIN");
f_initdatabase().then(function(result) {
return f_defaultdata(result.db);
}).then(function(result) {
console.log("END");
}).catch(function(result) {
// abandon all hope
});
});
////
// single promise usage
////
function f_initdatabase() {
return new Promise(function(resolve, reject) {
console.log(" INIT DB");
var lo_result = {db:null};
var lv_db = window.openDatabase("thenothing", "1.0", "The Nothing DB", 2 * 1024 * 1024);
lv_db.transaction(function(trans) {
trans.executeSql ("create table if not exists dummydata (dd_idno integer primary key, dd_code text not null, dd_desc text not null)", [], function(trans, results) {
console.log(" INIT DB : DONE");
lo_result.db = lv_db;
resolve(lo_result);
}, function(error) {
lo_result.db = null;
reject(lo_result);
});
});
});
}
////
// loop promise all usage
////
function f_defaultdata(lv_db) {
return new Promise(function(resolve, reject) {
console.log(" DEF DATA");
var lo_result = {db:null};
lv_db.transaction(function(trans) {
var la_promises = [];
var lo_data = [
{dd_code:"CODE01", dd_desc:"Code 01 Desc"},
{dd_code:"CODE02", dd_desc:"Code 02 Desc"},
{dd_code:"CODE03", dd_desc:"Code 03 Desc"}
];
for(i = 0; i < lo_data.length; i++) {
console.log(" INS : " + i);
trans.executeSql (" insert into dummydata (dd_code, dd_desc) values (?, ?)", [lo_data[i].dd_code, lo_data[i].dd_desc], function(trans, results) {
la_promises.push(resolve(lo_result));
}, function(error) {
la_promises.push(reject(lo_result));
});
}
Promise.all(la_promises).then(function(results) {
console.log(" DEF DATA : DONE");
lo_result.db = lv_db;
resolve(lo_result);
}).catch(function() {
lo_result.db = null;
reject(lo_result);
});
});
});
}
This gives the output according to the flow needed
BEGIN
INIT DB
INIT DB : DONE
DEF DATA
INS : 0
INS : 1
INS : 2
DEF DATA : DONE
END

Syncano Codebox - Call API - parse JSON - get Reference - Save new Objects

I am using Syncano as a baas, where I am trying to call an external API to receive a JSON array. This JSON needs to be parsed and afterwards stored in syncano. Before that I need to receive the reference object from the DB to link it to the new team object.
I receive the team (json) array & reference object successfully. But I am unable to get the new data stored, as only 12-14 teams (has to be 18) get saved.
I tried this & that with promises but it didnĀ“t work out. Anyone a good advise how to rewrite the code to store all data? Thank you - here is what I have so far...
//TODO: get from ARGS when executing this codebox
var teamKey = 394;
var requestURL = 'http://api.football-data.org/v1/soccerseasons/' + teamKey + "/teams";
var request = require("request");
var Syncano = require('syncano');
var Promise = require('bluebird');
var account = new Syncano({
accountKey: "abc"
});
var promises = [];
//from: http://docs.syncano.io/v1.0/docs/data-objects-filtering
//"_eq" means equals to
var filter = {
"query": {
"apikey": {
"_eq": apiKey
}
}
};
request({
headers: {
'X-Auth-Token': 'abc'
},
url: requestURL,
'Content-Type': 'application/json;charset=utf-8;',
method: 'GET',
}, function(error, response, body) {
if (error) {
console.log(error);
} else {
var json = JSON.parse(body);
var teamArray = json.teams;
var newObject;
account.instance('instance').class('competition').dataobject().list(filter)
.then(function(compRes) {
var competitionID = compRes.objects[0].id;
for (var i = 0; i < teamArray.length; i++) {
newObject = {
"name": teamArray[i].name,
"nameshort": teamArray[i].code,
"logo": teamArray[i].crestUrl,
"competition": competitionID
};
(account.instance('instance').class('teams').dataobject().add(newObject).then(function(res) {
console.log(res);
}).catch(function(err) {
console.log("Error eq: " + err);
})
);
}
}).catch(function(err) {
console.log(err);
});
}
});
The issue might be you are finishing the request process before all the save calls are made, you could try Promise.all():
account.instance('instance').class('competition').dataobject().list(filter)
.then(function(compRes) {
var competitionID = compRes.objects[0].id, promises=[];
for (var i = 0; i < teamArray.length; i++) {
newObject = {
"name": teamArray[i].name,
"nameshort": teamArray[i].code,
"logo": teamArray[i].crestUrl,
"competition": competitionID
};
promises.push(account.instance('instance').class('teams').dataobject().add(newObject).then(function(res) {
console.log(res);
}).catch(function(err) {
console.log("Error eq: " + err);
})
);
}
return Promise.all(promises);
}).catch(function(err) {
console.log(err);
});
if too many parallel calls at a time is the issue then chain one call after other:
account.instance('instance').class('competition').dataobject().list(filter)
.then(function(compRes) {
var competitionID = compRes.objects[0].id, promise = Promise.resolve();
function chainToPromise(promise, teamObj, waitTime){
waitTime = waitTime || 500;
return promise.then(function(){
return new Promise(function(resolve, reject){
setTimeout(resolve, waitTime);
});
}).then(function(){
return account.instance('instance').class('teams').dataobject().add(teamObj);
}).then(function(res) {
console.log(res);
}).catch(function(err) {
console.log("Error eq: " + err);
});
}
for (var i = 0; i < teamArray.length; i++) {
newObject = {
"name": teamArray[i].name,
"nameshort": teamArray[i].code,
"logo": teamArray[i].crestUrl,
"competition": competitionID
};
promise = chainToPromise(promise, newObject);
}
return promise;
}).catch(function(err) {
console.log(err);
});

Looping with Promises

I'm in a scenario where I have to get data from the server in parts in sequence, and I would like to do that with the help of Promises. This is what I've tried so far:
function getDataFromServer() {
return new Promise(function(resolve, reject) {
var result = [];
(function fetchData(nextPageToken) {
server.getData(nextPageToken).then(function(response) {
result.push(response.data);
if (response.nextPageToken) {
fetchData(response.nextPageToken);
} else {
resolve(result);
}
});
})(null);
});
}
getDataFromServer().then(function(result) {
console.log(result);
});
The first fetch is successful, but subsequent calls to server.getData() does not run. I presume that it has to do with that the first then() is not fulfilled. How should I mitigate this problem?
Nimrand answers your question (missing catch), but here is your code without the promise constructor antipattern:
function getDataFromServer() {
var result = [];
function fetchData(nextPageToken) {
return server.getData(nextPageToken).then(function(response) {
result.push(response.data);
if (response.nextPageToken) {
return fetchData(response.nextPageToken);
} else {
return result;
}
});
}
return fetchData(null);
}
getDataFromServer().then(function(result) {
console.log(result);
})
.catch(function(e) {
console.error(e);
});
As you can see, recursion works great with promises.
var console = { log: function(msg) { div.innerHTML += "<p>"+ msg +"</p>"; }};
var responses = [
{ data: 1001, nextPageToken: 1 },
{ data: 1002, nextPageToken: 2 },
{ data: 1003, nextPageToken: 3 },
{ data: 1004, nextPageToken: 4 },
{ data: 1005, nextPageToken: 0 },
];
var server = {
getData: function(token) {
return new Promise(function(resolve) { resolve(responses[token]); });
}
};
function getDataFromServer() {
var result = [];
function fetchData(nextPageToken) {
return server.getData(nextPageToken).then(function(response) {
result.push(response.data);
if (response.nextPageToken) {
return fetchData(response.nextPageToken);
} else {
return result;
}
});
}
return fetchData(0);
}
getDataFromServer().then(function(result) {
console.log(result);
})
.catch(function(e) { console.log(e); });
<div id="div"></div>
Because your then statement doesn't pass a function to handle error cases, requests to the server for data can fail silently, in which case the promise returned by getDataFromServer will never complete.
To fix this, pass a second function as an argument to then, as below:
function getDataFromServer() {
return new Promise(function(resolve, reject) {
var result = [];
(function fetchData(nextPageToken) {
server.getData(nextPageToken).then(function(response) {
result.push(response.data);
if (response.nextPageToken) {
fetchData(response.nextPageToken);
} else {
resolve(result);
}
}).catch(function(error) {
//Note: Calling console.log here just to make it easy to confirm this
//was the problem. You may wish to remove later.
console.log("Error occurred while retrieving data from server: " + error);
reject(error);
});
})(null);
});
}

chain async operations using native JavaScript Promise

I've following two async operations and then final onResult and onFault defined. How can I chain following two async operations getConnection and then select and then finally calling onResult or onFault
Edit need help in promisifying following sequence.
new Promise(this.getConnection)
.then(this.select)
.then(this.onResult)
getConnection: function(resolve, reject) {
console.log('get connection')
if(database = common.model.connections.Sync.getConnection()) {
database.transaction(function(transaction){
resolve(transaction);
});
} else {
reject("Connection Error");
}
},
select: function(transaction) {
console.log('select', transaction)
var self = this;
var query = "SELECT * FROM configuration WHERE key = 'schedule'";
self.transaction.executeSql(query, [], function(transaction, resultSet){self.selectTransactionComplete(transaction, resultSet)}, function(){self.selectTransactionError()});
},
selectTransactionComplete: function(transaction, resultSet) {
console.log('select transaction complete')
if(resultSet.rows.length == 0) {
this.onResult(false);
} else if(new Date().getTime() - new Date(common.Config.getLastSync()).getTime() > resultSet.rows.item(0).value) {
this.onResult(true);
}
},
selectTransactionError: function(error) {console.log(this.constructor.NAME, this.selectSQL); console.log(error);},
onResult: function(data) {
console.log(data);
},
onFault: function(info) {
console.log(info)
}
after trying couple of things, is this how it's supposed to be done? please review
this.getConnection()
.then(this.select, this.getConnectionError)
.then(this.selectTransactionComplete, this.selectTransactionError)
getConnection: function() {
console.log('get connection')
var promise = new Promise(function(resolve, reject){
try {
database = common.model.connections.Sync.getConnection();
database.transaction(function(transaction){
resolve(transaction);
});
} catch(error) {
reject("Connection Error");
}
});
return promise;
},
getConnectionError: function(info) {
console.log("connectionError");
this.onFault();
},
select: function(transaction) {
console.log('select')
var self = this;
var promise = new Promise(function(resolve, reject){
var query = "SELECT * FROM configuration WHERE key = 'schedule'";
transaction.executeSql(query, [], function(transaction, resultSet){resolve(resultSet)}, function(error){reject(error)});
});
return promise;
},
selectTransactionComplete: function(resultSet) {
console.log('selectTransactionComplete')
/*if(resultSet.rows.length == 0) {
this.onResult(false);
} else if(new Date().getTime() - new Date(common.Config.getLastSync()).getTime() > resultSet.rows.item(0).value) {
this.onResult(true);
}*/
},
selectTransactionError: function(error) {
console.log('selectTransactionError')
//console.log(this.constructor.NAME, this.selectSQL);
//console.log(error);
},
onResult: function(data) {
console.log('onResult')
},
onFault: function(info) {
console.log('onFault')
}
If you are using a promise library like q, the way you would go about chaining the promises is as below;
getConnection: function() {
var deferred = Q.Defer();
console.log('get connection')
try {
database = common.model.connections.Sync.getConnection();
database.transaction(function(transaction){
deferred.resolve(transaction);
});
} catch(error) {
deferred.reject("Connection Error");
}
return deferred.promise;
}
when you call you would do something like below;
Q.when(getConnection)
.then(function(result){
// handle success or resolve
}, function(error){
// handle rejection.
};
Also I suggest reading the common js promises specification

Categories

Resources