Javascript 如何对node.js非阻塞代码中的代码执行进行排序?

Javascript 如何对node.js非阻塞代码中的代码执行进行排序?,javascript,node.js,callback,request,nonblocking,Javascript,Node.js,Callback,Request,Nonblocking,我已经用node.js构建了一个网络爬虫程序,但是node.js的一个主要特性是非阻塞代码,这很好,但在我的例子中它破坏了我的程序。以下是我的程序过去的工作方式: 首先,它为该网站发出请求,并找到指向的所有链接 在页面上通过的所有公司 然后我有了for循环,它修改了这个数组 然后我有一个请求,它位于for循环中,抛出 公司URL数组,用于查找每个公司中的所有产品URL 页面 然后我有一个for循环,用于修改产品URL数组 然后我有另一个在for循环中的请求 抛出产品数组并获取每个产品的价格和 将

我已经用
node.js
构建了一个网络爬虫程序,但是
node.js
的一个主要特性是
非阻塞
代码,这很好,但在我的例子中它破坏了我的程序。以下是我的程序过去的工作方式:

  • 首先,它为该网站发出
    请求
    ,并找到指向的所有链接 在页面上通过的所有公司
  • 然后我有了
    for
    循环,它修改了这个数组
  • 然后我有一个
    请求
    ,它位于for循环中,抛出 公司URL数组,用于查找每个公司中的所有产品URL 页面
  • 然后我有一个
    for
    循环,用于修改产品URL数组
  • 然后我有另一个在
    for
    循环中的请求 抛出产品数组并获取每个产品的价格和 将其存储到字典(对象)中,其中键是 产品和价值就是价格
  • 最后,我修改了我的字典(对象)
  • 正如你所看到的,我的每一步都取决于它前面的一步。所以我需要做一些事情,让我的程序按照我列出的顺序运行。我曾尝试使用
    回调
    ,但结果并不理想。因为这里有一个
    回调的简单示例:

    function some_function(arg1, arg2, callback) {
        var my_number = (arg1 - arg2) * arg2;
        callback(my_number);
    }
    
    some_function(20, 15, function(num) {
        console.log("callback called! " + num);
    });
    
    但我无法想象如何进行包含6个函数的回调。也许有一种方法,但不是用我对
    回调的了解。这是我的程序的一个演示版本,没有
    回调

    var request = require('request');
    var cheerio = require('cheerio');
    var companiesUrls = [];
    var url = '';
    var companiesUrls2 = [];
    var carsUrls = [];
    var carsOwnerReview = {};
    var carReviewUrl = [];
    var site = '...'
    var companiesPath = '/companies'
    ///step 1\\\
    request(site+companiesPath, function(err, resp, body){
        if(!err && resp.statusCode == 200){
            var $ = cheerio.load(body);
            $('a', '#group-content').each(function(){
                var url = $(this).attr('href');
                companiesUrls.push(url);
            });
        };
    });
    ///step 2\\\
    for(var i=0;i<companiesUrls.length;i+=2){
        companiesUrls2.push(companiesUrls[i]);
    };
    
    ///step 3\\\
    for(var i=0;i<companiesUrls2.length;i++){
        request(site+companiesUrls2[i], function(err, resp, body){
            if(!err && resp.statusCode == 200){
                var $ = cheerio.load(body);
                $('h3.edition-title').children().children().each(function(){
                    var url = $(this).attr('href');
                    carsUrls.push(url);
                });
            };
        });
    };
    ///step 4\\\
    for(var i=0;i<carsUrls.length;i++){
        carReviewUrl.push(carsUrls[carsUrls.length-1].slice(0,-7)+'/owner-reviews');
    };
    ///step 5\\\
    for(var i=0;i<carReviewUrl.length;i++){
        request(site+carReviewUrl[i], function(err, resp, body){
            if(!err && resp.statusCode == 200){
                var $ = cheerio.load(body);
                var model = $('#page-title').text();
                $('span.total-votes').children().each(function(){
                    var reviewNum = $(this).text();
                    carsOwnerReview[model] = reviewNum;
                });
            };
        });
    }
    ///step 6\\\
    var keysSorted = Object.keys(carsOwnerReview).sort(function(a,b){return carsOwnerReview[a]-carsOwnerReview[b]});
    var keysSortedReversed = keysSorted.reverse();
    
    var request=require('request');
    var cheerio=需要('cheerio');
    var companiesUrls=[];
    var url='';
    var CompanyUrls2=[];
    var carsUrls=[];
    var carsOwnerReview={};
    var carReviewUrl=[];
    var site='…'
    var companiesPath='/companies'
    ///第一步\\\
    请求(站点+公司路径、功能(错误、响应、正文){
    如果(!err&&resp.statusCode==200){
    var$=总负荷(车身);
    $('a','#组内容')。每个(函数(){
    var url=$(this.attr('href');
    companiesURL.push(url);
    });
    };
    });
    ///步骤2\\\
    
    对于(var i=0;i如果你想用JavaScript/node.js认真编程,你必须深刻理解js是异步的,除了你的代码之外,一切都是并行的

    这意味着在您的情况下,除非您的所有代码都已终止,否则不会调用回调。因此,在for循环中调用异步函数应始终使所有警告灯变为红色

    以下是正确设计的代码:

    var request = require('request');
    var cheerio = require('cheerio');
    var companiesUrls = [];
    var url = '';
    var companiesUrls2 = [];
    var carsUrls = [];
    var carsOwnerReview = {};
    var carReviewUrl = [];
    var site = '...'
    var companiesPath = '/companies'
    
    
    ///step 1\\\
    request(site+companiesPath, function(err, resp, body){
        if(!err && resp.statusCode == 200){
            var $ = cheerio.load(body);
            $('a', '#group-content').each(function(){
                var url = $(this).attr('href');
                companiesUrls.push(url);
            });
        };
        ///step 2\\\
        for(var i=0;i<companiesUrls.length;i+=2){
            companiesUrls2.push(companiesUrls[i]);
        };
    
        ///step 3\\\
        function processCompaniesUrls2( i, callback_pcu2 ) {
            if( i<companiesUrls2.length ) {
                request(site+companiesUrls2[i], function(err, resp, body){
                    if(!err && resp.statusCode == 200){
                        var $ = cheerio.load(body);
                        $('h3.edition-title').children().children().each(function(){
                            var url = $(this).attr('href');
                            carsUrls.push(url);
                        });
                    };
                    processCompaniesUrls2( i+1, callback_pcu2 );
                });
            } else {
                callback_pcu2();
            }
        }
        processCompaniesUrls2( 0, function() {
    
            ///step 4\\\
            for(var i=0;i<carsUrls.length;i++){
                carReviewUrl.push(carsUrls[carsUrls.length-1].slice(0,-7)+'/owner-reviews');
            };
            ///step 5\\\
            function processCarReviewUrl( i, callback_pcru ) {
                if( i<carReviewUrl.length ) {
                    request(site+carReviewUrl[i], function(err, resp, body){
                        if(!err && resp.statusCode == 200){
                            var $ = cheerio.load(body);
                            var model = $('#page-title').text();
                            $('span.total-votes').children().each(function(){
                                var reviewNum = $(this).text();
                                carsOwnerReview[model] = reviewNum;
                            });
                        };
                        processCarReviewUrl( i+1, callback_pcru );
                    });
                } else {
                    callback_pcru();
                }
            }
            processCarReviewUrl( 0, function() {
                ///step 6\\\
                var keysSorted = Object.keys(carsOwnerReview).sort(function(a,b){return carsOwnerReview[a]-carsOwnerReview[b]});
                var keysSortedReversed = keysSorted.reverse();
            });
        });
    });
    
    var request=require('request');
    var cheerio=需要('cheerio');
    var companiesUrls=[];
    var url='';
    var CompanyUrls2=[];
    var carsUrls=[];
    var carsOwnerReview={};
    var carReviewUrl=[];
    var site='…'
    var companiesPath='/companies'
    ///第一步\\\
    请求(站点+公司路径、功能(错误、响应、正文){
    如果(!err&&resp.statusCode==200){
    var$=总负荷(车身);
    $('a','#组内容')。每个(函数(){
    var url=$(this.attr('href');
    companiesURL.push(url);
    });
    };
    ///步骤2\\\
    对于(var i=0;iI使用promissions()来实现这一点……对于我来说,这使事情更容易理解和管理