返回带有节点的异步Web查询的结果

时间:2017-10-07 07:03:02

标签: node.js

虽然我在概念上理解节点(和ajax&#s; s)异步行为,但我总是对我认为必须是常规任务感到困惑。请考虑以下伪代码:

let result = [];
request("http://server", function(error, response) {
    const links = response.links; // an array of links
    links.forEach(function(link) {
         request(link, function(error, response) {
             result.push(response.gold);
         });
         // *****C*****
    });
    // *****B*****
});
// *****A*****

我的问题是,何时将结果返回到网络以便返回完整的结果?在AB,结果可能尚未准备就绪,但如果我以某种流式方式执行此操作,我只能从C返回结果,我不会这样做。我知道怎么做。处理这样的事情的正确方法是什么(并且,假设我在第一个response中只显示了一个下降级别,我将如何处理任意数量的级别)?另外,我想知道在不使用任何外部帮助程序库(如async)的情况下执行此操作的方法。我没有反对使用类似的东西,但我想了解在nodejs中应该如何处理它的异步性质。

3 个答案:

答案 0 :(得分:0)

您可以使用promisses以及nodejs v8中的asyncawait对此进行归档。

首先,您需要让您的请求函数返回一个promise,而不是使用回调。所以要么宣传它,要么使用返回承诺的库。 现在你可以像这样编写一个异步函数

var requestPromisified = require('request-promise');

async function getGoldData() {
    var response = await requestPromisified('http://example.com');
    var links = ['http://example.com']; //get links somehow from response
    var goldPromises = links.map(async function (link) {
        var response = await requestPromisified(link);
        var gold = 0; //get gold somehow from response
        return gold;
    });
    return Promise.all(goldPromises);
}

然后在其他异步上下文中使用该函数,例如在快速路由回调中。 (其他建议:使用模块express-promise-router,这样您就不必使用next(err);此模块会自动将被拒绝的承诺传递给下一个中间件。)

var router = require('express-promise-router')();

router.get('/', async function(req, res) {
    var goldData = await getGoldData();
    console.log(goldData);
    res.json(goldData);
});

或者在任何地方调用它:

getGoldData().then(function(goldData){
    console.log('goldData:', goldData);
}).catch(function(err){
    console.log('ERROR:', err);
});

答案 1 :(得分:0)

让我们尝试解析两个youtube网址并让它们处理。承诺会使代码更容易推理。

const sonyPictures = 'https://www.youtube.com/playlist?list=PLYeOyMz9C9kYmnPHfw5-ItOxYBiMG4amq';
const fox = 'https://www.youtube.com/playlist?list=PLfPBohF1uFwoO5wBnYD67i0pbZ6GqrGEd';
const allURLs = [sonyPictures, fox];

所以现在我们有一个我们想要循环的数组。现在我们需要发出请求并保存承诺

const cheerio = require('cheerio');
const rp = require('request-promise');
const playlistArrayPromises = [];

...

allURLs.forEach((channelURL) => {
    const options = {
      uri: channelURL,
      transform: function (body) {
        return cheerio.load(body);
      },
    };
    playlistArrayPromises.push(rp(options));

下一步是等待所有这些承诺解决(或拒绝),我们可以执行我们想要的行动

Promise.all(playlistArrayPromises).then((result) => {
    console.log(result.length);
    console.log('here');
    return 0;
  });

现在,结果按照请求的顺序包含了所有parsedURL响应。

您可以将此扩展到您想要的任何级别。您所要做的就是等待所有承诺通过Promise.all在子级别解决,一旦完成,您可以继续进行。

以下是您可以使用node和所需的软件包运行的完整代码。

const cheerio = require('cheerio');
const rp = require('request-promise');

const sonyPictures = 'https://www.youtube.com/playlist?list=PLYeOyMz9C9kYmnPHfw5-ItOxYBiMG4amq';
const fox = 'https://www.youtube.com/playlist?list=PLfPBohF1uFwoO5wBnYD67i0pbZ6GqrGEd';
const allURLs = [sonyPictures, fox];
const playlistArrayPromises = [];

const main = () => {
  allURLs.forEach((channelURL) => {
    const options = {
      uri: channelURL,
      transform: function (body) {
        return cheerio.load(body);
      },
    };
    playlistArrayPromises.push(rp(options));
  });
  Promise.all(playlistArrayPromises).then((result) => {
    console.log(result.length);
    return 0;
  });
};




main();

答案 2 :(得分:-1)

你已经使用了函数push,它基本上添加了LIFO形式的元素。而不是使用,取消移位功能。 所以,如果你不理解https://www.w3schools.com/jsref/jsref_unshift.asp 看到这个链接。