Reputation: 9229
I have the following code which registers a series of socketio namespaces. PArts of the logic depend on database calls (via sequelize), hence I need to use promises. I want the complete
promise to resolve when all the constructor called logic is complete. My issue though is that the complete
promise resolves before the emitInitialPackage()
function has resolved.
export class demoClass {
public complete: Promise<boolean>;
server: any;
constructor(io: any) {
this.server = io;
this.complete = Promise.resolve(db.Line.findAll()).then(lines => {
// do some mapping to generate routes and cells
this.registerEndPoints(routes, [], cells);
}).catch(err => console.log(err))
}
registerEndPoints(routes: Array<string>, nsps: Array<any>, cells: Array<string>) {
for (let i = 0; i < routes.length; i++) {
nsps.push(this.server.of('/api/testNamespace/' + routes[i]));
let that = this;
const nsp = nsps[i];
nsp.on('connection', function (socket) {
that.emitInitialPackage(nsps[i], routes[i], cells[i]);
});
}
}
emitInitialPackage(nsp: any, name: string, cell: any) {
return db.Line.find({/* Some sequelize params*/}).then(results => {
nsp.emit('value', results);
}).catch(err => console.log(err));
}
}
How can I ensure emitInitialPackage
has completed before complete
resolves?
Upvotes: 0
Views: 113
Reputation: 349946
There are several issues to solve:
this.registerEndPoints
, so this.complete
will only resolve when that promise resolves;[]
argument passed for the nsps
parameter serves no purpose as argument, so you might as well skip that parameter all together;.on('connection', ...)
function should be wrapped to return a promise;for
loop should create these promises, and then pass them to Promise.all
for the final result. map
can be used for this;routes
, cells
, and nsps
) that have related data at equal indexes. A better structure is where you have one array of objects, where each object has three properties: (route
, cell
, and nsp
);Here is some untested code:
constructor(io: any) {
this.server = io;
// *** bluebird is promises/A+ compliant, no need to convert it:
this.complete = db.Line.findAll().then(lines => {
// do some mapping to generate routes and cells
// *** return the promise!
// *** removed [] argument: not needed
return this.registerEndPoints(routes, cells);
}).catch(err => console.log(err))
}
// *** Remove the nsps parameter
registerEndPoints(routes: Array<string>, cells: Array<string>) {
// *** Create a promise-version of the `.on('connection', ...)` method
function nspConnect(nsp) {
return new Promise( resolve => nsp.on('connection', resolve) );
}
let that = this;
// *** Combine each route, cell, and nsp in one object, and put in array:
const data = routes.map( (route, i) => ({
nsp: that.server.of('/api/testNamespace/' + route),
route,
cell: cells[i]
}) );
// *** Map the array of objects to promises
const proms = data.map( ({nsp, route, cell}) =>
nspConnect(nsp).then(that.emitInitialPackage.bind(that, nsp, route, cell)) );
// *** Return a promise that resolves when all these have resolved
return Promise.all(proms);
}
To have some debugging possibilities, you could expand the nspConnect
function to:
function nspConnect(nsp) {
return new Promise( resolve => {
console.log('creating promise');
return nsp.on('connection', socket => {
console.log('resolving');
resolve();
});
});
}
Upvotes: 0
Reputation: 1402
To wait for completion of all operations in registerEndPoints
, this method should return Promise
that can be chained after db.Line.findAll()
operation:
export class demoClass {
public complete: Promise<boolean>;
server: any;
constructor(io: any) {
this.server = io;
this.complete = Promise.resolve(db.Line.findAll()).then(lines => {
// return promise created by registerEndPoints method
return this.registerEndPoints(routes, [], cells);
}).catch(err => console.log(err))
}
registerEndPoints(routes: Array<string>, nsps: Array<any>, cells: Array<string>) {
const endPointPromises = routes.map((route, index) => {
// for each endpoint create Promise that gets resolved
// only when all work for endpoint is done
return new Promise((resolve) => {
nsps.push(this.server.of('/api/testNamespace/' + route));
const nsp = nsps[index];
nsp.on('connection', (socket) => {
// resolve promise when emitInitialPackage did its part of the work
this.emitInitialPackage(nsps[index], route, cells[index]).then(resolve);
});
});
});
return Promise.all(endPointPromises);
}
emitInitialPackage(nsp: any, name: string, cell: any) {
return db.Line.find({/* Some sequelize params*/}).then(results => {
nsp.emit('value', results);
}).catch(err => console.log(err));
}
}
Upvotes: 1