forEach中调用promise 如何控制执行顺序

最近在做一个网页爬虫,先抓取列表页面,再获取列表页所有内容页的url,然后将所有列表页的url循环调用抓取方法,这样导致抓取的顺序不可控,想知道如何能够控制抓取的顺序。
例如:正在抓取A页面, A页面抓取完毕;正在抓取B页面, B页面抓取完毕...按这样的顺序执行。

抓取函数:

function doRequest (url) {
    console.log(chalk.red(`正在抓取 ${url} 的内容...`));
    return new Promise ((resolve, reject) => {
        request
        .post(url)
        .set(headers)
        .charset('utf-8')
        .then(result => {
            resolve(result.text);
            console.log(chalk.red(`${url} 的内容抓取完毕!`));
        })
        .catch(err => {
            reject(err);
        })
    });
    } 

调用

// 请求列表
doRequest('list.html')
.then(content => {
    return this.parseList(content); // 得到所有的内容页面地址
})
// 请求内容页
.then(links => {
    return Promise.all(links.map(link => {
        return doRequest(link);
    }))
})
.then (allContent => {
    console.log(allContent);
})

执行的结果

图片描述

阅读 7.3k
4 个回答

都放then里可以认为你的这段逻辑变成了同步,效率会很低,可以把每个结果放在数组的固定位置,用promise.all方法判断全部加载完再处理要输出的返回数组。

下面是个人的想法,题主可以试一下

let arr = []
// 顺序添加抓取页面 promise 
arr.push(request.post(url).set(headers).charset('utf-8'))  // 抓取第一个页面
arr.push(request.post(url).set(headers).charset('utf-8'))  // 抓取第二个页面
arr.push(request.post(url).set(headers).charset('utf-8'))  // 抓取第三个页面

const handler = () => {
  if (arr.length) {
     let promise = arr.shift();
     promise.then((val) => {
       ......
       console.log(val) // 数据处理程序
     }).then(() => {
        // 下一个
        handler()
     })
   }
}

handler()

可以直接用 async/await;
要是有一个Promise的队列会方便很多, 安利一个 promise-queue-plus;

于是基于你的代码可以这样写:

var Queue = require('promise-queue-plus');
var q1 = new Queue(1);  //并发为1的队列

// 请求列表
doRequest('list.html')
.then(content => {
    return this.parseList(content); // 得到所有的内容页面地址
})
// 请求内容页
.then(links => {
    return q1.addLikeArray(links,doRequest,{
        "retry":1, //重试次数
        "timeout":10000  //超时
    },true);
})
.then (allContent => {
    console.log(allContent);
})
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏