在Node.js中返回一个延迟的嵌套Promise

3
我正在使用Google地理编码在我的代码中。我需要对大约50个地方进行地理编码,但是Google不允许同时对这么多地方进行地理编码,会生成“OVER_QUERY_LIMIT”错误。因此,当我超出配额时,我想延迟几秒钟进行地理编码。我有一个函数geocode()返回一个promise。当我超出配额时,我想递归调用自身。
这是我的不起作用的代码:
function geocodeAll(addresses)
 {
   for (addr in addresses)
     {
      geocode(addresses[addr])
        .then(
              function(coord)
               {/* I don't always get here */}
             )
     }
 }


function geocode(address)
{
 var deferred = Q.defer();
 geocoder.geocode(address, function ( err, geoData ) 
        {
         if (err)
            { deferred.reject(err);}
         else
            {
             if (geoData.status=="OVER_QUERY_LIMIT" )
                 { // doh! quota exceeded, delay the promise
                  setTimeout(function()
                     {geocode(address)
                      .then(
                            function(coord)
                             {deferred.resolve(coord);}
                           );
                      }, 1000);
                    }
                else
                    { // everything ok
                     var coord = {'lat':geoData.lat, 'lng':geoData.lng};              
                     deferred.resolve(coord);
                    }
               }
      });

  return deferred.promise; 
}

更新[已解决]

实际上这段代码是正确的。我有一个未捕获的异常与延迟无关。使用Q.all([..]).then().catch(),我发现了它。


你的承诺是不是只是永远没有解决,还是出现了错误? - bluetoft
我总是能够得到地理编码的结果,但第一次调用geocode()的主函数并没有将其返回。 - DeLac
你没有处理任何错误。你怎么知道你不会遇到其中之一? - Bergi
当结果没有返回时,你如何知道你总是得到了结果? - Bergi
1
尽量避免延迟反模式,而是将geocodesetTimeout分别转换为Promise。如果您不想这样做,至少使用setTimeout(function() { deferred.resolve(geocode(address)); }, 1000)来正确处理它。 - Bergi
2个回答

2
你的代码似乎运行良好。我唯一发现的问题是你正在超出范围使用latlng。但是你可能已经正确地编写了它们,只是没有包含在代码中。我怀疑你可能误用了这两个变量。这些应该是geoData.latgeoData.lng吗? var coord = { 'lat': geoData.lat, 'lng': geoData.lng }; Plnkr 编辑 我认为你的问题可能与geocodeAll函数的实现有关。
function geocodeAll(addresses)
 {
   for (addr in addresses)
     {
      geocode(addresses[addr]) /* This seems error prone to me!  Try keeping track of all your promises.*/
        .then(
              function(coord)
               {/*You only get here if there was no error returned from the geo request to the api. */}
             )
     }
 }

使用 Q.all 进行跟踪。
function geocodeAll(addresses) {
  var promises = addresses.map(geocode); 
  for (addr in addresses) {
    promises.push(
      geocode(addresses[addr])
         .catch(console.log.bind(console)) /* handle your error however you see fit*/
      ); 
  }
  return Q.all(promises).then(function(results) {
     var firstCord = results[0];
     var secondCord = results[1];
     //etc...
  });
 }

或者您还可以使用Q.allSettled来处理操作成功/失败的情况。

function geocodeAll(addresses) {
      var promises = addresses.map(geocode);
      return Q.allSettled(promises).then(function (results) {
        var success = [], fail = [];
        results.forEach(function (result) {
          if (result.state === "fulfilled") {
             success.push(result.value);
          } else {
            fail.push(result.reason);
          }
        });
        return { coords: success, errors: fail };
      });
     }

   });

是的,这里只是一个打字错误。在我的实际代码中是正确的。谢谢,我已经修正了问题。 - DeLac
谢谢,你的代码更加清晰了!现在它可以工作了,之前是一个未捕获的异常。 - DeLac

0

代码看起来不错。也许是geocoder.geocode或其他什么出了问题。尝试设置:

window.onerror = (err) => console.error(err)

或者:

process.on('uncaughtException', (err) => console.error(err))

1
你实际上正在寻找 process.on("unhandledRejection", console.error) - Benjamin Gruenbaum

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接