I'm working with callbacks to improve my program's efficiency. I would like to wait for my 'a' variable to get the value from callback if it hasn't already at some point. My code looks like this:
function myFunction(callback){
request("url", function(error, response, body) {
if (!error && response.statusCode == 200)
{
result = JSON.stringify(JSON.parse(body));
return callback(null, result);
}
else
{
return callback(error, null);
}
});
}
var a = myFunction(function(err, data){
if(!err)
{
return(data);
}
else
{
return(err);
}
});
//A CHUNK OF CODE EXECUTES HERE
//I'D LIKE TO CHECK IF THE a VARIABLE GOT THE VALUE (if the callback was executed), note that I don't want to nest the chunk inside the callback, but rather I'd like to check that outside function callback via IF statement/loop or some similar alternative
//MORE CODE CONNECTED TO THE CALLBACK
Any ideas on how I can wait for the variable to get the value if it didn't get it already by the time that the chunk of code is executed? I know that the sole intention of using callbacks is to not wait but in this instance it is necessary for me so just don't bother being annoying please :)
I'd suggest trying async/await and the request-promise-native module, this keeps the syntax very simple. If the call fails for some reason an error will be thrown, this is easy to catch:
For example:
const rp = require('request-promise-native');
async function testAsyncRequest() {
try {
let promise = rp('https://my-json-server.typicode.com/typicode/demo/posts');
// Do some stuff here...
let result = await promise;
console.log("Result: ", result);
} catch (err) {
console.error(`Something went wrong: ${err.message}`);
}
}
testAsyncRequest();
So I've ended up making a little compromise (had to nest), but the code works as intended:
var something;
function myFunction(callback){
request("url", function(error, response, body) {
if (!error && response.statusCode == 200)
{
result = JSON.stringify(JSON.parse(body));
return callback(null, result);
}
else
{
return callback(error, null);
}
});
}
var a = myFunction(function(err, data){
if(!err)
{
something = data;
return(data);
}
else
{
return(err);
}
});
//A CHUNK OF CODE EXECUTES HERE
var something_cachedValue=something;
doStuff();
function doStuff() {
if(something===something_cachedValue) {
setTimeout(doStuff, 10);
return;
}
something_cachedValue = something;
//MORE CODE CONNECTED TO THE CALLBACK
}
So basically I'm checking if the callback has completed every 10ms by re-calling the function. When it's completed, the "//MORE CODE CONNECTED TO THE CALLBACK" executes. Not the cleanest and most fancy way to do it but it does the job.
Related
I have been trying to read values from an ultrasonic sensor.
the code i am using
//callbacks; got from a previous post
var gpio_read = function (channel) {
new Promise(resolve => {
gpio.read(channel, function (error, result) {
console.log('gpio.read', error, result);
resolve(result);
});
});
}
//
var off = function () {
gpio.write(trig, 0);
}
tank.getDistance = function () {
var start, stop;
gpio.write(trig, 0);
gpio.write(trig, 1);
setTimeout(off, 10);
while (gpio_read(echo) === 0) {
start = Date.now();
console.log("nosig");
}
while (gpio_read(echo) === 1) {
stop = Date.now();
console.log("sig");
}
console.log(stop - start);
};
// pin setup
tank.initPins = function () {
async.parallel([
gpio.setup(p7, gpio.DIR_OUT),
gpio.setup(p11, gpio.DIR_OUT),
gpio.setup(p13, gpio.DIR_OUT),
gpio.setup(p15, gpio.DIR_OUT),
gpio.setup(echo, gpio.DIR_IN),
gpio.setup(trig, gpio.DIR_OUT)
]);
};
i wrote similar python code and i get values back but here i get
gpio.read null false
gpio.read null true
I dont know why ?
I though it was due to busy pins so i tried resetting them before use and all. Any ideas?
You don't need to wrap the gpio.read in a Promise.
var gpio_read = function (channel) {
gpio.read(channel, function (error, result) {
console.log('gpio.read', error, result);
return result;
});
}
The way you had it written (new Promise(...)) would just create a Promise but never actually return it to the calling function. For that, you would need return new Promise(...) and to change the calling code to wait for the promise (eg gpio_read.then(fn)).
In the case of gpio.read it will already return a value once it is read from the board so you don't need to wrap it in a promise.
I'm trying my damndest to avoid callback hell with my Node JS. But I'm trying to make a large number of api-requests and insert these into my database.
My issue here (of course) is that my for-loop runs and increments i before I finish my request and database insertion.
for(var i = 0; i <= 1 ; i++){
apiRequest = data[i];
apicall(apiRequest);
}
function apicall(urlApi){
request((urlApi), function(error, response, body){
if(error){
console.log("error");
} else if(!error && response.statusCode == 200){
var myobj = JSON.parse(body);
dbInsert(myobj);
}
});
}
function dbInsert(obj) {
//insert into database
}
If someone else would come by this question I can truly recommend this blogpost which I found after reading the response by joshvermaire:
http://www.sebastianseilund.com/nodejs-async-in-practice
There are a number of ways to approach this type of problem. Firstly, if you can run all the API calls in parallel (all in flight at the same time) and it doesn't matter what order they are inserted in your database, then you can get a result a lot faster by doing that (vs. serializing them in order).
In all the options below, you would use this code:
const rp = require('request-promise');
function apicall(urlApi){
return rp({url: urlApi, json: true}).then(function(obj){
return dbInsert(obj);
});
}
function dbInsert(obj) {
//insert into database
// return a promise that resolves when the database insertion is done
}
Parallel Using ES6 Standard Promises
let promises = [];
for (let i = 0; i <= data.length; i++) {
promises.push(apicall(data[i]));
}
Promise.all(promises).then(() => {
// all done here
}).catch(err => {
// error here
});
Parallel using Bluebird Promise Library
With the Bluebird Promise library, you can use Promise.map() to iterate your array and you can pass it the concurrency option to control how many async calls are in flight at the same time which might keep from overwhelming either the database or the target API host and might help control max memory usage.
Promise.map(data, apiCall, {concurrency: 10}).then(() => {
// all done here
}).catch(err => {
// error here
});
In Series using Standard ES6 Promises
If you have to serialize them for some reason such as inserting into the database in order, then you can do that like this. The .reduce() pattern shown below is a classic way to serialize promise operations on an array using standard ES6:
data.reduce(data, (p, item) => {
return p.then(() => {
return apicall(item);
});
}, Promise.resolve()).then(() => {
// all done here
}).catch(err => {
// error here
});
In Series Using Bluebird Promises
Bluebird has a Promise.mapSeries() that iterates an array in series, calling a function that returns a promise on each item in the array which is a little simpler than doing it manually.
Promise.mapSeries(data, apiCall).then(() => {
// all done here
}).catch(err => {
// error here
});
I'd recommend using something like async.each. Then you could do:
async.each(data, function(apiRequest, cb) {
apicall(apiRequest, cb);
}, function(err) {
// do something after all api requests have been made
});
function apicall(urlApi, cb){
request((urlApi), function(error, response, body){
if(error){
console.log("error");
cb(error);
} else if(!error && response.statusCode == 200){
var myobj = JSON.parse(body);
dbInsert(myobj, cb);
}
});
}
function dbInsert(obj, cb) {
doDBInsert(obj, cb);
}
When the dbInsert method completes, make sure the cb callback is called. If you need to do this in a series, look at async.eachSeries.
Hello I have to break from the while loop. In that while loop I am calling an asynchronous function. I have to check if a certain field from the output of that async call is empty then I have to break from while else I will call again to that async function. I have tried this :
var options = {
headers : {
'Fk-Affiliate-Id':'xxxxxxx' ,
'Fk-Affiliate-Token' : 'xxxxxxxxxxxxxxxx'
}
};
var state = ['approved','tentative','cancelled','disapproved'];
state.forEach(element => {
options.url = 'https://affiliate-api.flipkart.net/affiliate/report/orders/detail/json?startDate='+startDate+'&endDate='+endDate+'&status='+element+'&offset=0';
loop : while(true){
// This is the async call
request.get(options, (err, res, body) => {
var data = JSON.parse(body);
console.log(data);
// I have to check whether next is empty or not ?
if(data.next === ''){
// I will perform some action on data here
break loop;
}
else{
// I will perform some action on data here
options.url = data.next;
}
});
}
});
But this showing error Unsyntactic break. How to break from while loop?
Seems like you don't need while loop there. You just want to stop when you reach desired result for one of the states.
It means you need to wait until async call completes, and only after that proceed with another state. One of the solutions is to make calls synchronous (if possible).
Another solution is to create separate function for each state processing, and call it from async call callback:
var state = ['approved','tentative','cancelled','disapproved'];
// starting with first state
processState(0);
function processState(stateIdx){
if(stateIdx >= state.length){
// we tried all states and no success.
return;
}
// some code
request.get(options, (err, res, body) => {
// some code
if(data.next !== ''){
// we have more records for this state - call it one more time.
processState(stateIdx);
} else {
// done with this state, try next one.
processState(stateIdx + 1);
}
});
}
My goal is to make a simple JSON parser which runs continuously.
As I imagined it - an infinity loop makes a request and stores data in my DB. Simple.
But there always were memory leaks.
What is the proper way to make things run repeatedly over and over again in Node.js? What is actually memory leak? Why and how is it happening? I have tried it to do with setInterval, setTimeOut, processNextTick, setImmediate, promises but there is always the same result! I am obviously something missing.
What I get now:
function getItems(callback) {
request({
url: 'http://foo.com',
json: true
}, function (error, response, body) {
if (!error && response.statusCode == 200 && body) {
var total_count = body.total_count;
var body = body.results_html;
...
setTimeout(function() {callback(body}, 1000);
}
});
}
function series(item) {
if (item) {
getItems(function(result) {
console.log(result);
return series(true);
});
} else {
return;
}
}
series(true);
It's possible I don't understand Node's event loop well enough.
Say I have a function foo which contains an asynchronous function async_func. Do I have
//1
function foo(callback) {
//stuff here
async_func(function() {
//do something
callback();
});
//this eventually get executed
}
or
//2
function foo(callback) {
//stuff here
async_func(function() {
//do something
return callback();
});
//never executed
}
Actually, in your sample 2, //never executed will be execute every time. It's returning from the callback, not from the wrapping function.
Sometimes the caller actually expects some return value and the behavior can change based on that. Another common reason to see a return callback() is just a clear way of short circuiting the function you're in. For example.
function doSomething(callback) {
something(function(err, data) {
if(err) return callback(err);
// Only run if no error
});
// Always run
}
Even though the return value isn't being used, it's using return to ensure that execution doesn't continue past the error conditional. You could just as easily write it this way which has the same effect.
function doSomething(callback) {
something(function(err, data) {
if(err) {
callback(err);
return;
}
// Only run if no error
});
// Always run
}