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

2018-05-04 23:34:46 +08:00
 yuuk520

最近在做一个网页爬虫,先抓取列表页面,再获取列表页所有内容页的 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);
})

执行的结果

这个结果不是按照顺序来的。

8225 次点击
所在节点    Node.js
23 条回复
soooon
2018-05-04 23:45:54 +08:00
用 async 模块串行执行吧: https://github.com/caolan/async
ashong
2018-05-04 23:48:27 +08:00
array.reduce
xudaiqing
2018-05-04 23:56:06 +08:00
Bluebird 的 Promise.each
或者手动拼接 then
ashong
2018-05-05 00:07:52 +08:00
links.reduce( (promise, link) => { return promise.then( ()=>{
// do request with link
}) }, Promise.resolve()).then( () =>{
// done
})
yuuk520
2018-05-05 00:26:51 +08:00
@ashong 这个也只是将所有的请求结果一次性范围。并没有保证顺序~
IvanLi127
2018-05-05 00:48:55 +08:00
等待上一个执行完成,再执行下一个。await 了解一下?话说回来,你为啥要顺序执行?
df4VW
2018-05-05 00:52:05 +08:00
1 楼都已经回答你喽
ashong
2018-05-05 00:53:25 +08:00
@yuuk520 确定是保证顺序的
dd0754
2018-05-05 01:14:33 +08:00
加个字段来排序不是更好?页面多了你也一个一个爬啊?
ETiV
2018-05-05 01:17:36 +08:00
bluebird +1
des
2018-05-05 02:15:06 +08:00
没人说正经的 for of ???
des
2018-05-05 02:18:00 +08:00
@des async + for of 完美解决,反正后端 node 不像前端不能控制版本
yimity
2018-05-05 08:21:18 +08:00
all 中,你给的数组的顺序是什么,最终结果就是什么顺序,但是不保证拿到结果的过程的顺序。
yuuk520
2018-05-05 10:20:16 +08:00
@des 大佬,有 demo 么~
des
2018-05-05 10:46:04 +08:00
@yuuk520
(async function() {
// 请求列表
comst links = await doRequest('list.html').then(content => {
return this.parseList(content); // 得到所有的内容页面地址
});
for (const link of links) {
// 请求内容页
const allContent = await doRequest(link);
console.log(allContent);
}
})()
yuuk520
2018-05-05 11:00:50 +08:00
@des 厉害!!!这样可以实现,顺便有个疑问想请教一下

doRequest('list.html').then(content => {
return parseList(content);
}).then(links => {
links.forEach(async function(link){
const allContent = await doRequest(link);
});
})

这样为啥就不行呢~
yuuk520
2018-05-05 11:05:30 +08:00
@yuuk520 明白了。。。需要这样写

doRequest('list.html').then(content => {
return parseList(content);
}).then(async function(links){
for (const link of links) {
const allContent = await doRequest(link);
}
})
yuuk520
2018-05-05 11:05:56 +08:00
感谢各位的帮助,谢谢大家。
ChefIsAwesome
2018-05-05 11:33:25 +08:00
你可以自己再琢磨下怎么实现一次抓几个,这几个结束后再执行后面几个。一个一个抓太慢了。
POPOEVER
2018-05-05 12:28:01 +08:00
基于 express/koa 的话为什么不用中间件?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/452256

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX