使用promises进行异步循环?

时间:2017-02-27 16:51:47

标签: javascript typescript ecmascript-6 promise es6-promise

我有以下代码注册了一系列socketio命名空间。逻辑的PArts依赖于数据库调用(通过sequelize),因此我需要使用promises。当所有构造函数调用逻辑完成时,我希望complete promise得到解决。我的问题是complete承诺在emitInitialPackage()函数解决之前解析。

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));
    }
}

如何在emitInitialPackage结算之前确保complete已完成?

2 个答案:

答案 0 :(得分:1)

要等待registerEndPoints中的所有操作完成,此方法应返回Promise操作后可以链接的db.Line.findAll()

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));
    }
}

答案 1 :(得分:0)

有几个问题需要解决:

  • 返回this.registerEndPoints返回的承诺,因此this.complete只会在该承诺结算时解决;
  • []参数传递的nsps参数没有用作参数,因此您也可以一起跳过该参数;
  • .on('connection', ...)函数应该被包装以返回一个promise;
  • for循环应该创建这些承诺,然后将它们传递给Promise.all以获得最终结果。 map可以用于此目的;
  • 这是一个不错的结构,你有三个数组(routescellsnsps),它们具有相同索引的相关数据。一个更好的结构是你有一个对象数组,其中每个对象有三个属性:(routecellnsp);
  • bluebird是promises / A + compliance,因此不需要将bluebird promise转换为本机JS promise。

以下是一些未经测试的代码:

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); 
}

要进行一些调试,您可以将nspConnect功能扩展为:

function nspConnect(nsp) {
    return new Promise( resolve => { 
        console.log('creating promise'); 
        return nsp.on('connection', socket => { 
            console.log('resolving'); 
            resolve();
        }); 
    });
}