I am trying to execute an asynchronous method within a for-loop construct and then display the result. I believe the problem is that the for-loop increments before the cryto.randomBytes method calls the callback. How would I properly execute this for-loop ?
var crypto = require('crypto');
var nimble = require('nimble');
var codes = [];
nimble.series([
function(callback){
for(var i = 0; i < 100;i++){
crypto.randomBytes(64, function(ex, buf) {
if (ex) throw ex;
codes[i] = buf.toString('hex');
});
}
callback();
},
function(callback){
for(var i = 0; i < codes.length;i++){
console.log("Ticket " + i + ":" + codes[i]);
}
callback();
}]);
Yes, you are right that the loop completes before the callbacks are called. You can use an anonymous function to create a scope where each iteration gets its own copy of the variable.
Also, you would call the callback after the last value has been added to the result, not after the loop:
function(callback){
var cnt = 0;
for(var i = 0; i < 100;i++){
(function(i){
crypto.randomBytes(64, function(ex, buf) {
if (ex) throw ex;
codes[i] = buf.toString('hex');
if (++cnt == 100) {
callback();
}
});
})(i);
}
}
Instead of:
function(callback){
for(var i = 0; i < 100;i++){
crypto.randomBytes(64, function(ex, buf) {
if (ex) throw ex;
codes[i] = buf.toString('hex');
});
}
callback();
},
You might try something like:
function(callback){
for(var i = 0, len = 100; i < len; i++){
crypto.randomBytes(64, function(ex, buf) {
if (ex) throw ex;
codes.push(buf.toString('hex'));
if (codes.length === len)
callback();
});
}
},
Using an IIFE with recursivity, should work :
var crypto = require('crypto');
var nimble = require('nimble');
var codes = [];
nimble.series([
function (callback) {
// Using an IIFE
(function recursive(index) {
if (index < 100) { // i < 100
crypto.randomBytes(64, function (ex, buf) {
if (ex) throw ex;
codes[index] = buf.toString('hex');
recursive(index + 1);
});
} else callback();
})(0); // i = 0
},
function (callback) {
for (var i = 0; i < codes.length; i++) {
console.log("Ticket " + i + ":" + codes[i]);
}
callback();
}]);
Related
async function getjsondata() {
try {
state = await fetch('https://covid.ourworldindata.org/data/owid-covid-data.json');
data = await state.json();
} catch (err) {
console.log(err);
}
}
var sumy = 0;
var tt_list = [];
async function getmonthnewcases() {
await getmonthsum();
await getjsondata();
await getCountry();
try {
for (let i = 0; i < 12; i++) {
for (let z = listy[i]; z <= listy[i + 1]; z++) {
for (key in data[isocount]["data"][z]) {
if (data[isocount]["data"][z].hasOwnProperty(key) == true) {
sumy += Number(data[isocount]["data"][z]["total_cases_per_million"]);
tt_list[i] = sumy;
} else if (typeof data[isocount]["data"][z]["total_cases_per_million"] === "undefined") {
data[isocount]["data"][z]["total_cases_per_million"] = sumy;
continue;
}
if (z == data[isocount]["data"].length - 1) {
break;
}
console.log(data[isocount]["data"][z]["date"], sumy, data[isocount]["data"][z]["total_cases_per_million"]);
}
}
}
console.log(tt_list);
} catch (err) {
console.log(err);
}
}
So this is how the code looks like am having trouble solving the issue of undefined in a particular month date from the api call, i have tried so many other options but nothing seems to be working please i nned some help
First is seems that you have some errors in the code,
You are not returning anything from getjsondata.
And you are not using the results in getmonthnewcases
(See code comments)
async function getjsondata() {
try {
state = await fetch('https://covid.ourworldindata.org/data/owid-covid-data.json');
return await state.json(); //<-- here
} catch (err) {
console.log(err);
}
}
var sumy = 0;
var tt_list = [];
async function getmonthnewcases() {
await getmonthsum();
const data = await getjsondata(); //<-- and here
await getCountry();
try {
for (let i = 0; i < 12; i++) {
for (let z = listy[i]; z <= listy[i + 1]; z++) {
for (key in data[isocount]["data"][z]) {
if (data[isocount]["data"][z].hasOwnProperty(key) == true) {
sumy += Number(data[isocount]["data"][z]["total_cases_per_million"]);
tt_list[i] = sumy;
} else if (typeof data[isocount]["data"][z]["total_cases_per_million"] === "undefined") {
data[isocount]["data"][z]["total_cases_per_million"] = sumy;
continue;
}
if (z == data[isocount]["data"].length - 1) {
break;
}
console.log(data[isocount]["data"][z]["date"], sumy, data[isocount]["data"][z]["total_cases_per_million"]);
}
}
}
console.log(tt_list);
} catch (err) {
console.log(err);
}
}
I have one mongojs query such as:
db.mapping.find(
{ "provider":req.params.provider, "perId":mongojs.ObjectId(req.params.perId) },
function(err, que){
if(err)
res.send(err);
else if(que)
{
totalVideoList = [];
for (var i=0; i < que.length; i++)
{
myid = que[i].providerId;
db.ABC.find({}, function(err, que1){
if(err)
res.send(err);
var x = {};
for (var j=0; j < que1.length; j++)
{
searching(que1[j]);
}
videoList = [];
getVideo(requiredDocument);
totalVideoList = totalVideoList.concat(videoList);
});
}
res.json(totalVideoList);
}
else
res.json([]);
});
Currently I am always getting [] (empty array) as my response. The problem is due asynchronous nature of callback function of mongojs. Before the expected output comes in "totalVideoList" variable, it responds us with totalVideoList = [].
I don't know how to use async.each() here. Please help me to tackle this problem.
Assuming using async
db.mapping.find(
{ "provider":req.params.provider, "perId":mongojs.ObjectId(req.params.perId) },
function(err, que){
if(err)
res.send(err);
else if(que)
{
totalVideoList = [];
async.each(que, function(item, callback){
myid = item.providerId;
db.ABC.find({}, function(err, item){
if(err)
return callback(err);
var x = {};
for (var j=0; j < item.length; j++)
{
searching(item[j]);
}
videoList = [];
getVideo(requiredDocument);
totalVideoList = totalVideoList.concat(videoList);
callback(null);
});
}, function(asyncErr){
if(asyncErr)
return resp.json(asyncErr);
resp.json(totalVideoList);
});
}
else
res.json([]);
});
I want to use method in other method in the same class to calc some data from db as below but Im only getting
error
[TypeError: Object #<Query> has no method 'methodMaxLct']"
exports.UserClass = function() {
this.methodMaxLct = function(lct, callback) {
var counting = Math.ceil(Math.pow(1.15, (lct - 1)) * 10) * 10;
callback(counting);
this.methodGetData = function(idu, callback) {
connection = mysql.createConnection(dbconfig);
connection.query(dataUserResources, [idu], function(err, results, fields) {
if (err) throw err;
if (results.length == 0) {
callback = 0;
} else {
for (var i in results) {
var dataU = results[i];
}
dataU.enMax = 30;
var ap = this.methodMaxLct(dataU.lct, function(answer) {
dataU.lctMax = answer;
});
callback(dataU);
}
connection.end();
});
};
};
Can anyone give me a tip or same clue how to do that in right way?
Try it like so. You have to store the this object into a variable so you can use it in an inner function. The this is different based on the execution context.
exports.UserClass = function() {
var self = this;
this.methodMaxLct = function(lct, callback) {
var counting = Math.ceil(Math.pow(1.15, (lct - 1)) * 10) * 10;
callback(counting);
this.methodGetData = function(idu, callback) {
connection = mysql.createConnection(dbconfig);
connection.query(dataUserResources, [idu], function(err, results, fields) {
if (err) throw err;
if (results.length == 0) {
callback = 0;
} else {
for (var i in results) {
var dataU = results[i];
}
dataU.enMax = 30;
var ap = self.methodMaxLct(dataU.lct, function(answer) {
dataU.lctMax = answer;
});
callback(dataU);
}
connection.end();
});
};
};
The context (this) of the callback function is determined when the callback function is called. So, you should use either arrow function or .bind( this )
Using an arrow function
exports.UserClass = function() {
this.methodMaxLct = function(lct, callback) {
var counting = Math.ceil(Math.pow(1.15, (lct - 1)) * 10) * 10;
callback(counting);
this.methodGetData = function(idu, callback) {
connection = mysql.createConnection(dbconfig);
// use arrow function
connection.query(dataUserResources, [idu], (err, results, fields) => {
if (err) throw err;
if (results.length == 0) {
callback = 0;
} else {
for (var i in results) {
var dataU = results[i];
}
dataU.enMax = 30;
var ap = this.methodMaxLct(dataU.lct, function(answer) {
dataU.lctMax = answer;
});
callback(dataU);
}
connection.end();
});
};
};
Using .bind(...) method
exports.UserClass = function() {
this.methodMaxLct = function(lct, callback) {
var counting = Math.ceil(Math.pow(1.15, (lct - 1)) * 10) * 10;
callback(counting);
this.methodGetData = function(idu, callback) {
connection = mysql.createConnection(dbconfig);
connection.query(dataUserResources, [idu], function(err, results, fields) {
if (err) throw err;
if (results.length == 0) {
callback = 0;
} else {
for (var i in results) {
var dataU = results[i];
}
dataU.enMax = 30;
var ap = this.methodMaxLct(dataU.lct, function(answer) {
dataU.lctMax = answer;
});
callback(dataU);
}
connection.end();
}.bind( this ) );
};
};
I made a script in Javascript (for phantomjs) to automate tests in a website.
I would like to count the errors encountered.
With this script, I get the error : "nbe variable unknown".
I understand that try... catch works in a specific way but I don't know what to do.
How can I modify my script to make it work?
Thank you
var nbe = 0;
var err = 0;
function next_page() {
page.evaluate(function() {
try {
document.querySelector('input[name="cc"]').click();
} catch (e) {
nbe++;
console.log('Error');
err = 1;
}
});
var k = i + 1 - nbe;
if (err == 0) console.log('Test ' + k + ' done');
i++;
if (i < links.length) setTimeout(handle_page, 1500);
else {
console.log('Errors : ' + nbe);
phantom.exit();
}
}
You could do something like the following:
var nbe = 0;
var err = 0;
var errors = [];
function next_page() {
page.evaluate(function() {
try {
document.querySelector('input[name="cc"]').click();
} catch (e) {
nbe++;
console.log('Error');
err = 1;
errors.push(e);
}
});
var k = i + 1 - nbe;
if (err === 0) {
console.log('Test ' + k + ' done')
};
i++;
if (i < links.length) {
setTimeout(handle_page, 1500);
} else {
for(var j = 0; j < errors.length; j++){
console.log(errors[j]);
}
phantom.exit();
}
}
When I get a request, I want it to generate a 4-character code, then check if it already exists in the database. If it does, then generate a new code. If not, add it and move on. This is what I have so far:
var code = "";
var codeFree = false;
while (! codeFree) {
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var code = "";
for (var i = 0; i < 4; i++) {
var rand = Math.floor(Math.random() * chars.length);
console.log(rand);
code += chars.charAt(rand);
}
console.log("Code: %s generated.", code);
client.execute("select * from codes where code=" + code, function(err, result) {
if (! err) {
if (result.rows.length > 0) {
codeFree = false;
} else {
codeFree = true;
}
} else {
console.log('DB ERR: %s', err);
}
console.log(codeFree);
});
console.log('here');
}
This does not do nearly what I want it to do. How can I handle something like this?
You are doing an async task.
When you have an asyncronous task inside your procedure, you need to have a callback function which is going to be called with the desired value as its argument.
When you found the free code, you call the function and passing the code as its argument, otherwise, you call the getFreeCode function again and passing the same callback to it. Although you might consider cases when an error happens. If your the db call fails, your callback would never get called. It is better to use a throw/catch mechanism or passing another argument for error to your callback.
You can achieve what you need to do by doing it this way:
function getFreeCode(callback) {
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var code = "";
for (var i = 0; i < 4; i++) {
var rand = Math.floor(Math.random() * chars.length);
console.log(rand);
code += chars.charAt(rand);
}
console.log("Code: %s generated.", code);
client.execute("select * from codes where code="+code, function(err, result) {
if(!err) {
if(result.rows.length > 0) {
getFreeCode(callback);
} else {
callback(code);
}
}else {
console.log('DB ERR: %s', err);
}
console.log(codeFree);
});
console.log('here');
}
// in your main:
getFreeCode(function (code) {
console.log(' this code was free: ' + code)
})
I recommend you look into two alternatives to help deal with asynchronous code.
node generator functions using the 'yield' keyword
promises
Using generators requires running a recent version of node with the --harmony flag. The reason I recommend generators is because you can write code that flows the way you expect.
var x = yield asyncFunction();
console.log('x = ' + x);
The previous code will get the value of x before logging x.
Without yielding the console.log would write out x before the async function was finished getting the value for x.
Your code could look like this with generators:
var client = {
execute: function (query) {
var timesRan = 0;
var result = [];
return function () {
return setTimeout(function () {
result = ++timesRan < 4 ? ['length_will_be_1'] : [];
return result;
},1);
};
}
};
function* checkCode () {
var code;
var codeFree = false;
while(!codeFree) {
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
code = "";
for (var i = 0; i < 4; i++) {
var rand = Math.floor(Math.random() * chars.length);
console.log(rand);
code += chars.charAt(rand);
}
console.log("Code: %s generated.", code);
try {
var result = yield client.execute("select * from codes where code="+code);
codeFree = result.rows.length > 0 ? false : true;
}catch(e) {
console.log('DB ERR: %s', err);
} finally {
console.log(codeFree);
}
console.log('here');
}
}
checkCode().next();
You would leave off the client object. I only added that to make a working example that fakes an async call.
If you have to use an older version of node or do not like the yield syntax then promises could be a worthy option.
There are many promise libraries. The reason I recommend promises is that you can write code that flows the way you expect:
asyncGetX()
.then(function (x) {
console.log('x: ' + x);
});
The previous code will get the value of x before logging x.
It also lets you chain async functions and runs them in order:
asyncFunction1()
.then(function (result) {
return asyncFunction2(result)
})
.then(function (x) { /* <-- x is the return value from asyncFunction2 which used the result value of asyncFunction1 */
console.log('x: ' + x);
});
Your code could look like this with the 'q' promise library:
var Q = require('q');
var client = {
timesRan: 0,
execute: function (query, callback) {
var self = this;
var result = {};
setTimeout(function () {
console.log('self.timesRan: ' + self.timesRan);
result.rows = ++self.timesRan < 4 ? ['length = 1'] : [];
callback(null, result);
},1);
}
};
function checkCode () {
var deferred = Q.defer();
var codeFree = false;
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var code = "";
for (var i = 0; i < 4; i++) {
var rand = Math.floor(Math.random() * chars.length);
console.log('rand: %s', rand);
code += chars.charAt(rand);
}
console.log("Code: %s generated.", code);
client.execute("select * from codes where code="+code, function(err, result) {
console.log('err: '+err+', result: ' + JSON.stringify(result));
console.log('result.rows.length: ' + result.rows.length);
if(!err) {
if(result.rows.length > 0) {
codeFree = false;
console.log('result.rows: %s, codeFree: %s', result.rows, codeFree);
checkCode();
} else {
codeFree = true;
console.log('line 36: codeFree: ' + codeFree);
deferred.resolve(code);
}
}else {
console.log('DB ERR: %s', err);
deferred.reject(err);
}
console.log(codeFree);
});
console.log('waiting for promise');
return deferred.promise;
}
checkCode()
.then(function (code) {
console.log('success with code: ' + code);
})
.fail(function(err) {
console.log('failure, err: ' + err);
});
Also omit the client object here. I only added that to make a working example that fakes an async call.
Promises and generators definitely take some time to get used to. It's worth it because they make the code a lot easier to follow in the end than code written with nested callbacks.