Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/36.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript node.js-控制承诺队列_Javascript_Node.js_Asynchronous_Promise_Web Crawler - Fatal编程技术网

Javascript node.js-控制承诺队列

Javascript node.js-控制承诺队列,javascript,node.js,asynchronous,promise,web-crawler,Javascript,Node.js,Asynchronous,Promise,Web Crawler,我正在编写一个爬虫程序,它将使用node.js从电子商务网站获取数据。要获取的每个输入都包含: url:该链接的url 目录:以后应将输出文件写入的目录名 页面:要查询的参数 每个页面将获取大量项目,每个项目都将在稍后获取详细信息 这是我的fetchPagepromise(agentisrequire('superagent')),它将获取HTML文本: function fetchPage(url,page){ return new Promise( (resolv

我正在编写一个爬虫程序,它将使用node.js从电子商务网站获取数据。要获取的每个输入都包含:

  • url
    :该链接的url
  • 目录
    :以后应将输出文件写入的目录名
  • 页面
    :要查询的参数
每个页面将获取大量项目,每个项目都将在稍后获取详细信息

这是我的
fetchPage
promise(
agent
is
require('superagent')
),它将获取HTML文本:

function fetchPage(url,page){
    return new Promise(
        (resolve,reject)=>{
            if (page>0){
                agent
                .get(url)
                .send('page='+page)
                .end(function(err,res){
                    if (err){
                        reject(err);
                    } else{
                        resolve(res.text);
                    }
                });
            } else{
                agent
                .get(url)
                .end(function(err,res){
                    if (err){
                        reject(err);
                    } else{
                        resolve(res.text);
                    }
                });
            }

        });
}
全球电话:

var data=[];
for (var i=1;i<=links[0].numOfPages;i++){
    data.push({
        url:links[0].url,
        directory:links[0].directory,
        page:i
    });
}

const promises=data.reduce(
    (promise,data)=>promise.then(()=>{
        fetchPage(data.url,data.page).then(
            (result)=>{
                const urls=getUrls(result);
                Promise.all(urls.map((url,i)=>fetchPage(url,0).then(
                        (result)=>{
                            var item=getItem(result);
                            item.url=url;
                            writeItem(item,data.directory,data.page,i+1);
                        },
                        (error)=>console.log(error)
                )));
            });
    }),
    Promise.resolve());

promises.then((values)=>console.log('All done'));
var数据=[];
对于(var i=1;ipromise.then(()=>{
fetchPage(data.url,data.page),然后(
(结果)=>{
constURL=getURL(结果);
Promise.all(url.map)((url,i)=>fetchPage(url,0)。然后(
(结果)=>{
var item=getItem(结果);
item.url=url;
writeItem(项,数据.目录,数据.页,i+1);
},
(错误)=>console.log(错误)
)));
});
}),
承诺,决心;
然后((值)=>console.log('All done');
您将看到3个实用程序功能(所有功能都正常工作):

  • getURL
    :处理页面的HTML文本,返回 稍后将详细介绍要爬网的项目
  • getItem
    :处理 项的详细页,返回将写入的对象 文件
  • writeItem
    :将对象写入文件,并提供目录 和页码,以制作适当的目录并写入和存储
我遇到了一个问题:

  • 我如何使用一个承诺队列来重建它 承诺将一个接一个有序地运行 同步且仅允许有限数量的承诺同时运行
如何正确有效地进行?我应该如何更改这些当前代码?我还需要一些演示


我删除了
fetchItem
函数,因为它是不必要的(实际上,它用
page=0
调用
fetchPage
),现在我只使用
fetchPage
首先,如果你想真正控制你的执行,那么你不应该构造一个循环来调用承诺。它将立即执行。相反,您应该构造一些要提供给promise的数据。对不起,我不太明白你的程序流程。我可以看到您正在调用
fetchPage
,完成后,调用
fetchItem
,这将再次调用
fetchPage
。这也许就是为什么你会收到双重回电

对于第二个问题,这里有一个示例,说明如何串行处理每个链接,并并行处理一个链接中的页面,最多并行处理3个作业

var Promise = require('bluebird');
var chance = new (require('chance'))();

var fetchPage = (url, page) => new Promise((resolve, reject) => {
    // Simulate Network Operation
    if (page === 0) {
        console.log('Start Downloading: ' + url);
        setTimeout(() => {
            resolve({
                url: url,
                content: 'Content of ' + url
            });
        }, chance.integer({ min: 10, max: 250 }));
    } else {
        console.log('Start Downloading: ' + url + '?page=' + page);
        setTimeout(() => {
            resolve({
                url: url + '?page=' + page,
                content: 'Content of ' + url + '?page=' + page
            });
        }, chance.integer({ min: 10, max: 250 }));
    }
});

var fetchItem = link => {
    // Get the data to be supplied to fetchPage promise
    var data = [];
    for (var i = 0; i <= link.numOfPages; i++) {
        data.push({
            url: link.url,
            page: i
        });
    }
    return data;
};

var writeItem = (item, directory) => {
    // Simulate Writing to Directory
    console.log('Writing ' + item + ' to ' + directory + ' folder');
};

// Make some dummy links
var links = [];
for (var i = 0; i < 10; i++) {
    var domain = chance.domain();
    links.push({
        url: chance.url({ domain: domain }),
        directory: domain,
        numOfPages: chance.integer({ min: 0, max: 5 })
    });
}

// Process each URL serially
Promise.each(links, link => Promise.map(fetchItem(link), data => fetchPage(data.url, data.page).then(result => {
    writeItem(result.content, link.directory);
    console.log('Done Fetching: ' + result.url);
}), {
    // Control the number of concurrent job
    concurrency: 3
})).then(() => {
    console.log('All Done!!');
});

在本例中,您可以清楚地看到每个任务都是按顺序运行的,并且任务中的每个作业一次最多并行运行3个并发作业。

对于您的情况,我建议您安装Promise库,因为它提供了两个您可以使用的实用程序

对于您的问题,通常情况下,您不会将For循环与Promise结合使用,而是构造一个数据数组和一个返回Promise的映射函数,然后使用
.map()+Promise.all()
.reduce()
将数组分解为一个Promise,在完成所有操作后进行解析

Bluebird的
Promise.map()
还允许您指定一个并发选项,该选项将限制可以同时运行的操作数


以下是一些让您开始学习的示例:

并发运行异步操作 按顺序运行异步操作:

尝试将事物视为值的集合,以及对这些值执行的操作,将操作抽象为函数,并在适当的时候调用它们,不要在同一个位置混合获取和写入操作。

在for循环中存在异步操作的典型情况,请参阅。使用
let page
而不是
var page
作为一个非常简单的解决方案。我删除了
fetchItem
函数,因为它不必要(实际上,它使用
page=0
调用
fetchPage
),现在我只使用了
fetchPage
,我做了一些更改,但事情似乎并没有按顺序运行,我删除了
fetchItem
函数,因为它是不必要的(实际上,它用
page=0
调用
fetchPage
),现在我只使用
fetchPage
,我做了一些更改,但事情似乎没有按顺序运行。我还编辑了问题,以便您更好地理解我的流程。如果您想按顺序运行,请使用
Promise。每个
。否则,如果您想与有限数量的并发作业同时运行,请使用
Promise.map
并为函数提供
concurrency
选项,如我在上述示例中演示的关于
Promise.each
,让我试试,这对我来说是新的。谢谢,不客气。只需确保您使用的是
bluebird
,否则无法使用
Promise.each
Promise.map
。并尝试运行我的示例以更好地理解。我使用的是
bluebird
。但是我对
Promise.each
Promise.all
之间的区别感到困惑。据我所知,
Promise.all
一个接一个地运行一系列承诺
var Promise = require('bluebird');
var chance = new (require('chance'))();

var tasks = [];

for (var i = 1; i <= chance.integer({ min: 10, max: 20 }); i++) {
    var jobs = [];
    for (var j = 1; j <= chance.integer({ min: 2, max: 10 }); j++) {
        jobs.push({
            job_name: 'Job ' + j
        });
    }

    tasks.push({
        task_name: 'Task ' + i,
        jobs: jobs
    });
}

Promise.each(tasks, task => Promise.map(task.jobs, job => new Promise((resolve, reject) => {
    setTimeout(() => resolve(task.task_name + ' ' + job.job_name), chance.integer({ min: 20, max: 150 }));
}).then(log => console.log(log)), {
    concurrency: 3
}).then(() => console.log())).then(() => {
    console.log('All Done!!');
});
const Promise = require('bluebird');
const urls = ['https://url1.com', 'https://url2.com', ... ]; // lots of urls
// {concurrency: 4} means only 4 URLs are processed at any given time.
const allPromise = Promise.map(urls, fetchUrlAsync, {concurrency: 4});
allPromise.then(allValues => {
  // Deal with all results in order of original array
});
const Promise = require('bluebird');
const urls = ['https://url1.com', 'https://url2.com', ... ]; // lots of urls
// {concurrency: 4} means only 4 URLs are processed at any given time.
const allPromise = urls.reduce((promise, url) => 
  // Start with an empty promise, chain all calls on top of that
  promise.then(() => fetchUrlAsync(url)), Promise.resolve()); 
allPromise.then(allValues => {
  // Deal with all results in order of original array
});