【问题标题】:Why are promises in Promise.all still pending?为什么 Promise.all 中的 Promise 仍然未决?
【发布时间】:2016-09-26 21:59:33
【问题描述】:

这是我尝试重组代码以正确利用 Promise 的尝试。整个程序是一个基本的网络爬虫。

挑战在于尝试确保 lastStep 可以访问每个页面的 HTML 和 URL,因此我尝试在 nextStep() 中返回一个对象。

我正在控制台记录 html,它被正确返回,但由于某种原因,承诺被记录如下:Promise { <pending> }。为什么会发生这种情况,我该如何解决?

谢谢!

//Modules being used:
var cheerio = require('cheerio');
var json2csv = require('json2csv');
var request = require('request');
var moment = require('moment');
var fs = require('fs');

//harcoded url
var url = 'http://shirts4mike.com/';

//url for tshirt pages
var urlSet = new Set();

var remainder;
var tshirtArray = [];


const requestPromise = function(url) {
    return new Promise(function(resolve, reject) {
        request(url, function(error, response, html) {

            if(error) return reject(error);

            if(!error && response.statusCode == 200){
                return resolve(html);   
            }       
        });
    });
}


function scrape (url) {
    return requestPromise(url)
        .then(function(html) {
            var $ = cheerio.load(html);

            var links = [];

            //get all the links
            $('a[href*=shirt]').each(function(){
                var a = $(this).attr('href');

                //add into link array
                links.push(url + a);
            });
            // return array of links
            return links;
        });
}


function nextStep (arrayOfLinks) { 
    var promiseArray = [];

    for(var i = 0; i < arrayOfLinks.length; i++){
        promiseArray.push(requestPromise(arrayOfLinks[i]));
        var promises = Promise.all(promiseArray);
        console.log(promises);
    }

    return {arrayOfHtml: promises , arrayOfUrls: arrayOfLinks};                 
}


function lastStep (obj){ 
    for(var i = 0;  i < obj.arrayOfHtml.length; i++){
        var $ = cheerio.load(obj.arrayOfHtml[i]);

        //if page has a submit it must be a product page
        if($('[type=submit]').length !== 0){

            //add page to set
            urlSet.add(obj.arrayOfUrls[i]);
            console.log(obj.arrayOfUrls[i]);

        } else if(remainder == undefined) {
            //if not a product page, add it to remainder so it another scrape can be performed.
            remainder = obj.arrayOfUrls[i];
            console.log("remainder: " + remainder);                         
        }
    }
}


scrape(url)
    .then(nextStep)
    .then(lastStep)
    .catch(function(err) {
        // handle any error from any request here
        console.log(err);
     });

【问题讨论】:

  • 如果没有错误,但response.statusCode不是200会怎样?
  • 不,现在没有错误。如果我输入if(error){ console.log(response.statusCode); return reject(error);},我仍然没有在控制台中得到任何不同。
  • @bloppit 这不是问题。如果存在not 错误且response.statusCode not 等于200 怎么办?例如,想象一下response.statusCode === 1。然后会发生什么?
  • 对不起,迈克,我不听。如果我 console.log 输入我的 requestPromise,我会一直得到 200 的回报
  • @bloppit 假装error = false。也假装response.statusCode = 1,好吗?如果您愿意,您甚至可以在 request 回调的顶部编写该代码。发生什么了?你有没有打电话给rejectresolve

标签: javascript node.js web-scraping promise


【解决方案1】:

您可以尝试几件事。首先,在您的requestPromise 函数中,当您调用'resolve()' 和reject() 时不需要返回。我不知道这是否会有所不同,但您至少可以尝试一下。

接下来,正如 cmets 中所讨论的,您应该更改拒绝和解决请求承诺的方式。最简单的:

if(error) {
    reject(error);
} else {
    resolve(html);
}

假设没有错误(错误只会发生在 4xx 或 5xx 状态码),但状态码不是 200?您可以得到 2xx 或 3xx 范围内的任何内容而不会出现错误,在这种情况下,您的 requestPromise 将永远不会被解决或拒绝。这肯定会引起你的问题,因为所有的承诺都必须以一个或另一个结束。

下一期在nextStep。我会重构如下:

function nextStep (arrayOfLinks) { 
    var promiseArray = [];

    for(var i = 0; i < arrayOfLinks.length; i++){
        promiseArray.push(requestPromise(arrayOfLinks[i]));
    }

    return Promise.all(promiseArray)
          .then(function (arrayOfHtml) {
            return {arrayOfHtml: promises , arrayOfUrls: arrayOfLinks};
          });                
} 

使用Promise.all,您希望首先填写您的承诺数组,然后在您完成所有异步调用之后,您就可以调用Promise.all(promisesArray)all 末尾的额外 then 将获取您的 promises 数组产生的 html,然后将其作为 promise 与 arrayOfLinks 一起返回到您的 promise 链中的下一步,在这种情况下是您的lastStep.

如果这些都不能解决你的问题,你需要回头看看状态码问题,我之前也有问题,状态码是202,表示请求被接受,但是请求的处理没有不完整。 (您可以阅读更多关于 HTTP 状态码 here 的信息)。这是一个非常相似的情况,我们有一堆我们正在向其发出请求的 url。我们最终将获得 202 的所有 url 放回 tryAgain 数组中,然后再次尝试访问它们。

在您的情况下,您有几种方法可以解决它。最简单的事情是拒绝除 200 之外的所有状态码的承诺,这会有点严格。您可以做的另一件事是,如果没有错误并且状态码不是 200,那么您可以使用一些特殊值或简单的非 200 状态码来解决承诺,这表明您需要再试一次。然后在nextStep 之后,您可以过滤所有使用非 200 代码解析的结果,然后再次尝试点击它们。之后,您可以使用lastStep 结束。如果您尝试了其他所有方法但均无效,我会尝试其中一种解决方案。不过这需要一些努力。

希望这会有所帮助。如果您有任何问题,请告诉我。

【讨论】:

    【解决方案2】:

    您的代码中有一个未处理的else

    if(error) return reject(error);
    
    if(!error && response.statusCode == 200){
        return resolve(html);   
    }
    

    让我们重新安排一下,以便更清楚。由于return,上面的代码与此完全相同:

    if(error) {
        reject(error);
    }
    else if (response.statusCode == 200) {
        resolve(html);   
    }
    else {
        // keep this promise pending FOREVER!!
    }
    

    你还没有处理最后的 else。根据您的意图,您可以进行的最小更改是:

    if(error) return reject(error);
    
    if(!error && response.statusCode == 200){
        return resolve(html);   
    }
    
    reject(new Error('Not code 200'));
    

    if(error) return reject(error);
    
    if(!error && response.statusCode == 200){
        return resolve(html);   
    }
    
    resolve(html);
    

    不过,我还是会亲自重写逻辑以使其更清晰(您错过了最后的 else 的事实证明代码不清楚)。

    【讨论】:

      猜你喜欢
      • 2018-02-06
      • 1970-01-01
      • 2017-09-10
      • 2020-04-11
      • 2020-09-03
      • 2021-08-19
      • 2020-06-23
      • 1970-01-01
      • 2020-09-03
      相关资源
      最近更新 更多