How to access await function variable outside function in Node.js - javascript

I am trying to implement an await function which local variable I am trying to access outside the function. However, I'm not getting the value which is defined inside function.
async CreateProduceRateAsset(data, callback) {
// Create a new file system based wallet for managing identities.
try {
var TimeStamp = new Date();
var TxId = '';
var blockNumber = '';
const result = await contract.submitTransaction('CreateProduceRateAsset', args);
await network.addBlockListener('block-listener', (err, block) => {
if (err) {
console.log(err);
return;
}
TimeStamp = block.data.data[0].payload.header.channel_header.timestamp;
var Tx_id = block.data.data[0].payload.header.channel_header.tx_id;
var BlockNO = block.header.number;
console.log('TxId', Tx_id)
console.log('blockNumber', BlockNO)
TxId = Tx_id
blockNumber = BlockNO
return TimeStamp, Tx_id, BlockNO
});
console.log('Timestamp', TimeStamp)
console.log('TxId 123', TxId)
console.log('blockNumber 123', blockNumber)
response.data = result
return callback(response);
} catch (error) {
response.httpstatus = 404;
response.message = `Failed to get MVP Price ${error.message} `;
return callback(response);
}
};
In the above code, I want to access Tx_id, Timestamp and BlockNO. For that, I am assigning a local variable to a global variable, however, I'm still getting the value of those as blank.
Could someone help me to get those values?

if I understand network.addBlockListener is a callback-based API and not return a promise so you cant await it, I guessed that you use https://hyperledger.github.io/fabric-sdk-node/release-1.4/module-fabric-network.Network.html
Here is your code with my comments
async CreateProduceRateAsset(data, callback) {
// Create a new file system based wallet for managing identities.
try {
var TimeStamp = new Date();
var TxId = '';
var blockNumber = '';
const result = await contract.submitTransaction('CreateProduceRateAsset', args);
// --> this api is callback based and wont not return a promise so you cant await it
await network.addBlockListener('block-listener', (err, block) => {
if (err) {
console.log(err);
return;
}
TimeStamp = block.data.data[0].payload.header.channel_header.timestamp;
// --> becasue you are inside a function these vars are not the same as the one ouside the call back , becasue they are scoped to this function
var Tx_id = block.data.data[0].payload.header.channel_header.tx_id;
var BlockNO = block.header.number;
console.log('###########TxId#####################', Tx_id)
console.log('###########blockNumber#####################', BlockNO)
TxId = Tx_id
blockNumber = BlockNO
// --> you can only return one value in js, also this reurn is useless since you dont call this callback
// --> better way to get these vars outside the callback is to pass them to your own callbak like this `callback(response,TimeStamp, Tx_id, BlockNO)`
return TimeStamp, Tx_id, BlockNO
});
console.log('*************** Timestamp:: **********************', TimeStamp)
console.log('###########TxId#####################123', TxId)
console.log('###########blockNumber#####################123', blockNumber)
response.data = result
return callback(response);
} catch (error) {
// if(error) throw error;
// response.error = error;
response.httpstatus = 404;
response.message = `Failed to get MVP Price ${error.message} `;
return callback(response);
}
};
if you need to use async/await with network.addBlockListener you can use utils.promisify to convert it to promise-based API but I don't recommend it since this an event listener

You have to put your return into a variable like this:
var data = await network.addBlockListener('block-listener', (err, block) => {
if (err) {
console.log(err);
return;
}
TimeStamp = block.data.data[0].payload.header.channel_header.timestamp;
var Tx_id = block.data.data[0].payload.header.channel_header.tx_id;
var BlockNO = block.header.number;
console.log('###########TxId#####################', Tx_id)
console.log('###########blockNumber#####################', BlockNO)
TxId = Tx_id
blockNumber = BlockNO
return {TimeStamp, Tx_id, BlockNO};
});
console.log('*************** Timestamp:: **********************', data.TimeStamp)
console.log('###########TxId#####################123', data.Tx_id)
console.log('###########blockNumber#####################123', data.BlockNO)
Now you can store all your values into data and put them into the desired variables.

Related

How to use filepath in extractIssueData?

I am using request NPM package and request take two parameters
request (URL, callback);
Here I somehow want to pass extra argument to my callback function how do I do it.
This is the code I am trying to write
function extractIssues(url,filepath){
request(url,issueCb);
}
function issueCb(err,response,html)
{
if(err){
console.log(err+"at line 61");
}
else{
extractIssueData(html);
}
}
function extractIssueData(html){
let selTool = cheerio.load(html);
let arr = [];
let issueLinksArr = selTool(".flex-auto.min-width-0.p-2.pr-3.pr-md-2 > a");
let result="";
for(let i = 0;i<issueLinksArr.length;i++){
let issueLink = selTool(issueLinksArr[i]).attr("href");
let content = selTool(issueLinksArr[i]).text().trim().split("/n")[0];
let obj = {
link :issueLink,
content:content
}
let str = JSON.stringify(obj);
result = result + str + " ,"+ "\n" ;
}
console.log(result);
}
I want to use filepath in extractIssueData so I need to first catch it in issueCb how do I do it
I can't find proper answer.
This issue disappears entirely if you simplify to use a single function! The following should be equivalent to, if not an improvement on, your code:
let extractIssues = async (url, filepath) => {
// Wait for the html content of a `request` call
let html = await new Promise((resolve, reject) => {
// If an Error occurs reject with the error, otherwise resolve with
// html data
request(url, (err, res, html) => err ? reject(err) : resolve(html));
});
let selTool = cheerio.load(html);
// Collect href and text content values from <a> elements
let resultsArr = selTool(".flex-auto.min-width-0.p-2.pr-3.pr-md-2 > a")
.map(issueLink => ({
link: selTool(issueLink).attr("href"),
content: selTool(issueLink).text().trim().split("/n")[0]
}));
// Print the array of link+content data
console.log(resultsArr);
};
Note that within this function you have access to filepath, so you can use it as is necessary.
A solution without async looks like:
let extractIssues = (url, filepath) => {
request(url, (err, res, html) => {
if (err) throw err;
let selTool = cheerio.load(html);
// Collect href and text content values from <a> elements
let resultsArr = selTool(".flex-auto.min-width-0.p-2.pr-3.pr-md-2 > a")
.map(issueLink => ({
link: selTool(issueLink).attr("href"),
content: selTool(issueLink).text().trim().split("/n")[0]
}));
// Print the array of link+content data
console.log(resultsArr);
});
};
Once again this creates a scenario where filepath is available to be used as necessary within the function.
Note: If you are being "given instruction" not to use async you are being done a disservice; async is a core feature and major selling point of javascript. Once you become comfortable with async you will quickly regret ever having worked without it. I recommend you take the time to understand it! :)
I can create you an easy sample to use callback functions.
It's a stupid example I know but it helps you to understand how callbacks are working.
function myFunc(callback) {
let errMessage = "This is error callback";
let message = "This is normal message";
callback(errMessage, message);
}
function myCallbackFunc(err, result) {
if(!err) {
console.log(result);
return;
}
console.log(err);
}
myFunc(myCallbackFunc);
You won't use it like this but If you want to send a error callback, you should null to response param. Like this:
function myFunc(callback) {
let errMessage = "This is error callback";
let message = "This is normal message";
let somethingBadHappened = true;
if(somethingBadHappened) {
return callback(errMessage, null);
}
callback(null, message)
}
function myCallbackFunc(err, result) {
if(!err) {
console.log(result);
return;
}
console.log(err);
}
myFunc(myCallbackFunc);

Node.js mssql return query result to ajax

I'm new to learning Node.js, so I'm still getting used to asynchronous programming and callbacks. I'm trying to insert a record into a MS SQL Server database and return the new row's ID to my view.
The mssql query is working correctly when printed to console.log. My problem is not knowing how to properly return the data.
Here is my mssql query - in addJob.js:
var config = require('../../db/config');
async function addJob(title) {
var sql = require('mssql');
const pool = new sql.ConnectionPool(config);
var conn = pool;
let sqlResult = '';
let jobID = '';
conn.connect().then(function () {
var req = new sql.Request(conn);
req.query(`INSERT INTO Jobs (Title, ActiveJD) VALUES ('${title}', 0) ; SELECT ##IDENTITY AS JobID`).then(function (result) {
jobID = result['recordset'][0]['JobID'];
conn.close();
//This prints the correct value
console.log('jobID: ' + jobID);
}).catch(function (err) {
console.log('Unable to add job: ' + err);
conn.close();
});
}).catch(function (err) {
console.log('Unable to connect to SQL: ' + err);
});
// This prints a blank
console.log('jobID second test: ' + jobID)
return jobID;
}
module.exports = addJob;
This is my front end where a modal box is taking in a string and passing it to the above query. I want it to then receive the query's returned value and redirect to another page.
// ADD NEW JOB
$("#navButton_new").on(ace.click_event, function() {
bootbox.prompt("New Job Title", function(result) {
if (result != null) {
var job = {};
job.title = result;
$.ajax({
type: 'POST',
data: JSON.stringify(job),
contentType: 'application/json',
url: 'jds/addJob',
success: function(data) {
// this just prints that data is an object. Is that because I'm returning a promise? How would I unpack that here?
console.log('in success:' + data);
// I want to use the returned value here for a page redirect
//window.location.href = "jds/edit/?jobID=" + data;
return false;
},
error: function(err){
console.log('Unable to add job: ' + err);
}
});
} else {
}
});
});
And finally here is the express router code calling the function:
const express = require('express');
//....
const app = express();
//....
app.post('/jds/addJob', function(req, res){
let dataJSON = JSON.stringify(req.body)
let parsedData = JSON.parse(dataJSON);
const addJob = require("../models/jds/addJob");
let statusResult = addJob(parsedData.title);
statusResult.then(result => {
res.send(req.body);
});
});
I've been reading up on promises and trying to figure out what needs to change here, but I'm having no luck. Can anyone provide any tips?
You need to actually return a value from your function for things to work. Due to having nested Promises you need a couple returns here. One of the core features of promises is if you return a Promise it participates in the calling Promise chain.
So change the following lines
jobID = result['recordset'][0]['JobID'];
to
return result['recordset'][0]['JobID']
and
req.query(`INSERT INTO Jobs (Title, ActiveJD) VALUES ('${title}', 0) ; SELECT ##IDENTITY AS JobID`).then(function (result) {
to
return req.query(`INSERT INTO Jobs (Title, ActiveJD) VALUES ('${title}', 0) ; SELECT ##IDENTITY AS JobID`).then(function (result) {
and
conn.connect().then(function () {
to
return conn.connect().then(function () {
You may need to move code around that is now after the return. You would also be well served moving conn.close() into a single .finally on the end of the connect chain.
I recommend writing a test that you can use to play around with things until you get it right.
const jobId = await addJob(...)
console.log(jobId)
Alternatively rewrite the code to use await instead of .then() calls.

Use ldapjs with bluebird promise

I posted something similar here: Use ldapjs with promise. Unfortunately, it is still unsolved.
This time I tried bluebird and hopefully I can get some luck.
// https://www.npmjs.com/package/ldapjs
var Promise = require('bluebird');
var ldap = Promise.promisifyAll( require('ldapjs') );
var config = require('./config');
var print_r = require('print_r').print_r;
var my_filter = "(&(objectCategory=person)(objectClass=user)" + "(cn=" + 'someone' + "))";
var ldap_username = config.ad.username;
var ldap_password = config.ad.password;
var ldap_url = config.ad.url;
var ldap_dn_search = config.ad.dn_search;
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
ldap.Attribute.settings.guid_format = ldap.GUID_FORMAT_B;
var opts = {
filter: my_filter,
scope: 'sub',
};
//test
console.log(my_filter);
console.log(ldap_username);
console.log(ldap_password);
console.log(ldap_url);
console.log(ldap_dn_search);
/* NOTE: This code is working!!!
client.bind(ldap_username, ldap_password, function (err) {
client.search(ldap_dn_search, opts, function (err, search) {
search.on('searchEntry', function (entry) {
var user = entry.object;
console.log(user);
});
});
});
*/
// I tried to rewrite the code above with promise
ldap.createClientAsync({
url: ldap_url
})
.then(function(client){
console.log('bind'); // No print
return client.bindAsync(ldap_username, ldap_password);
})
.then(function() {
console.log('search'); // No print
return client.searchAsync(ldap_dn_search, opts);
})
.then(function(search) {
// No flow here
search.on('searchEntry', function (entry) {
var user = entry.object;
console.log(user);
});
})
The script doesn't output anything. It is waiting for something in terminal.
Using Bluebird Promises, the easy way to do this is to create your client normally, and then run the promisifyAll() on the client.
var ldap = require('ldapjs');
var Promise = require('bluebird');
var client = ldap.createClient({
url: 'ldap://my-server:1234',
});
Promise.promisifyAll(client);
Now you can call client.addAsync() and client.searchAsync() and such.
client.bindAsync(secUserDn, secUserPassword)
.then(doSearch) // if it works, call doSearch
.catch(function (err) { // if bind fails, handle it
console.error('Error on bind', err)
});
function doSearch(data) {
client.searchAsync('CN=A Test,OU=Users,DC=website,DC=com', options)
.then(function (data) { // Handle the search result processing
console.log('I got a result');
})
.catch(function (err) { // Catch potential errors and handle them
console.error('Error on search', err);
});
}

Node/Express - How to wait until For Loop is over to respond with JSON

I have a function in my express app that makes multiple queries within a For Loop and I need to design a callback that responds with JSON when the loop is finished. But, I'm not sure how to do this in Node yet. Here is what I have so far, but it's not yet working...
exports.contacts_create = function(req, res) {
var contacts = req.body;
(function(res, contacts) {
for (var property in contacts) { // for each contact, save to db
if( !isNaN(property) ) {
contact = contacts[property];
var newContact = new Contact(contact);
newContact.user = req.user.id
newContact.save(function(err) {
if (err) { console.log(err) };
}); // .save
}; // if !isNAN
}; // for
self.response();
})(); // function
}; // contacts_create
exports.response = function(req, res, success) {
res.json('finished');
};
There are a few problems with your code besides just the callback structure.
var contacts = req.body;
(function(res, contacts) {
...
})(); // function
^ you are redefining contacts and res in the parameter list, but not passing in any arguments, so inside your function res and contacts will be undefined.
Also, not sure where your self variable is coming from, but maybe you defined that elsewhere.
As to the callback structure, you're looking for something like this (assuming contacts is an Array):
exports.contacts_create = function(req, res) {
var contacts = req.body;
var iterator = function (i) {
if (i >= contacts.length) {
res.json('finished'); // or call self.response() or whatever
return;
}
contact = contacts[i];
var newContact = new Contact(contact);
newContact.user = req.user.id
newContact.save(function(err) {
if (err)
console.log(err); //if this is really a failure, you should call response here and return
iterator(i + 1); //re-call this function with the next index
});
};
iterator(0); //start the async "for" loop
};
However, you may want to consider performing your database saves in parallel. Something like this:
var savesPending = contacts.length;
var saveCallback = function (i, err) {
if (err)
console.log('Saving contact ' + i + ' failed.');
if (--savesPending === 0)
res.json('finished');
};
for (var i in contacts) {
...
newContact.save(saveCallback.bind(null, i));
}
This way you don't have to wait for each save to complete before starting the next round-trip to the database.
If you're unfamiliar with why I used saveCallback.bind(null, i), it's basically so the callback can know which contact failed in the event of an error. See Function.prototype.bind if you need a reference.

error while using async in node.js

I am trying write a restapi using express framework and node.js. I am facing an error which I am unable to find out the root cause. I am getting the following error while trying to execute the code :
TypeError: Cannot read property 'node_type' of undefined where 'node_type' is a value that comes from a function
var GdbProcess = require('../../dao/gdb/processnds')
var mongo = require('mongodb');
var async = require('async');
exports.executeService = function(req,res){
//Make the process object to query
var manualProcessQuery = new Object();
manualProcessQuery.index = req.params.processmap;
manualProcessQuery.key = "pid";
manualProcessQuery.value = req.params.pid;
manualProcessQuery.event = req.params.event;
var tempDataNodeToExecute = new Object();
//This function returns an object (dataNodeToExecute) to execute
GdbProcess.getParametersbyNode(manualProcessQuery,function(err,dataNodeToExecute){
if(err) res.send(err);
tempDataNodeToExecute = dataNodeToExecute;
var isSystem = false;
if (tempDataNodeToExecute.node_type =="system"){
isSystem = true;
}
var count = 0;
async.whilst(
function () { return isSystem },
function (callback) {
//execute the function
executeSystem(dataNodeToExecute,function(err,executionStatus){
if (err) callback(err);
count++;
if(executionStatus=="completed"){
manualProcessQuery.value = tempDataNodeToExecute.pid;
manualProcessQuery.event = "completed";
GdbProcess.getParametersbyNode(manualProcessQuery,function(err,dataNodeToExecute2){
if(err) callback(err);
tempDataNodeToExecute = dataNodeToExecute2;
if (tempDataNodeToExecute.node_type == "manual"){
isSystem = false;
}
});
callback();
}
});
},
function (err) {
if(err) res.send(err);
res.send("success");
}
);
});
}
var executeManual = function(prosNodeToExecute,callback){
//do something
callback (null);
}
var executeSystem = function(prosNodeToExecute,callback){
//do something
callback(null,"completed");
}
When I debug the code, i clearly see that node_type is available. Can someone help me to find the root problem here ?
remove the new object tempDataNodeToExecute and use dataNodeToExecute instead of it, and it is a good practice to check for null of an object before using its property so that the program does not crashes.

Categories