V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
yuuk520
V2EX  ›  Node.js

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

  •  1
     
  •   yuuk520 · 2018-05-04 23:34:46 +08:00 · 8420 次点击
    这是一个创建于 2392 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    执行的结果

    图片描述

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

    23 条回复    2018-05-05 14:24:38 +08:00
    soooon
        1
    soooon  
       2018-05-04 23:45:54 +08:00
    用 async 模块串行执行吧: https://github.com/caolan/async
    ashong
        2
    ashong  
       2018-05-04 23:48:27 +08:00
    array.reduce
    xudaiqing
        3
    xudaiqing  
       2018-05-04 23:56:06 +08:00
    Bluebird 的 Promise.each
    或者手动拼接 then
    ashong
        4
    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
        5
    yuuk520  
    OP
       2018-05-05 00:26:51 +08:00
    @ashong 这个也只是将所有的请求结果一次性范围。并没有保证顺序~
    IvanLi127
        6
    IvanLi127  
       2018-05-05 00:48:55 +08:00 via Android
    等待上一个执行完成,再执行下一个。await 了解一下?话说回来,你为啥要顺序执行?
    df4VW
        7
    df4VW  
       2018-05-05 00:52:05 +08:00
    1 楼都已经回答你喽
    ashong
        8
    ashong  
       2018-05-05 00:53:25 +08:00
    @yuuk520 确定是保证顺序的
    dd0754
        9
    dd0754  
       2018-05-05 01:14:33 +08:00
    加个字段来排序不是更好?页面多了你也一个一个爬啊?
    ETiV
        10
    ETiV  
       2018-05-05 01:17:36 +08:00 via iPhone
    bluebird +1
    des
        11
    des  
       2018-05-05 02:15:06 +08:00 via Android
    没人说正经的 for of ???
    des
        12
    des  
       2018-05-05 02:18:00 +08:00 via Android
    @des async + for of 完美解决,反正后端 node 不像前端不能控制版本
    yimity
        13
    yimity  
       2018-05-05 08:21:18 +08:00 via Android
    all 中,你给的数组的顺序是什么,最终结果就是什么顺序,但是不保证拿到结果的过程的顺序。
    yuuk520
        14
    yuuk520  
    OP
       2018-05-05 10:20:16 +08:00
    @des 大佬,有 demo 么~
    des
        15
    des  
       2018-05-05 10:46:04 +08:00 via Android
    @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
        16
    yuuk520  
    OP
       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
        17
    yuuk520  
    OP
       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
        18
    yuuk520  
    OP
       2018-05-05 11:05:56 +08:00
    感谢各位的帮助,谢谢大家。
    ChefIsAwesome
        19
    ChefIsAwesome  
       2018-05-05 11:33:25 +08:00 via Android
    你可以自己再琢磨下怎么实现一次抓几个,这几个结束后再执行后面几个。一个一个抓太慢了。
    POPOEVER
        20
    POPOEVER  
       2018-05-05 12:28:01 +08:00
    基于 express/koa 的话为什么不用中间件?
    mrcode
        21
    mrcode  
       2018-05-05 13:53:48 +08:00
    同 #11,使用 for-of 循环解决
    orzfly
        22
    orzfly  
       2018-05-05 14:23:44 +08:00 via Android   ❤️ 1
    Bluebird.mapSeries(...)
    Bluebird.map(..., { concurrency: 5 })
    orzfly
        23
    orzfly  
       2018-05-05 14:24:38 +08:00 via Android
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3272 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 11:50 · PVG 19:50 · LAX 03:50 · JFK 06:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.