You need to enable JavaScript to run this app. 使用Promise时将变量传递到外部作用域的最佳实践有哪些?
优惠活动
大模型
产品
解决方案
定价
更多

使用Promise时将变量传递到外部作用域的最佳实践有哪些?

问题根源

你遇到的是同步遍历逻辑和异步IO请求的控制流不匹配问题,具体如下:

  • cheerio.each 是纯同步遍历方法,不会等待内部的异步fetchRedirect请求完成就会执行完整个循环
  • 你在循环内直接往parsedResultspush metadata的时候,重定向请求还没返回结果,自然没法把最终url插入对象
  • 最后执行console.log(parsedResults)、关闭浏览器的时机远早于所有异步请求返回的时机,就算你在then里赋值也会出现部分请求还没完成、资源就被释放的问题
最佳实现方案

建议把同步遍历改成异步批量处理,用Promise.all统一等待所有重定向请求完成后再组装结果,修改后的代码如下:

(async function main() {
    const browser = await puppeteer.launch({
        headless: true,
    });
    const page = await browser.newPage();
    await page.goto('https://www.example.com/?q=async+urls&s=s');
    const content = await page.content();
    const $ = cheerio.load(content);

    const fetchRedirect = async (url) => {
        try {
            let response = await doRequest(url);
            return response;
        } catch (err) {
            return false;
        }
    };

    function doRequest(url) {
        return new Promise(function (resolve, reject) {
          request(url, function (error, res, body) {
            if (!error) {
              resolve(getPathFromUrl(res.request.uri.href));
            } else {
              reject(error);
            }
          });
        });
    }

    function getPathFromUrl(url) {
        return url.split(/[?#]/)[0];
    }  

    // 核心修改:把cheerio对象转成普通数组,用异步map处理每个元素
    const parsedResults = await Promise.all(
        $('td[itemprop="subjectOf"]').first().get().map(async (element) => {
            const $el = $(element);
            const originUrl = $el.find('a.title').attr('href');
            const title = $el.find('a.title').text();
            // 等待重定向请求返回
            const redirectUrl = await fetchRedirect(originUrl);
            // 直接组装完整metadata返回
            return {
                title,
                redirectUrl // 重定向后的url直接插入对象
            };
        })
    );

    // 这里打印的就是带完整重定向url的结果
    console.log(parsedResults);
    await page.close()
    await browser.close();
})();
额外说明
  • 上述方案是并行发起所有重定向请求,如果你爬取的目标站点有并发限制,可以把map改成for循环串行await每个请求,避免被封禁
  • 你已经引入了puppeteer的情况下,也可以直接用page.goto的能力获取重定向地址,不需要额外引入request库,减少依赖

内容的提问来源于stack exchange,提问作者coolsaint

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.19 14:15:03