为什么我无法访问多个网络呼叫的结果?

时间:2017-07-09 01:15:56

标签: node.js express pug

在我的Node应用程序中,我正在尝试获取包裹的装运数据。我需要一种方法来获取json数据并将其附加到对象或其他东西,以便我可以将它传递到我的渲染页面(使用pug)。

这是我的代码:

    var test;

    for(var i = 0; i < result.length; i++) {
        var currentNumber = result[i].trackingNumber;
        ups.track(currentNumber, function(err, tracking) {
            test += tracking
        });
    }

    res.send(result)

似乎我在ups.track中做的任何事情都没有进入var测试的范围,我无法想到更好的方法来做到这一点。任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:1)

首先,ups.track()是一个异步函数。这意味着它将在未来的某个不确定的时间内调用它的回调。因此,您的for循环运行完成,向result.length发起ups.track()次调用,然后在其中任何一个完成之前执行res.send(result),之后任何人调用他们的回调。

其次,您没有初始化test变量,因此test += ...不会产生良好的结果。

因此,在完成所有结果之前,您无法知道结果。有很多方法可以做到这一点。

低技术专柜

“低技术”的做法是保留一个柜台:

var test = "";

var cntr = 0;
for(var i = 0; i < result.length; i++) {
    var currentNumber = result[i].trackingNumber;
    ups.track(currentNumber, function(err, tracking) {
        test += tracking;
        // see if we are done with all the ups.track() calls yet
        if (++cntr === result.length) {
            res.send(test);
        }
    });
}

使用承诺

更高科技的方法是使用承诺(由于许多其他原因,这有更大的好处)。尽管promises在这里只显示了适度的好处,但只要你需要协调一系列异步操作,它们就会带来巨大的好处,并且它们也会使错误处理(你在代码示例中忽略的东西)变得更加简单。

首先,您创建ups.track()的“promisified”版本,这是一个函数,它返回在ups.track()完成时解析或拒绝的承诺。

function upsTrack(num) {
    return new Promise(function(resolve, reject) {
        ups.track(num, function(err, tracking) {
           err ? reject(err) : resolve(tracking);
        });
    });
}

现在,我们将使用.map()的组合来创建一个数组或承诺来自upsTrack()Promise.all()的所有电话,告诉我们所有这些承诺何时完成。< / p>

Promise.all(result.map(function(item) {
    return upsTrack(item.trackingNumber);
})).then(function(allResults) {
    // allResults is an array of tracking results
    // process that array into your final result and send it
    res.send(allResults.join(","));
}).catch(function(err) {
    // handle error here
    res.status(500).end();
});

请注意,此结构还消除了之前以低技术方式定义的多个变量,因为不再需要testcntri。处理数组或处理结果数组的循环结构和函数会为我们处理所有这些。

使用Bluebird Promise库

而且,通过使用像Bluebird这样更智能的承诺库,你甚至可以采取更多的快捷方式:

const Promise = require('bluebird');
ups = Promise.promisifyAll(ups);

Promise.map(result, function(item) {
    return ups.trackAsync(item.trackingNumber);
}).then(function(allResults) {
    // allResults is an array of tracking results
    // process that array into your final result and send it
    res.send(allResults.join(","));
}).catch(function(err) {
    // handle error here
    res.status(500).end();
});

Bluebird promise库在这里有两个有用的东西。它有一个promisifyAll()方法,可以自动生成所有ups对象方法的有效方法,它还有Promise.map(),只需一步组合array.map()Promise.all()(因为它是一种常用的结构。)

如果您对Promises和Promise库感兴趣,您可能会觉得这很有用:

Are there still reasons to use promise libraries like Q or BlueBird now that we have ES6 promises?

答案 1 :(得分:0)

第一个问题是test在开始时是undefined。那很糟。您需要一个与预期相同类型的默认值var test = "";

但是,最大的问题是,在任何ups次回调之前,您要发送回来。

你已经向UPS发出了18个(或其他)请求,但是在UPS服务器收到第一个请求之前你已经返回result,更不用说全部完成了。

我建议尝试这样的事情,而不是向您展示可能为了让事情在循环中起作用而编写的毛茸茸的代码,我建议尝试这样的事情:

const track = trackingNumber =>
  new Promise((resolve, reject) =>
    ups.track(trackingNumber, (err, data) =>
      err ? reject(err) : resolve(data)));

Promise.all(result.map(info => track(info.trackingNumber)))
  .then(trackingNumbers => trackingNumbers.join(''))
  .then(bigStringOfNumbers => res.send(bigStringOfNumbers));

了解Promises,Promise.all()[].map()将比尝试构建此解决方案的复杂回调版本更有用。