How to make a function wait until a callback has been called using node.js
The "good node.js /event driven" way of doing this is to not wait.
Like almost everything else when working with event driven systems like node, your function should accept a callback parameter that will be invoked when then computation is complete. The caller should not wait for the value to be "returned" in the normal sense, but rather send the routine that will handle the resulting value:
function(query, callback) {
myApi.exec('SomeCommand', function(response) {
// other stuff here...
// bla bla..
callback(response); // this will "return" your value to the original caller
});
}
So you dont use it like this:
var returnValue = myFunction(query);
But like this:
myFunction(query, function(returnValue) {
// use the return value here instead of like a regular (non-evented) return value
});
Waiting until a callback is executed
Your while
loop will never let setTimeout
run. it's a synchronous infinite loop logic and will block the js thread till eternity.
setTimeout
is an asynchronous method and registers itself in event queue. When the main stack is done with it's work, event loop checks the queue for pending work and brings it back in main thread.
In your case, main stack will never be free, because while
loop will never complete.
You can use Promise
to do that.
var promise = new Promise((resolve, reject) => { setTimeout(() => { console.log('timeout executed after 2s'); resolve(); // resolve when setTimeout is done. }, 2000);});console.log('waiting');promise.then(_ => console.log("finished waiting")); // Use `.then` to do work after promise is resolved.
How to make a function which relies on a previous functions data wait until that data is set
Easiest solution is to just call your second function inside your first subscription:
ngOnInit() {
this.getActiveCities();
}
getActiveCities() {
this.fbService.getActiveCities().subscribe(
data => {
this.activeCities = data;
this.getAllFamilies();
},
error => {
console.log("Error retrieving active cities");
}
);
}
Although a better design is to keep everything as observables and subscribe with the async
pipe in html.
export class HomeComponent implements OnInit {
constructor(private fbService: FirebaseService) { }
activeFamiliesMap = new Map<string, Observable<Family[]>>();
activeCities$: Observable<any[]> = this.fbService.getActiveCities().pipe(
tap((activeCities) => {
for (const city of activeCities) {
this.activeFamiliesMap.set(city.id, this.activeFamilies(city.id));
}
}),
catchError((err) => {
console.error('Error retrieving active cities', err);
return of([]);
})
);
activeFamilies(id: any): Observable<Family[]> {
return this.fbService.getFamiliesForCity(id).pipe(
catchError((err) => {
console.error('Error retrieving families for city id:', id, err);
return of([]);
})
);
}
}
Just an example of how to display the data:
<div>Active Cities</div>
<pre>{{ activeCities$ | async | json }}</pre>
<ng-container *ngFor="let city of activeCities$ | async">
<div>City Id: {{ city.id }}</div>
<div>Families:</div>
<pre>{{ activeFamiliesMap.get(city.id) | async | json }}</pre>
</ng-container>
stackblitz: https://stackblitz.com/edit/angular-ivy-trkuqx?file=src/app/app.component.ts
And an even better design may be for your service to return an observable of the map, although it's an ugly beast of an observable. At least it hides the logic from your components:
Service
getFamilyMap(): Observable<Map<string, Family[]>> {
return this.getActiveCities().pipe(
map((activeCities) => {
return activeCities.map((city) => {
return this.getFamiliesForCity(city.id).pipe(
map((families) => {
return { id: city.id, families };
})
);
});
}),
switchMap((data) => forkJoin(data)),
map((data) => {
const res = new Map<string, Family[]>();
for (const entry of data) {
res.set(entry.id, entry.families);
}
return res;
}),
catchError((err) => {
console.error('Error retrieving family map', err);
return of(new Map<string, Family[]>());
})
);
}
Component
export class HomeComponent {
constructor(private fbService: FirebaseService) {}
activeCities$ = this.fbService.getActiveCities();
familyMapEntries$ = this.fbService
.getFamilyMap()
.pipe(map((map) => Array.from(map)));
}
I use Array.from()
rather than map.entries()
because iterators tend to throw changedAfterChecked
errors.
Html
<div>Active Cities</div>
<pre>{{ activeCities$ | async | json }}</pre>
<ng-container *ngFor="let entry of (familyMapEntries$ | async)">
<div>City Id: {{ entry[0] }}</div>
<div>Families:</div>
<pre>{{ entry[1] | json }}</pre>
</ng-container>
stackblitz: https://stackblitz.com/edit/angular-ivy-ffzpya?file=src/app/firebase.service.ts
Make callback wait till function returns result in node js
you could try something like:
if(file_is_type1(file_from_list)){
process_filetype_1(file_from_list, callback);
}
and then process_filetype_1 would look something like:
function process_filetype_1(file_from_list, callback){
async.auto({
dosth_1: function(cb) {
//logic implementation
},
dosth_2: ['dosth_1', function(cb) {
//logic implementation
}]
}, function(error, results) {
if(error) { callback (error);
} else {
callback(results);
}
});
}
How to wait for a function to finish and then call another function in ES6?
Use the promise, Luke
const prompt = require('prompt');
const properties = [{
name: 'module_name',
default: 'custom-module',
description: "Name your Application module",
type: 'string'
},
{
name: 'application_home',
description: "Provide Application Home location (absolute path)",
message: "Your Application Home location should be a clone of Application Github repo and should have package.json",
type: 'string',
required: true
}];
let module_name;
let application_home;
/**Prompt user to get mandatory details */
const getModuleDetails = async () => new Promise((resolve, reject) => {
prompt.start();
prompt.get(properties, function (err, result) {
if (err) { reject(err); } else {
module_name = result.module_name;
application_home = result.application_home;
resolve(result);
}
});
})
/**Verbose */
const verboseDetails = async () => {
await getModuleDetails()
console.log('Command-line input received:');
console.log('Module Name: ' + module_name);
console.log('Application Home location: ' + application_home);
}
const init = async () => {
//getModuleDetails();
await verboseDetails();
}
init();
Related Topics
JSON.Stringify Output to Div in Pretty Print Way
Should I Use Window.Navigate or Document.Location in JavaScript
Most Elegant Way to Force a Textarea Element to Line-Wrap, *Regardless* of Whitespace
Convert Relative Path to Absolute Using JavaScript
How to Get Old Value with Onchange() Event in Text Box
How to Separate Web Components to Individual Files and Load Them
Detect Click Event Inside Iframe
How to Save and Restore a File Object in Local Storage
Opening New Window in HTML for Target="_Blank"
Any Event Triggered on Autocomplete
How to Get File Name When User Select a File via <Input Type="File" />
How to Pass Along Variables with Xmlhttprequest
Jquery Add Class Based on Page Url
Convert JavaScript-Generated Svg to a File
Good "Background-Size: Cover" Fallbacks/Shims/Tricks for Cross-Browser Compatibility on Divs
Fcm - Programmatically Send Push Notification to User Segments