与承诺异步循环?

问题描述:

我有以下代码注册一系列socketio命名空间。逻辑的PArts取决于数据库调用(通过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)); 
    } 
} 

如何确保emitInitialPackage之前complete做出决议已经完成?

+1

不能使用循环与这样的异步代码。 ..需要循环异步 – elclanrs

+0

@elclanrs好吧,那么异步循环会是什么样子? –

+0

这里有一点不太清楚吗? 'this.server.of()'做/返回什么? 'cell'来自构造函数,'registerEndPoints'中的'nsp'是什么?你为什么要等nsp连接,然后关闭套接字返回?你为什么这样做:'Promise.resolve(db.Line.findAll())'? findAll()会回复一个承诺,或者它是同步的,或者它期望一个回调函数。在所有这些情况下,你都可以通过将其包装在承诺中获得任何收益。 – Thomas

等待所有操作完成在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)); 
    } 
} 

有几个问题需要解决:

  • 回报的承诺由this.registerEndPoints返回,所以this.complete只有在承诺解决时才能解决;
  • nsps参数传递的参数[]没有任何用途作为参数,因此您不妨一起跳过该参数;
  • .on('connection', ...)函数应该打包以返回一个承诺;
  • for循环应创建这些承诺,然后将它们传递给Promise.all以获得最终结果。可以使用map;
  • 这是不是一个很好的结构,你有三个数组(routes,cellsnsps)在相同的索引有相关的数据。更好的结构是你有一个对象数组,其中每个对象都有三个属性:(route,cellnsp);
  • 蓝鸟承诺/ A +兼容,所以不应该将蓝鸟承诺转换为原生JS承诺。

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

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(); 
     }); 
    }); 
} 
+0

谢谢你!你能解释'nspConnect(nsp)'什么是'resolve'这一行吗? –

+0

'nspConnect'是一个返回承诺的函数。该承诺解决了何时调用'nsp.on'的回调。 'Promise'构造函数总是将两个函数作为参数传递给提供给它的回调函数:'resolve'和'reject'。它们是由承诺内部人员提供的解决承诺或拒绝承诺的句柄。我忽略了第二个。我提供'resolve'函数作为'nsp.on'的回调函数,以便在建立连接时调用这个'resolve'。当这个'resolve'函数被调用时,它将解析'new Promise'。 – trincot

+0

但是为什么''resolve'作为'nsp.on(...'? –