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

时间:2018-03-10 12:47:13

标签: javascript asynchronous meteor google-cloud-vision

在看了一堆Futures,Promises,wrapAsync后,我仍然不知道如何解决这个问题

我有这种方法,它采用一系列图像,将其发送到Google Cloud Vision进行徽标检测,然后将所有检测到的图像与徽标一起推送到数组中,我尝试在我的方法中返回。

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

总是返回空数组,这是可以理解的,因为在Google完成处理所有这些并返回结果之前,方法返回logosArray

如何处理这种情况?

1 个答案:

答案 0 :(得分:4)

  1. 使用Meteor方法,您实际上可以简单地"返回Promise",并且Server方法将在内部等待Promise解析,然后将结果发送给客户端:
  2. 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。通过这样做,你不再明确地返回一个Promise(但在底层仍然如此),但是"等待"让Promise在你的方法代码中解决。
    2. 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. 一旦理解了这个概念,就可以逐步处理几个异步操作,并在完成所有操作后返回结果。为此,您只需使用Promise.all
      2. 即可
        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);
            }, []);
          }
        });