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);
}
});
}
Related
I have the following async function and want to have the value data.
I tried with Promises and Async/Awaits. I always miss something. Would be happy for some help. Thanks!
Here the async function:
ad.getData("ID1", function (err, res) {
let data = 11111;
if ((res!= null) && (res.val != null)){
data = res.val;
} else {
// store data
}
});
and I tried:
async function getData(id){
return await ad.getData(id, function (err, res) {
let data = 11111;
if ((res!= null) && (res.val != null)){
data = res.val;
} else {
// store data
}
return data;
})
}
let data = await getData("Id1");
console.log(data);
This is an asynchronous operation, and the code is written in callback style.
ad.getData("ID1", function (err, res) { // the function is a callback
let data = 11111;
if ((res!= null) && (res.val != null)){
data = res.val;
} else {
// store data
}
});
In order to get the result outside the function, or in general, write asynchronous code that look like a synchronous one, we can wrap callback style code in a Promise, like this :
function getDataAsync() {
return new Promise ((resolve, reject) => {
// callback style code is wrapped in the Promise
ad.getData("ID1", function (err, res) {
let data = 11111;
if ((res!= null) && (res.val != null)){
data = res.val;
} else {
// store data
}
resolve(data); // it means, when the Promise is resolved, it will give you the data
});
And you can get the output of getDataAsync like this :
let data = await getDataAsync("Id1");
// we use "await" keyword to wait for the Promise is resolve, data is the variable in the resolve
console.log(data);
You should use Promise instead of callback, and async/await will help you write Promise code easier.
Please take a deep look at https://scotch.io/courses/10-need-to-know-javascript-concepts/callbacks-promises-and-async. When you understand the concept, it's literally easy.
i need to query multiple pages from another api and return a object from the page if a specified value matches.
i guess the problem is that the loop is done asychron because i always get "not found" and later i get "Cannot set headers after they are sent to the client" if the loop found the object.
solved this by calling it recursive but i need more speed because there are many pages with many entries. if possible requests should run parallel but not "found should" be called after all loops finished
router.post('/search', function (req, res) {
var foundObj = false;
for (var page = 1; page < req.body.cubesize; page++) {
request({
method: 'GET',
uri: 'http://localhost:8080/api/v1/getpage/json/' + page
},
function (error, response, body) {
if (!error) {
var result = JSON.parse(body);
for (var obj in result) {
console.log(result[obj]);
if (result[obj].hasOwnProperty(req.body.field)) {
if (result[obj][req.body.field] == req.body.value) {
foundObj = true;
return res.status(200).send(result[obj]);
}
}
}
}
});
}
if(!foundObj){
return res.status(404).send("not found");
}
});
anyone got an idea how to fast loop all pages with all entries but wait for calling not found?
As long as you have a res.send() inside a for loop and at least two matches occurs, two (at least) res.send() calls will be executed and an error will rise.
How to run in parallel ?
router.post('/search', function (req, res) {
const callApi = (page) => new Promise( (resolve, reject) => {
request({
method: 'GET',
uri: `http://localhost:8080/api/v1/getpage/json/${page}`,
},
function (error, response, body) {
if (error) reject(null)
let result = JSON.parse(body);
for (var obj in result) {
console.log(result[obj]);
if (result[obj].hasOwnProperty(req.body.field)) {
if (result[obj][req.body.field] == req.body.value)
return resolve(result[obj]);
}
}
return reject(null);
}
});
});
const promisesArr = [];
for ( let page = 1; page < req.body.cubesize; page++) {
promisesArr.push(callApi(page))
}
Promise.allSettled(promisesArr).then((resArr)=>{
const resolvedArray = resArr.filter(val => !!val);
if (resolvedArray.length === 0) return res.status(404).send("not found");
if (resolvedArray.length === 1)
return res.status(200).send(resolvedArr[0][obj])
if (resolvedArray.length > 1)
return res.status(500).send("Too many matches")
// It is not clear to me in your code what you need to do in case more than one resolves
});
});
Some explanation about the code.
The idea is to promisify request and run in parallel
To run in parallel, Promise object allows four methods:
Promise.all, Promise.race and Promise.allSettled and Promise.any. The two last ones, Promise.allSettled and Promise.any are not fully compatible, so keep this in mind.
Once you have the array and run in parallel, Promise.all and Promise.allSettled returns an array of results. The array is filtered and if some value matchs, it response that, otherwise, response 404.
Further information about promises will be required to select the right one for your specific case. You can found about it here[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise]
Unfortunately my code is not tested, so please review it and refactor to adapt to your specific case.
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.
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 writing a function that's returning and array of values. Some of the values are calculated in a callback. But I don't know how to make the program asynchronious so all of my results are in the array, and not added after they're returned.
let array = []
for (stuff : stuffs) {
if (condition) {
array.add(stuff)
} else {
api.compute(stuff, callback(resp) {
array.add(resp.stuff)
}
}
}
res.json({ "stuff": array })
In this example the array is written to the response before the async calls have finished.
How can I make this work asynchronously?
You have to use one of the approaches:
async library
Promise.all
coroutines/generators
async/await
The most cool yet, I think, is async/await. First we modify your function, so it returns a promise:
const compute = function(stuff) {
return new Promise( (resolve, reject) => {
api.compute(stuff, callback(resp){
resolve(resp.stuff)
});
});
};
Then we modify your route with async handler:
app.get('/', async function(req, res, next) {
const array = [];
for (const stuff of stuffs) {
if (condition) {
array.add(stuff);
} else {
const stuff = await compute(stuff);
array.push(stuff);
}
}
res.json({ stuff: array });
});
Note: You might need to update node version to latest.
UPDATE:
Those who are not awared, how event loop works, execute this snippet, and finish with that:
const sleep = async function(ms) {
console.log(`Sleeping ${ms}ms`);
return new Promise( resolve => setTimeout(resolve, ms));
};
async function job() {
console.log('start');
for (let t = 0; t < 10; t++) {
await sleep(100);
}
}
job();
console.log('oops did not expect that oO');
You will be surprised.
Here is an answer without package using callbacks
Create a function that's gonna recursively treat all your stuffs.
getArray(stuffs, callback, index = 0, array = []) {
// Did we treat all stuffs?
if (stuffs.length >= index) {
return callback(array);
}
// Treat one stuff
if (condition) {
array.add(stuffs[index]);
// Call next
return getArray(stuffs, callback, index + 1, array);
}
// Get a stuff asynchronously
return api.compute(stuffs[index], (resp) => {
array.add(resp.stuff);
// Call next
return getArray(stuffs, callback, index + 1, array);
});
}
How to call it?
getArray(stuffs, (array) => {
// Here you have your array
// ...
});
EDIT: more explanation
What we want to do to transform the loop you had into a loop that handle asynchronous function call.
The purpose is that one getArray call gonna treat one index of your stuffs array.
After treating one index, the function will call itself again to treat the next index, until all get treated.
-> Treat index 0 -> Treat index 1 -> Treat index 2 -> Return all result
We are using parameters to pass the infos through the process. Index to know which array part we have to treat, and array to keep a tract of what we did calculate.
EDIT: Improvement to 100% asynchronous soluce
What we have done here it's a simple transposition of your initial for loop into an asynchronous code. it can be improved so by making it totally asynchronous, which make it better but slightly more difficult.
For example :
// Where we store the results
const array = [];
const calculationIsDone = (array) => {
// Here our calculation is done
// ---
};
// Function that's gonna aggregate the results coming asynchronously
// When we did gather all results, we call a function
const gatherCalculResult = (newResult) => {
array.push(newResult);
if (array.length === stuffs.length) {
callback(array);
}
};
// Function that makes the calculation for one stuff
const makeCalculation = (oneStuff) => {
if (condition) {
return gatherCalculResult(oneStuff);
}
// Get a stuff asynchronously
return api.compute(oneStuff, (resp) => {
gatherCalculResult(resp.stuff);
});
};
// We trigger all calculation
stuffs.forEach(x => x.makeCalculation(x));