why should we use q.defer(); in node js? - 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

Related

NodeJS+Express call async method in loop

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

Reading a csv file async - NodeJS

I am trying to create a function where I can pass file path and the read the file in async way. What I found out was that it supports streams()
const fs = require('fs');
var parse = require('csv-parse');
var async = require('async');
readCSVData = async (filePath): Promise<any> => {
let csvString = '';
var parser = parse({delimiter: ','}, function (err, data) {
async.eachSeries(data, function (line, callback) {
csvString = csvString + line.join(',')+'\n';
console.log(csvString) // I can see this value getting populated
})
});
fs.createReadStream(filePath).pipe(parser);
}
I got this code from here. but I am new to node js so I am not getting how to use await to get the data once all lines are parsed.
const csvData = await this.util.readCSVData(path)
My best workaround for this task is:
const csv = require('csvtojson')
const csvFilePath = 'data.csv'
const array = await csv().fromFile(csvFilePath);
This answer provides legacy code that uses async library. Promise-based control flow with async doesn't need this library. Asynchronous processing with async.eachSeries doesn't serve a good purpose inside csv-parse callback because a callback waits for data to be filled with all collected data.
If reading all data into memory is not an issue, CSV stream can be converted to a promise:
const fs = require('fs');
const getStream = require('get-stream');
const parse = require('csv-parse');
readCSVData = async (filePath): Promise<any> => {
const parseStream = parse({delimiter: ','});
const data = await getStream.array(fs.createReadStream(filePath).pipe(parseStream));
return data.map(line => line.join(',')).join('\n');
}

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.

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);
}
});
});
...

How to wait in a request and response in another request with koa

I wanna await the reponse in a request until I set value in another request. How to code this in koa?
request 1: /always-get?key=abc
request 2: /set?key=abc&val=1234
var querystring = require("querystring");
module.exports = resTest = {
awaitRes: function *(next) {
var qs = querystring.parse(this.request.querystring);
var key = qs.key;
resTest.qrResponses[key] = this.response;
// I wanna wait here, response in the next method
},
setRes: function *(next) {
var qs = querystring.parse(this.request.querystring);
var key = qs.key;
var val = qs.val;
//reponse here....
resTest.qrResponses[key].body = val;
this.body = "OK";
},
qrResponses: {}
};
router.get("/always-get", resTest.awaitRes);
router.get("/set", resTest.setRes);
This is easy in callback hell framework...
You can create an event bus to share events between them. Also, co will yield to a thunk and provide a callback. So you can yield a thunk and call the callback when you've "heard" the event with the same name emitted. Once that callback is called, the middleware chain will continue.
var Emitter = require('events');
var bus = new Emitter();
router.get('/info/:key', function* (next) {
var ctx = this;
// yield a thunk
yield function(cb) {
// this has the potential to leak event listeners!
bus.once(ctx.params.key, function(value) {
ctx.body = value;
cb();
});
};
});
router.get('/set/:key/:value', function* (next) {
bus.emit(this.params.key, this.params.value);
this.body = 'OK';
});

Resources