从 Meteor.method 内的多个承诺返回值

Returning value from multiple promises within Meteor.method

在研究了 Futures、Promises、wrapAsync 之后,我仍然不知道如何解决这个问题

我有这个方法,它获取一组图像,将其发送到 Google Cloud Vision 进行徽标检测,然后将所有检测到的带有徽标的图像推送到一个数组中,我尝试在其中 return 在我的方法中。

Meteor.methods({
  getLogos(images){
    var logosArray = [];
    images.forEach((image, index) => {
        client
        .logoDetection(image)
        .then(results => {
            const logos = results[0].logoAnnotations;
            if(logos != ''){
                logos.forEach(logo => logosArray.push(logo.description));
            }
        })
    });
    return logosArray;      
  },
});

但是,当从客户端调用该方法时:

Meteor.call('getLogos', images, function(error, response) {
  console.log(response);
});

空数组总是 returned,并且可以理解,因为方法 returned logosArray 在 Google 完成所有处理之前 return 结果。

遇到这种情况怎么处理?

  1. 使用 Meteor 方法,您实际上可以简单地 "return a Promise",服务器方法将在内部等待 Promise 解析,然后再将结果发送给客户端:
Meteor.methods({
  getLogos(images) {
    return client
      .logoDetection(images[0]) // Example with only 1 external async call
      .then(results => {
        const logos = results[0].logoAnnotations;
        if (logos != '') {
          return logos.map(logo => logo.description);
        }
      }); // `then` returns a Promise that resolves with the return value
          // of its success callback
  }
});
  1. 您还可以将 Promise 语法转换为 async / await。通过这样做,您不再显式 return Promise(但在幕后仍然如此),但 "wait" Promise 在您的方法代码中解析。
Meteor.methods({
  async getLogos(images) {
    const results = await client.logoDetection(images[0]);
    const logos = results[0].logoAnnotations;
    if (logos != '') {
      return logos.map(logo => logo.description);
    }
  }
});
  1. 一旦理解了这个概念,就可以开始处理多个异步操作,并在所有操作完成后 return 结果。为此,您可以简单地使用 Promise.all:
Meteor.methods({
  getLogos(images) {
    var promises = [];
    images.forEach(image => {
      // Accumulate the Promises in an array.
      promises.push(client.logoDetection(image).then(results => {
        const logos = results[0].logoAnnotations;
        return (logos != '') ? logos.map(logo => logo.description) : [];
      }));
    });
    return Promise.all(promises)
      // If you want to merge all the resulting arrays...
      .then(resultPerImage => resultPerImage.reduce((accumulator, imageLogosDescriptions) => {
        return accumulator.concat(imageLogosDescriptions);
      }, [])); // Initial accumulator value.
  }
});

async/await:

Meteor.methods({
  async getLogos(images) {
    var promises = [];
    images.forEach(image => {
      // DO NOT await here for each individual Promise, or you will chain
      // your execution instead of executing them in parallel
      promises.push(client.logoDetection(image).then(results => {
        const logos = results[0].logoAnnotations;
        return (logos != '') ? logos.map(logo => logo.description) : [];
      }));
    });
    // Now we can await for the Promise.all.
    const resultPerImage = await Promise.all(promises);
    return resultPerImage.reduce((accumulator, imageLogosDescriptions) => {
      return accumulator.concat(imageLogosDescriptions);
    }, []);
  }
});