How to Make a Function Wait Until a Callback Has Been Called Using Node.Js

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



Leave a reply



Submit