NodeJS+Express call async method in loop - node.js

I'm trying to create a web api function with NodeJS and Express, retrieving data from private ethereum blockchain.
The problem is that the method mytoken.tokenOfOwnerByIndex... is async method in loop but want to wait all results until done and let the function returns tokenIds as a result.
I tried to use async/await but don't know how to use them properly.
Here is the snippet of my current code:
app.get("/get", function(req, res, next){
var Web3 = require('web3');
var BigNumber = require('bignumber.js');
Web3.providers.HttpProvider.prototype.sendAsync = Web3.providers.HttpProvider.prototype.send;
var web3 = new Web3();
web3.setProvider(new web3.providers.HttpProvider('http://localhost:8545'));
var contract = require("truffle-contract");
var contractJson = require("./build/contracts/MyToken.json");
var MyToken = contract(contractJson);
MyToken.setProvider(web3.currentProvider);
var tokenIds = [];
MyToken.deployed().then(function(mytoken) {
var account0 = web3.eth.accounts[0];
mytoken.balanceOf(accounts0).then(function(balance){
var x = balance.toNumber();
for (i = 0; i < x; i++){
mytoken.tokenOfOwnerByIndex(account0,0).then(function(tokenId){
var y = tokenId.toNumber();
tokenIds.push(y);
});
}
res.json(tokenIds);
});
});
});
Can anyone guide me or give me the clue?

I think in such cases library like Bluebird is really helpful. When working with iterables and promises you can use Bluebird's map() method (map).
Since you have a number here (var x) and not iterable you could do something like this
var Promise = require('bluebird);
var x = balance.toNumber();
var promises = [];
for (i = 0; i < x; i++){
promises.push(mytoken.tokenOfOwnerByIndex(account0,0));
});
Promise.all(promises).then(function(results) {
//do something
});
You can refer to the Bluebird docs for more information

Related

Node.js and Redis: How do I add items to a function wide object from within the callback?

So, if I have something like the following:
let searchObj = {};
for (var i = 0; i < list.length; i++){
var info = client.runFunction(variable, 0, -1, async function(err, result){
var amount = parseInt(result[0]);
searchObj[result1[i]] = amount;
});
}
How can I access searchObj from outside of the callback? If I debug and print from within the callback, it works, otherwise it doesn't.

why should we use q.defer(); in node js?

I am learning node JS. I have read some articles but I can't find the proper answer.
Here is the syntax
'use strict';
var express = require('express'),
app = express();
var cors = require('cors');
let request = require('request');
let async = require('async');
let q = require('q');
let fs = require('fs');
let BC_STORE_URL = '*****************************';
let BC_USER = '********';
let BC_TOKEN = '******************************';
app.get('/api/:sku', function(req, res, next) {
getData('products?sku=' + productSku).then((resData) => {
});
})
let getData = function(storeField) {
let listInfo = [];
let deferred = q.defer();
request(getDataOptions, (getDataOptionsError, getDataOptionsResponse,
getDataOptionsData) => {
deferred.resolve(responseData);
}
}
The q.defer() is used to create deferred which is used to work with promises. The promise will tell the caller that the method is returning some data in some time (async). The caller can then declare logic on the promise then() to be executed when the data is returned.
You should return a promise from the getData function to which the caller can use then()
let getData = function(storeField) {
let listInfo = [];
let deferred = q.defer();
request(getDataOptions, (getDataOptionsError, getDataOptionsResponse,
getDataOptionsData) => {
deferred.resolve(responseData);
}
return deferred.promise;
}
Here's better explanation to your answer :
Q vs native promises

nodejs request running last

I am trying to figure out how to make the callback function in request run in order. Currently, my loop runs 10 times but does not wait for the callback function in request to finish before moving to the next iteration. My output is nothing like what I'd expect it to be and I'm not sure why certain things are being printed before others. Here is how my code is as of now:
var express = require('express');
var path = require('path');
var request = require('request');
var cheerio = require('cheerio');
var fs = require('fs');
var app = express();
var url;
for(var i=0; i < 10; i++ ){
url = "http://www.ratemyprofessors.com/ShowRatings.jsp?tid=" + i;
request(url, function(err, resp, body){
console.log("hello");
var $ = cheerio.load(body);
if($('.error').text().substring(0, 14) == "Page Not Found"){
console.log("sorry page not found");
return;
}else{
console.log($('.error').text().substring(0, 14) );
var pfname = $('.pfname');
var plname = $('.plname');
var professorName = pfname.text().replace(/\s/g, '') + " " +plname.text().replace(/\s/g, '');
console.log(professorName);
console.log(url);
return;
}
});
}
Here is the output I am getting:
hello
sorry page not found
hello
sorry page not found
hello
sorry page not found
hello
sorry page not found
hello
sorry page not found
hello
sorry page not found
hello
sorry page not found
hello
Michael Beeson
http://www.ratemyprofessors.com/ShowRatings.jsp?tid=9
hello
Sami Khuri
http://www.ratemyprofessors.com/ShowRatings.jsp?tid=9
hello
aaa aaa
http://www.ratemyprofessors.com/ShowRatings.jsp?tid=9
Here is the proper output:
aaa aaa
http://www.ratemyprofessors.com/ShowRatings.jsp?tid=1
Sami Khuri
http://www.ratemyprofessors.com/ShowRatings.jsp?tid=2
Michael Beeson
http://www.ratemyprofessors.com/ShowRatings.jsp?tid=3
sorry page not found
sorry page not found
sorry page not found
sorry page not found
sorry page not found
sorry page not found
sorry page not found
There are multiple issues in your code, but the main issue is that you're running an async operation inside the for loop so your for loop will start all the async operations and then they will, one-by-one complete later. Any variables shared between the loop invocations will tromp one another.
So, in a nutshell, I did:
Removed all shared variables so each loop invocation has its own variables (no conflicts).
Switched over to request-promise so we can use Promise.all() to more easily tell us when they are all done.
Returned the value we want from each .then() handler so that will be collected by Promise.all() as the final values for each invocation of the loop.
Because there appears to be no reason to sequence your operations, I let them all run in a parallel (that's faster) and then let Promise.all() put the results in order for us in the final array of results.
Here's the code:
const express = require('express');
const path = require('path');
const rp = require('request-promise');
const cheerio = require('cheerio');
const fs = require('fs');
const app = express();
let promises = [];
for (let i = 0; i < 10; i++ ) {
let url = "http://www.ratemyprofessors.com/ShowRatings.jsp?tid=" + i;
promises.push(rp(url).then(function(body) {
console.log(url);
let $ = cheerio.load(body);
if($('.error').text().substring(0, 14) == "Page Not Found"){
console.log("sorry page not found");
return null;
} else {
console.log($('.error').text().substring(0, 14) );
let pfname = $('.pfname');
let plname = $('.plname');
let professorName = pfname.text().replace(/\s/g, '') + " " +plname.text().replace(/\s/g, '');
console.log(professorName);
return professorName;
}
}));
}
// see when they are all done
Promise.all(promises).then(results => {
// array of results, some entries that were not found may be null
console.log(results);
}).catch(err => {
console.log(err);
});
If you want to sequence them one at a time so the second request doesn't start until the first one is done, that could be done like this using async/await:
const express = require('express');
const path = require('path');
const rp = require('request-promise');
const cheerio = require('cheerio');
const fs = require('fs');
const app = express();
async function run() {
let results = [];
for (let i = 0; i < 10; i++ ) {
let url = "http://www.ratemyprofessors.com/ShowRatings.jsp?tid=" + i;
try {
let body = await rp(url);
console.log("hello");
let $ = cheerio.load(body);
if($('.error').text().substring(0, 14) == "Page Not Found"){
console.log("sorry page not found");
results.push(null);
} else {
console.log($('.error').text().substring(0, 14) );
let pfname = $('.pfname');
let plname = $('.plname');
let professorName = pfname.text().replace(/\s/g, '') + " " +plname.text().replace(/\s/g, '');
console.log(professorName);
console.log(url);
results.push(professorName);
}
} catch(e) {
console.log(url, e);
results.push(null);
}
}
return results;
}
run().then(results => {
console.log(results);
}).catch(err => {
console.log(err);
});
EDIT Jan, 2020 - request() module in maintenance mode
FYI, the request module and its derivatives like request-promise are now in maintenance mode and will not be actively developed to add new features. You can read more about the reasoning here. There is a list of alternatives in this table with some discussion of each one. I have been using got() myself and it's built from the beginning to use promises and is simple to use.

How to read and get events from zoho calendar using CalDav in node.js

I want to communicate from backend with the calendar using caldav of zoho mail using nodejs. Could anyone suggest me how to implement it?
I am using plugin node-caldav-mod
I tried this piece of code which doesn't seem to be working.
var caldav = require("node-caldav-mod");
var moment = require('moment-timezone');
var express = require('express');
var app = express();
var xmljs = require("libxmljs");
var https = require("https");
var CalendarId = "2123123123";
var url = "https://calendar.zoho.com/caldav/{CalendarId}/events";
var username = "username"
var password = "password"
var timeFormat = "YYYYMMDDTHHmms";
var getTodayEvent = function (callback){
var startDate = moment().set({'hour': 0,'minute': 0,'second': 0}).format(timeFormat) + "Z";
var endDate = moment().set({'hour': 23,'minute': 59,'second': 59}).format(timeFormat) + "Z";
var output = {};
output.startDate = startDate;
output.endDate = endDate;
caldav.getEvents(url, username, password, startDate, endDate, function(response){
console.log(response);
callback(response);
});
}
var findPropertyNameByRegex = function(o, r) {
var key;
for (key in o) {
if (key.match(r)) {
return key;
}
}
return undefined;
};
function compare(a,b) {
var startDate_a = findPropertyNameByRegex(a, "DTSTART");
var startDate_b = findPropertyNameByRegex(b, "DTSTART");
if (a[startDate_a] < b[startDate_b])
return -1;
else if (a[startDate_a] > b[startDate_b])
return 1;
else
return 0;
}
app.get('/today',function(req, res){
getTodayEvent(function(events){
events.sort(compare);
res.send("Communication set up properly!")
})
});
This is the error which I am getting
Parsing.....
undefined
Error parsing response
TypeError: Cannot read property 'D:multistatus' of undefined
Could somebody tell me what's wrong with this code?
Could somebody tell me what's wrong with this code?
Yes. Your error seems to come from
this line
in the module you seem to use:
var data = result['D:multistatus']['D:response'];
Which is utter non-sense. D: is just a namespace prefix and can be anything the server chooses.
A CalDAV client needs to properly parse and process XML namespaces.
Solution: Use a proper module, or just write it on your own.

Get data from nested foreach

I'm building an app using firebase and Node.js. I need to get data from nested foreach. How to do it correctly? Need to return the results of all iterations simultaneously.
exports.userParty = function (userInfo, cb) {
var userID = userInfo.userID;
var clubID = userInfo.clubID;
var refUserParty = ref.child(userID).child('my_party_id');
var party = {};
refUserParty.orderByValue().once("value", function (snapshot) {
var party = {};
snapshot.forEach(function (partyID) {
var refParty = dbb.ref('clubs').child(clubID).child('party').child(partyID.val());
refParty.once('value', function (partyBody) {
party[partyID.val()] = partyBody.val();
//console.log(party);
});
});
cb(party); // {}
});
};
You need to call the callback after all the async functions in the forEach block have completed. You can do this using a simple counter to check all async functions are complete:
...
let completedSnapshots = 0;
snapshot.forEach(function (partyID) {
var refParty = dbb.ref('clubs').child(clubID).child('party').child(partyID.val());
refParty.once('value', function (partyBody) {
party[partyID.val()] = partyBody.val();
completedSnapshots++;
if (completedSnapshots === snapshot.val().length) {
cb(party);
}
});
});
...

Resources