使我的异步代码与setTimeout同步。我需要承诺吗?

时间:2016-03-23 17:40:31

标签: javascript node.js asynchronous request promise

我正在为特定网站构建数据抓取工具。因为我只想每10秒发出一次请求,所以我将它设置为一个setTimeout循环,它将url作为我手动输入的url数组中的参数。在回调中,它请求此url并解析响应,将数据推送到一个新的数组结构中,以便最终可以将其转换为csv。我将粘贴下面的完整代码以及我的依赖项。

问题是这些请求中约有五分之一以未定义的形式返回。我认为超时功能会处理这个并且程序会同步运行我显然是错的。研究这个我发现很多人使用promises依赖来命令异步请求。我的问题是:那是必要的吗?或者我可以调整我的回调/ setTimeout,以便它可以在不添加其他依赖项的情况下工作吗?

编辑由于我不清楚我正在复制我想要这个应用程序做的事情: 我希望程序接受请求,返回一个json字符串,解析该数据的json字符串,将该数据添加到数组,并将该数组导出为csv。我想循环这个功能,所以它可以为一长串URL做这个,但我希望它一次只能发出1个请求,并等待迭代到下一个请求,直到从初始响应中收集到所需的数据。我想每10秒发送一次请求。

这是我的代码:

var express = require('express');
var fs = require('fs');
var request = require('request');
var cheerio = require('cheerio');
var app     = express();

var arr = [];

var url = //A bunch of urls that I'm leaving out to conserve space


i = 0;
function timeout() {
    setTimeout(function() {
        request(url[i], function(error, response, html){     
            if(error){
                console.log(error);
            } else {

                var $ = cheerio.load(html);

                var company, industry, size, website, type;

                var inArr = [];
                $('div .image-wrapper img').filter(function(){
                    var data = $(this);
                     company = data.attr('alt');
                     inArr.push("\"" + company + "\"");
               })

                 $('.industry p').filter(function(){
                     var data = $(this);            
                     industry = data.text();  
                     inArr.push("\"" + industry + "\"");
                })

                 $('.company-size p').filter(function(){
                     var data = $(this);                 
                     size = data.text();
                     inArr.push("\"" + size + "\"");
                })

                 $('.website p a').filter(function(){
                     var data = $(this);                 
                     website = data.text();
                     inArr.push("\"" + website + "\"");
                })

                 $('.type p').filter(function(){
                     var data = $(this);                 
                     type = data.text();
                     inArr.push("\"" + type + "\"");
                })


                arr.push(inArr);
                console.log("I just sourced data for " + company);

                if (i === url.length - 1) {
                    clearTimeout(timeout);
                    console.log("All done!")
                    var csvContent;
                    arr.forEach(function(infoArray, index){

                       dataString = infoArray.join(",");
                       csvContent += index < arr.length ? dataString+ "\n" : dataString;

                    });
                    fs.writeFile('output.csv', csvContent, function(err){
                        console.log('File successfully written! - Check your project directory for the output.csv file');
                    });

                } else {
                    i++;
                    timeout();                  
                }
            }

        });
    }, 10000);

};


timeout();

2 个答案:

答案 0 :(得分:1)

如果这里真正的问题是你想要一个接一个地发送一个请求,但是相隔不超过10秒,那么你可以这样做,通过制作一个小包装函数来告诉你下次调用的时间:

var request = require('request');

function requestNext(url, callback, delay, nextCallback) {
    var start = Date.now();
    request(url, function(error, response, html) {
        callback(error, response, html);
        var elapsed = Date.now() - start;
        var wait = Math.max(delay - elapsed, 0);
        // schedule next call to request()
        setTimeout(nextCallback, wait);
    });
}

然后,你可以调用requestNext()来指定延迟时间,再调用第二个回调来告诉你何时进行下一次调用。

然后,在你特别的情况下,你可以使用这样的重复函数:

function getURLs(urls, delay, processCallback, doneCallback) {
    var index = 0;
    var data = [];

    function next() {
        if (index < urls.length) {
            requestNext(urls[index++], function(err, response, html) {
                // need to decide what you want do for error handling here
                // continue? stop further processing?
                data.push(processCallback(err, response, html));
            }, delay, next);
        } else {
            doneCallback(null, data);
        }
    }
    next();
}

getURLs(urlArray, 10000, processResult, function(err, dataArray) {
   if (!err) {
       // results are in dataArray
   }
});

然后,将您的逻辑用于处理名为processResult的函数中的URL,如下所示:

function processResult(err, response, html) {
    // your code to process a page here
    // return the final result as a return value and it will be collected for you
}

答案 1 :(得分:0)

尝试调整js以声明引用setTimeout的变量。在问题js处,clearTimeout()作为参数调用timeout,但timeout是一个函数,而不是对setTimeouttimeout()的引用}。 js以下t timeout被声明为setTimeout函数之外的变量,在timeout()调用中设置为var timeoutID = window.setTimeout(func, [delay, param1, param2, ...]); var timeoutID = window.setTimeout(code, [delay]); 的引用。

请参阅WindowTimers.setTimeout()

  

<强>语法

var arr = [];

var url = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 

var i = 0;

var t = null;

function timeout() {
  t = setTimeout(function() {
    (function() {
        var inArr = [];
        inArr.push(i)
        arr.push(inArr);
        console.log("I just sourced data for " + i, t);
        if (i === url.length - 1) {
          clearTimeout(t);
          console.log("All done!", i, url.length - 1, arr)
        } else {
          i++;
          timeout();
        }
    }());
  }, 1000); // reduced duration to `1000` for stacksnippets
};

timeout();

&#13;
&#13;
func articles() -> [Article] {
    return arrayOfModels.map(ArticleStruct.init)
}

func authors() -> [Author] {
    return arrayOfModels.map(AuthorStruct.init)
}
&#13;
&#13;
&#13;