【问题标题】:Asynchronous looping with promises?带有承诺的异步循环?
【发布时间】:2017-07-18 09:26:32
【问题描述】:

我有以下代码,它注册了一系列 socketio 命名空间。部分逻辑依赖于数据库调用(通过 sequelize),因此我需要使用 Promise。我希望 complete 承诺在所有称为逻辑的构造函数完成后解决。我的问题是 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));
    }
}

如何确保emitInitialPackagecomplete 解析之前完成?

【问题讨论】:

  • 不能使用带有异步代码的循环...需要异步循环
  • @elclanrs 好的,那么异步循环会是什么样子?
  • 这里有什么不清楚的地方吗? this.server.of() 做什么/返回? cells 在构造函数中来自哪里,registerEndPoints 中的 nsp 是什么?为什么要等待 nsp 连接,然后关闭返回的套接字?你为什么这样做:Promise.resolve(db.Line.findAll())? findAll() 要么返回一个 Promise,要么它是同步的,要么它需要一个回调函数。在这些情况下,您都不会通过将其包装在 Promise 中来获得任何收益。
  • @Thomas this.server.of 返回一个 socketio 命名空间。如上面注释行中所述,单元格和路由是从行映射的。我将更新代码示例以更正此问题,但nspnsps 的成员。不太确定等待 nsp 连接是什么意思? findAll 返回一个 bluebird 承诺,所以我使用 Promise.resolve 将其转换为 es6 promsie
  • 您需要更新 registerEndPoints 以返回一个承诺,当所有 emitInitialPackage 调用完成时,该承诺完成。然后将该承诺作为完整调用的一部分返回。这允许承诺流动(或真正通过map

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


【解决方案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));
    }
}

【讨论】:

    【解决方案2】:

    有几个问题需要解决:

    • 返回this.registerEndPoints返回的promise,所以this.complete只会在promise解决时才解决;
    • nsps 参数传递的[] 参数没有用作参数,因此您不妨一起跳过该参数;
    • .on('connection', ...) 函数应该被包装以返回一个承诺;
    • for 循环应该创建这些承诺,然后将它们传递给Promise.all 以获得最终结果。 map 可以用于此;
    • 这不是一个很好的结构,其中三个数组(routescellsnsps)在相同索引处具有相关数据。更好的结构是您拥有一个对象数组,其中每个对象具有三个属性:(routecellnsp);
    • bluebird 符合 promises/A+,因此无需将 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();
            }); 
        });
    }
    

    【讨论】:

    • 谢谢你!你能解释一下nspConnect(nsp)中的那一行resolve是什么吗?
    • nspConnect 是一个返回承诺的函数。该承诺在调用nsp.on 的回调时解决。 Promise 构造函数总是将两个函数作为参数传递给提供给它的回调:resolvereject。它们是由 Promise 内部提供的句柄,用于解决 Promise 或拒绝它。我忽略了第二个。我提供resolve 函数作为nsp.on 的回调函数,以便在建立连接时调用此resolve。当这个resolve 函数被调用时,它将解析new Promise
    • 但是为什么要将resolve 作为nsp.on(... 的参数传入呢?
    • 因为on 函数在完成它的事情后会调用第二个参数(回调),而当它完成时,我想让resolve 执行(因为现在是时候承诺解决),这就是为什么我将它传递给on
    • 好的,emitInitialPackage 这个解决方案会怎样?
    猜你喜欢
    • 2014-10-27
    • 2017-10-30
    • 2017-10-22
    • 1970-01-01
    • 2013-06-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多