Access variable outside a function in Node js [duplicate] - javascript

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 2 years ago.
var count=0;
function test2(callback) {
db.doc("Kerala/Pathanamthitta")
.listCollections()
.then((snap) => {
snap.forEach((collection) => {
var col = collection.id;
db.collection(`Kerala/Pathanamthitta/${col}`)
.where("completionStatus", "<", 3)
.get()
.then((snapshot) => {
snapshot.forEach((doc) => {
var data = doc.data();
console.log(data.place);
if (data.completionStatus == 0) count++;
});
});
});
})
.then(callback);
}
test2(function () {
console.log(count);
});
I want to print the final count after the execution of test2 function. It prints the statement but always 0, even if any updation happens inside function test2. I tried to do a callback() but still the same happens
Please help. Thanks in advance

You're misunderstanding how asynchronous functions and promise chains works. You're calling a promise chain and the callback right after each other.
db.doc(...)...
callback()
This ends up executing like so:
db.doc
callback
db.doc.then
at this point you have called callback BEFORE the resolution of the promise chain. You want to put the callback in the promise chain so that it is delayed until after all of that has finished. A good spot would be in another promise chain after the outer loop for a single log of the eventual count.
...
.then(snap => {
snap.forEach(collection => {...});
})
.then(callback);
...
This way after you've finished going over all of the snaps and finished counting the snapshots you'll print out the count in the correct order after both traversals.
BUT WAIT it's still printing 0. Why is that? Well we're not properly chaining our promises. We'll need to make sure that any promises we create in a promise properly chain so that when we do get to the logging step we've got a proper chain going.
Full code:
var count = 0;
function test2(callback) {
db.doc("Kerala/Pathanamthitta")
.listCollections()
.then((snap) => {
return Promise.all(
snap.map((collection) => {
var col = collection.id;
return db
.collection(`Kerala/Pathanamthitta/${col}`)
.where("completionStatus", "<", 3)
.get()
.then((snapshot) => {
return Promise.all(
snapshot.map((doc) => {
var data = doc.data();
console.log(data.place);
if (data.completionStatus == 0) count++;
})
);
});
})
);
})
.then(callback);
}
test2(function () {
console.log(count);
});

As per my comment, you could just make a function call after you're done counting.
//content above
snapshot.forEach(doc => {
var data = doc.data();
console.log(data.place);
if (data.completionStatus == 0) count++;
});
functionTwo(count);
// content below
functionTwo(count) {
console.log(count);
}

Related

Resolve promise on array push - javascript

I'm attempting to define a function that returns a promise. The promise should resolve when a given array is set (push()).
To do this I'm attempting to use a Proxy object (influenced by this):
let a = []
;(async function(){
const observe = array => new Promise(resolve =>
new Proxy(array, {
set(array, key, val) {
array[key] = val;
resolve();
}
}));
while(true){
await observe(a);
console.log(new Date().toLocaleTimeString(),"Blimey Guv'nor:",`${a.pop()}`);
}
})(a);
;(async function(){
await new Promise(resolve => timerID = setTimeout(resolve, 2000))
a.push('ʕ·͡ᴥ·ʔ');
a.push('¯\(°_o)/¯ ')
})(a)
I can't see why this doesn't work. Does anyone have any idea?
More generally, what is a good way to have a promise resolve on push to an array?
The problems with your attempt:
you invoke .push on the original array, not the proxied one. Where you create the proxy, it is returned to no-one: any reference to it is lost (and will be garbage collected).
The code following after the line with await will execute asynchronously, so after all of your push calls have already executed. That means that console.log will execute when the array already has two elements. Promises are thus not the right tool for what you want, as the resolution of a promise can only be acted upon when all other synchronous code has run to completion. To get notifications during the execution synchronously, you need a synchronous solution, while promises are based on asynchronous execution.
Just to complete the answer, I provide here a simple synchronous callback solution:
function observed(array, cb) {
return new Proxy(array, {
set(array, key, val) {
array[key] = val;
if (!isNaN(key)) cb(); // now it is synchronous
return true;
}
});
}
let a = observed([], () =>
console.log(new Date().toLocaleTimeString(),"Blimey Guv'nor:", `${a.pop()}`)
);
a.push('ʕ·͡ᴥ·ʔ');
a.push('¯\(°_o)/¯ ');
As noted before: promises are not the right tool when you need synchronous code execution.
When each push is executed asynchronously
You can use promises, if you are sure that each push happens in a separate task, where the promise job queue is processed in between every pair of push calls.
For instance, if you make each push call as part of an input event handler, or as the callback for a setTimeout timer, then it is possible:
function observed(array) {
let resolve = () => null; // dummy
let proxy = new Proxy(array, {
set(array, key, val) {
array[key] = val;
if (!isNaN(key)) resolve();
return true;
}
});
proxy.observe = () => new Promise(r => resolve = r);
return proxy;
}
let a = observed([]);
(async () => {
while (true) {
await a.observe();
console.log(new Date().toLocaleTimeString(),"Blimey Guv'nor:",`${a.pop()}`);
}
})();
setTimeout(() => a.push('ʕ·͡ᴥ·ʔ'), 100);
setTimeout(() => a.push('¯\(°_o)/¯ '), 100);

How to enforce promise in array loop

I'm trying to get the basics of how to implement promises and Promise.All within a given array loop, for example, from a firebase query, which can perform any number of actions within the loop, and then I have access to the results later. I can't seem to get the correct syntax, logic . below is a sample method in which I'd like to delay insert some items into an array and then have access to the array after the loop for processing.
I've researched different methods on Stackoverfllow. Some assign a promises = the snap.forEach loop and then somehow resolve this after completion. Others are creating a promise inside the loop. In my example below, I'm just using the settimeout to delay/create an async process.
testfirebaseData = () => {
let channels =[];
var promises=[];
firebase.database().ref('challengeResponses').child(day).once('value', snap => {
snap.forEach(channelChild => {
promises.push(new Promise((resolve) => {
setTimeout(() => {
channels.push(channelChild.key);
resolve(channels)
},1000)
})
)
})
}).then (() => {
Promise.all(promises).then(() => {
console.log(channels[0])
})
}
}
I would expect the output of the above to show the first element in the "channels" array, but it's always coming back with "undefined"..as I clearly am not processing the promises correctly. What am i missing?
Here is sample example using your code style. Instead of objects within the array I've put numbers;
https://jsfiddle.net/th3vecmg/4/
var promises = [];
let channels =[];
[1, 2, 3, 4, 5].forEach((elm, i) => {
promises.push(new Promise((resolve) => {
setTimeout(() => {
channels.push(elm)
resolve(elm);
}, 1000)
}))
});
console.log(channels[0]) // gives undefined
Promise.all(promises).then(() => {
console.log(channels[0]) // gives 1
})
You should not be getting undefined inside the then because it's waiting for the promise to execute. You will only get undefined it you are trying to display the result outside the then scope
The code doesn't make really sense since you are displaying a global variable inside the then rather then relying on the output of the promise
If you want to delay execution by 1 then do some multiplication in the timeArgument of the setTimeout...
Your new Promise(...) should wrap the firebase call as that is the async operation. And not be in the callback. There is no async operations going on inside the callback just plain array push which is a sync op
var promise = new Promise((resolve)=>{
firebase.database().ref('challengeResponses').child(day).once('value', snap => {
var channels=[];
snap.forEach(channelChild => {
channels.push(channelChild.key);
});
resolve(channels);
});
});
promise.then((channels)=>{
console.log(channels);
});
If you want a delay on the whole operation:
var promise = new Promise((resolve)=>{
firebase.database().ref('challengeResponses').child(day).once('value', snap => {
var channels=[];
setTimeout(()=>{
snap.forEach(channelChild => {
channels.push(channelChild.key);
});
resolve(channels);
},1000);
});
});
To simulate nested async operations you would add the promises to an array like you were but resolve Promises.all() in the outer promise and resolve your data in the inner promises:
var promise = new Promise((resolve)=>{
firebase.database().ref('challengeResponses').child(day).once('value', snap => {
var snapPromises=[];
snap.forEach(channelChild => {
snapPromises.push(new Promise((snapresolve)=>{
setTimeout(()=>{
snapresolve(channelChild.key);
},1000)
});
});
resolve(Promise.all(snapPromises));
});
});

return a value from setTimeout() [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I want to return the status and store its value in variable s.
I will appreciate some help please.
here is my code:
let s = setTimeout( ()=>{
this.matchService.getMatches().subscribe(ms => {
this.matches = ms;
let match = this.matches.find(match => match.id == id);
let status = match.status;
if(status == 'closed' || status == 'live') {
this.status.name = status;
}
return status;
});
},9000);
}
This answer here is specifically for the setTimeout question. If you work with an observable, consider the answer of bambam!
Ok, this answer might be a little weird if you don't know the concept of async.
Basically the easiest is to wrap your setTimeout into a Promise, like so:
const someTimeoutAction = () => {
// Let's return a new Promise, promising to eventually return a value
return new Promise((resolve) => {
setTimeout(() => {
resolve('hello world');
}, 1000);
});
};
// The following function is an async function, that will
// help to work better with Promises.
const mainFunction = async () => {
console.log('Waiting for status');
const status = await someTimeoutAction();
console.log(status);
};
mainFunction();
So what happens here?
The mainFunction is called and calls someTimeoutAction.
The someTimeoutAction returns a promise. The old syntax looks a bit different. This medium article document should be a good starting point.
mainFunction waits for the Promise to resolve. After a second it is resolved and the value is written to status.
Everything else just continues now like usual.
The code above only works for modern browsers. It does not work without a transpiler for e.g. IE11.
However, the old syntax works just fine:
function someTimeoutAction() {
// Let's return a new Promise, promising to eventually return a value
return new Promise((resolve) => {
setTimeout(() => {
resolve('hello world');
}, 1000);
});
};
// The following function is an async function, that will
// help to work better with Promises.
function mainFunction() {
console.log('Waiting for status');
someTimeoutAction()
.then(function(status) {
console.log(status);
});
};
mainFunction();
Since you already have an observable, simply delay it instead of using setTimeout! Also the promise approach from the other answer is nonsense for this scenario.
this.matchService.getMatches()
.pipe(delay(9000))
.subscribe(ms => {
this.matches = ms;
let match = this.matches.find(match => match.id == id);
let status = match.status;
if(status == 'closed' || status == 'live') {
this.status.name = status;
}
});
The bigger problem with your code is that you would never never return from subscribe. Actually you would rather delay the (I'm guessing) http call in matchService, but you didn't show the relevant code.
Note that a subscription may get triggered multiple times, again depending on what getMatches() is. You're on the wrong path here and should update your question so we can tailor you a real solution.

How can I chain Promise functions with dynamic length?

I have variable count of data in an array. I want something, to do this:
function tryThis(num: number): Promise<any> {
return new Promise ((resolve, reject) => {
if(num == 3){
resolve()
}else{
reject();
}
}
}
let arrayOfSomething : Array<number> = [1,2,3,4];
let chain;
// create the chain
arrayOfSomething.forEach( (element) => {
// add element to the chain
chain.catch(() => {
tryThis(element);
});
});
// run chain
chain
.then(() => {
// one of elemnts of the array was 3
})
.catch(() => {
// no "3" found in array
})
So, my goal is to create a Promise chain form an array with variable count of data and at the end catch if all of tryThis() function gives reject. If one of tryThis() function gives resolve in the chain, then jump to the end and exit with resolve.
I know, that my code is not correct, this is just to show what I want to do.
Can anyone help me, to do this?
Thanks!
Maybee you could use Promise.any from bluebird, or similar functionality from some other library: https://www.npmjs.com/package/promise.any or https://www.npmjs.com/package/promise-any.
Promise.any is rejected if all elements are rejected. So, from array of numbers you need to create array of promises, and then call Promise.any. So for example:
let arrayOfSomething = [1,2,3,4];
Promise.any(arrayOfSomething.map(x => tryThis(x))
.then(() => {
// one of elemnts of the array was 3
})
.catch(() => {
// no "3" found in array
})

Identify when nested FOR loops and IF statement with one asynchronous function call are done

I have a function with nested for-loops and IF statements and one call of an asynchronous function. The structure looks similar to this:
search(){
// for loop 1
for (let i in object){
// IF statement 1
if (condition1){
// for loop 2
for (let o in object[i]){
// IF statement 2
if (condition2){
// call of an asynchronous function
this.asyncFunction().then( data => {
this.result.push(data)
});
}
}
}
}
}
I want to call one function (let's say goToNextPage() ) as soon as all loops are done and all async-function calls are completed and, thus, my this.result object is completed.
However, I don't know how to manage this. I tried to work with counters, to know when the last object[i] is handled and when the last object[i][o] was handled to call goToNextPage() afterwards. But this doesn't work properly, if the last object fails one if statement. Then goToNextPage() is called, while the async function of the second to last object is still running.
And I can't just call goToNextPage() within
this.asyncFunction().then( data => {
this.result.push(data)
// call it here, if last element is reached
});
since then it would not be called if the last element doesn't meet one of the IF statements.
I hope it's understandable what my problem is and that there is a solution for this...
Thank you for your time!
Koleman answered this perfectly on the official Ionic forum and thereby helped me with the solution: Link
His approach is to insert promises in the structure and to use Promise.all(promises).then(...) to wait until all promises are resolved.
search(){
let promises = [];
// for loop 1
for (let i in object){
// IF statement 1
if (condition1){
// for loop 2
for (let o in object[i]){
// IF statement 2
if (condition2){
let promise = new Promise((resolve, reject) => {
this.asyncFunction().then( data => resolve(data));
});
promises.push(promise);
}
}
}
}
return new Promise((resolve, reject) => {
if(!promises.length){
reject('Something went worng!');
}else{
Promise.all(promises).then((results) => {
this.result = this.result.concat(results);
resolve('We done it!');
});
}
});
}
search().then(
successText => {
console.log(statusText);
console.log(this.result);
},
failText => {
console.log(failText);
}
);

Categories

Resources