在节点.js中强制使用顺序代码

Forcing Sequential Code in node.js

本文关键字:顺序 代码 节点 js      更新时间:2023-09-26

我终于弄清楚了回调在node.js中是如何工作的,但是我现在正在尝试让我的代码按顺序执行。

目标是(按顺序):

  1. 将网址加载到欢呼中
  2. 分析页面上<tbody>中的每个<td>
  3. 将文本元素加载到数据数组后,回调。
  4. 在完整数据数组上调用循环透数据。
  5. 遍历数据数组并调用每个数组上的 lookForPlayer 数组一,其中:
  6. 在我的数据库中运行一个与从文本元素,如果我的数据库中没有匹配项,请插入它们(我有它现在只是打印到控制台以进行测试)。

最终目标是遍历每个页面(每个日期都有一个单独的 URL,所以我正在循环浏览日期)并插入一次不在我的数据库中的玩家。问题是它在执行 INSERT 查询之前会经历每个 SELECT,因此它会多次插入它们。

这是我正在解析的页面,如果有帮助的话:http://www.basketball-reference.com/friv/dailyleaders.cgi?month=12&day=29&year=2014

这是我的代码:

function loadPage (url, callback){
    request(url, function(err, response, body){
        if(!err && response.statusCode ==200){
            var $ = cheerio.load(body);
            rowsRemaining = $.length;
            $('td', 'tbody').each(function(){
                var text = $(this).text();
                data.push(text);
                rowsRemaining -= 1;
                console.log('rows left: ',rowsRemaining);
            });
        }
        if (rowsRemaining == 0){
            console.log('$ length: ',$.length);
            callback(data);
        }
    });
}
function loopThroughData (data, callback){
    for(i=1;i<data.length;i+=26){
        lookForPlayer(data[i].replace("'",""),function(name){
            /* var insertPlayer = connection.query(
                'INSERT INTO player (provider_id, team_id, position_id, name) VALUES (1, (SELECT id FROM team WHERE slug = "'+data[i+1]+'"),1,"'+name+'");',function(err,result,fields){
            }); */
            console.log('i is currently = ',i);
        });
    }
    callback();
}
function lookForPlayer(name, callback){
    console.log('Looking for Player...');
    var selectPlayer = connection.query(
    "SELECT * FROM player WHERE name = '"+name+"'", function(err, rows, fields){
    if(err) throw err;
    if(rows.length==0){
        callback(name);
    }
    });
}
//loop through every day since the season started
for (d = seasonStart; d <= Date.now(); d.setDate(d.getDate() + 1)){
    console.log('d = ',d);
    loadPage(baseURL+(d.getMonth()+1)+'&day='+d.getDate()+'&year='+d.getFullYear(),function(data){
        console.log('Page loaded...');
        loopThroughData(data,function(){
        });
    });
}

如您所见,我尝试添加一个 rowsRemaining 变量,该变量旨在确保在调用 loadPage 函数中的回调之前我已经解析了整个文件,但它从未达到这一点。请注意,我在这些函数(行剩余、数据等)之前初始化了很多这些变量。

它似乎也在完全加载、解析和插入第一页之前遍历每个日期,这是它不应该做的。

这是基于@Brant答案的更新代码

   function loadPage (url, callback){
    request(url, function(err, response, body){
        if(!err && response.statusCode ==200){
            var $ = cheerio.load(body);
            console.log(url);
            $('td', 'tbody').each(function(){
                var text = $(this).text();
                data.push(text);
            });
        }
        callback(data);
    });
}
function loopThroughData (data, callback){
    for(i=1;i<data.length;i+=26){
        lookForPlayer(data[i].replace("'",""),function(name){
            var insertPlayer = connection.query(
                'INSERT INTO player (provider_id, team_id, position_id, name) VALUES (1, (SELECT id FROM team WHERE slug = "'+data[i+1]+'"),1,"'+name+'");',function(err,result,fields){
            });
        });
    }
    callback(data);
}
function lookForPlayer(name, callback){
    var selectPlayer = connection.query(
    "SELECT * FROM player WHERE name = '"+name+"'", function(err, rows, fields){
    if(err) throw err;
    if(rows.length==0){
        console.log(name,' was not found in DB!');
        callback(name);
    }
    });
}
//loop through every day since the season started
for (d = seasonStart; d <= Date.now(); d.setDate(d.getDate() + 1)){
    validDatesArr.push(d);
}
async.eachSeries(validDatesArr,
    function(validDatesArr, callback){
    loadPage(baseURL+'/month='+validDatesArr.getMonth()+1+'&day='+validDatesArr.getDate()+'&year='+validDatesArr.getFullYear(),function(data){
        loopThroughData(data, function(){
            callback();
        });
    });
    }, function(err){
        if(!err){
            console.log('We processed each date requests one by one');
        }
    }
);

所以现在它正在逐个加载页面,但它没有在该数据的loopThroughData函数中执行INSERT函数。我想我只会在异步列表中添加另一个函数,但这个特定的函数是调用一个函数,而不是使用匿名函数。

将 for 循环修改为如下所示:

//loop through every day since the season started
var validDatesArr = [];
for (var d = seasonStart; d <= Date.now(); d.setDate(d.getDate() + 1)){
    validDatesArr.push(d);
}
async.eachSeries(validDatesArr, 
    function(d, callback) {
        loadPage(baseURL+(d.getMonth()+1)+'&day='+d.getDate()+'&year='+d.getFullYear(),function(data){
            console.log('Page loaded...');
            loopThroughData(data,function(){
                callback();
            });
        });
    }, function(err) {
        if(!err) {
            console.log('We processed each date request one by one')
        }
    }
);

并且需要异步,可以在这里找到:https://github.com/caolan/async

npm install async

你可以嵌套异步函数来控制执行流程,就像在序列编程中一样,在厄运金字塔中要小心,另一种解决方案是使用你使用的异步函数的同步版本(如果存在)。如果你不需要它们,你不会被迫编写异步函数,Node.js使用大量的异步函数,因为它是一种非bloking语言,对Web开发非常强大。所以不要在你的函数中使用 asyn 风格和回调!