【问题标题】:Check if async function has a random loop is finished检查异步函数是否有随机循环完成
【发布时间】:2021-03-24 06:15:59
【问题描述】:

我正在做一个简单的任务,它将从一个网站获取一些数据并在另一个网站中检查它,所以我成功获取了数据并创建了一个循环来检查数据的每个值,但问题是由于 nodejs 发送所有的请求,然后得到响应,这对我来说太糟糕了

  1. 索引没有以正确的方式排列,这意味着当我在控制台中检查 i 值时,有时会变成 55,然后是 2,然后是 26,然后是 5 - 所以我无法检查循环是否由索引

  2. 检查条件变得随机,因此无法捕获它是否已完成并将所有结果推送到数组中

发生的事情是,每次条件为真时它都会返回数组的结果,但我不希望这样,我想在完成所有检查后得到数组中的所有结果 - 我试图检查数组结果超出了循环,但它只是从数据中得到了 4 或 5,有时什么都不给我,因为所有请求都已发送并且循环已完成,我正在等待响应,所以我无法抓住正确的方法检查是否收到所有响应

这是我的代码:

const http = require("http");
const r = require("request");
const $ = require("jquery-jsdom");
var arr = [];
let data_list_req = function() {
    return new Promise((resolve, reject) => {
        r({
            //get the data from this url
            url: "https://example.com/",
            method: "GET"
        }, function(err,resp,htm) {
            resolve(htm)
        })
    })
}
data_list_req().then((response) => {
    let tr =  $($($(response).find("table")[0]).html()).find("tr");
    let data1_list =  tr.find("td:nth-child(2)");
    var data2_list =  tr.find("td:nth-child(1)");
    for(let i = 0, q = 0; i < data2_list.length, q < data1_list.length; i++, q++) {
         if (data1_list[q].innerHTML == "text") {
            //second step get the data code
            let result_page =  "http://example.com/code.php";
            let check =  function() {
                return new Promise( (resolve, reject) => {
                    r({
                        url: result_page,
                        method: "POST",
                        formData: {
                            "list": data2_list[i].innerHTML+":text"
                        }
                    },  function(err,resp,htm) {
                        resolve(htm)
                    })
                })
            }
            check().then((resp) => {
                let proxy_check =  function() {
                    return new Promise( (resolve, reject) => {
                        var code =  $($(resp).find("table").html()).find("tr")[1].getAttribute("code");
                         console.log(code);
                         console.log(data2_list[i].innerHTML+":text")
                         //last step checking the data
                        r({
                            url:  "http://example.com/checkdata.php?data="+data2_list[i].innerHTML+":"+data1_list[q].innerHTML+"&code="+code,
                            method: "GET"
                        },  function(err,resp,htm) {
                             resolve(htm);
                        })
                    })
                }
                proxy_check().then( (checked) => {
                    //data
                    let first_data =  $(checked).find("td")["prevObject"][1].innerHTML;
                    let second_data =  $(checked).find("td")["prevObject"][2].innerHTML;
                    let check_status =  $(checked).find("td")["prevObject"][3].innerHTML;
                     console.log(first_data+":"+second_data)
                     console.log(check_status)
                    if (check_status == "working") {
                        arr.push(first_data+":"+second_data)
                        console.log(arr)
                    }
                })
            })  
        }
    }
})

【问题讨论】:

    标签: node.js web-scraping async-await request


    【解决方案1】:

    您需要创建一个变量requests 来维护代码已提交给事件循环的承诺计数,并且一旦承诺解决,您需要减少requests 的计数。一旦计数变为零,您就可以解决承诺。

    例如-

    const http = require("http");
    const r = require("request");
    const $ = require("jquery-jsdom");
    var arr = [];
    let requests = 0;
    let data_list_req = function() {
        return new Promise((resolve, reject) => {
            r({
                //get the data from this url
                url: "https://example.com/",
                method: "GET"
            }, function(err,resp,htm) {
                resolve(htm)
            })
        })
    }
    const data = await new Promise(function (resolve, reject) {
        data_list_req().then((response) => {
            let tr =  $($($(response).find("table")[0]).html()).find("tr");
            let data1_list =  tr.find("td:nth-child(2)");
            var data2_list =  tr.find("td:nth-child(1)");
            for(let i = 0, q = 0; i < data2_list.length, q < data1_list.length; i++, q++) {
             if (data1_list[q].innerHTML == "text") {
                //second step get the data code
                requests++;
                let result_page =  "http://example.com/code.php";
                let check =  function() {
                    return new Promise( (resolve, reject) => {
                        r({
                            url: result_page,
                            method: "POST",
                            formData: {
                                "list": data2_list[i].innerHTML+":text"
                            }
                        },  function(err,resp,htm) {
                            resolve(htm)
                        })
                    })
                }
                check().then((resp) => {
                    let proxy_check =  function() {
                        return new Promise( (resolve, reject) => {
                            var code =  $($(resp).find("table").html()).find("tr")[1].getAttribute("code");
                             console.log(code);
                             console.log(data2_list[i].innerHTML+":text")
                             //last step checking the data
                            r({
                                url:  "http://example.com/checkdata.php?data="+data2_list[i].innerHTML+":"+data1_list[q].innerHTML+"&code="+code,
                                method: "GET"
                            },  function(err,resp,htm) {
                                 resolve(htm);
                            })
                        })
                    }
                    proxy_check().then( (checked) => {
                        //data
                    requests--;
                        let first_data =  $(checked).find("td")["prevObject"][1].innerHTML;
                        let second_data =  $(checked).find("td")["prevObject"][2].innerHTML;
                        let check_status =  $(checked).find("td")["prevObject"][3].innerHTML;
                         console.log(first_data+":"+second_data)
                         console.log(check_status)
                        if (check_status == "working") {
                            arr.push(first_data+":"+second_data)
                            console.log(arr)
                        }
                        if(requests === 0){
                            resolve(arr);               
                        }
                    })
                })  
            }
            }
        })
    }
    

    【讨论】:

    • 简单有效,很多加载器都用这个方法
    • 这是真正的代码文件,您可以安装 request , jquery-jsdom 模块并尝试drive.google.com/file/d/1R2NoKQVW_1XNjUMhR7b55wHnVRn6LmRF/… - 如果您可以删除超时并尝试您所说的
    • 我之前尝试过但没有工作,所以我添加了超时,这不是最好的方法,但它是我找到它的唯一方法 - 问题是循环已完成,这意味着如果我在循环 - 代码将在将数据推入数组之前执行,因此在收到响应并重复多次后不会执行任何方法 - 它不再是循环,就像用户在一段时间后单击按钮一样,所以你无法确定他将完成单击按钮的时间
    • 我只是想知道这是一个错误或 JavaScript 还是我的代码的问题,因为在我使用 ajax 之前,我也发生过类似的事情,我用它从网站上获取一些产品但是当我同时单击多个产品(例如 4 个或 5 个)来选择它们时 - 我发现 ajax 发送请求并接收响应,但问题在于完成、成功等事件 - 它只是为最后 1 或 2 个产品工作并弃用旧请求,因为响应只是需要更多时间......
    【解决方案2】:

    我很久以前就发布了这个问题并尝试了很多东西,但它们没有奏效,但最后我创建了一个递归函数,它将逐个循环遍历数组并等待响应,然后在完成后所有请求并接收所有响应它将返回最后一个结果作为参数并执行我的函数

    我的实际问题在于 2 点

    1. 所有请求都是随机发送的,响应也是随机返回的
    2. 当我在数组中循环时,我完成了一个条件,该条件将过滤端口并仅获取具有 8080 的代理,这会导致新问题

    这是一个函数如何工作的示例

    const r = require("request");
    const jsdom = require("jsdom")
    const { JSDOM } = jsdom;
    
    //use Promise to allow the response to be accessed anywhere not just inside request module function
    function httpRequest(url, method) {
      return new Promise ((resolve, reject) => {
          r({
            url: url,
            method: method,
          }, function(err, resp, data) {
            resolve(data)
          }) 
      })
    }
    /*
        @param 1 > holds the object who we will loop through
        @param 2 > holds function who will be executed after the loop is finished and presend the result after scraping through all of the object keys as argument to execute your function after scraping the object
        @param 3, param 4 > they shouldn't get any value they are uses for the recursion operation so the only required is just the first, second parameter
    */
    function loop(el, fn, val = [], n = 0) {
      //access object keys as array
      var elKeys = Object.keys(el);
      var valKeys = Object.keys(val);
      //get the key and url of the function
      var url = el[elKeys[n]], k = elKeys[n];
    
      //codition to stop the function and execute fn param after finishing the loop
      if (elKeys.length !== valKeys.length) {
        httpRequest(url, "GET").then(data => {
          //parse the results as dom object to access elements
          var doc = new JSDOM(data)
          //access title tag
          var title = doc.window.document.getElementsByTagName("title")[0].innerHTML
          console.log(title)
    
          //add elements for val array for comparison operation
          val.push(title)
          //increment {n} variable to navigate into the next index when re use the function
          n++;
          //recursion function
          loop(el, fn, val, n)
        })
      } else {
        //after finished pass the result as argument and execute my function
        fn(val)
      }
    }
    
    
    var obj = {
        "url 1": "https://example.com/",
        "url 2": "https://www.google.com/",
        "url 3": "https://www.youtube.com/"
    }
    loop(obj, function(results) {
        console.log(results, "finished")
    })
    

    我希望这对某人有帮助

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-10-04
      • 2020-09-07
      • 1970-01-01
      • 2015-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多