当我期望它在 forEach 中时,为啥 node.js 不等待? [复制]

Posted

技术标签:

【中文标题】当我期望它在 forEach 中时,为啥 node.js 不等待? [复制]【英文标题】:Why doesn't node.js wait when I'd expect it to inside a forEach? [duplicate]当我期望它在 forEach 中时,为什么 node.js 不等待? [复制] 【发布时间】:2022-01-04 22:29:19 【问题描述】:

我正在编写一个节点脚本来从带有https module 的 URL 中提取状态代码。该脚本有一组 URL。我正在使用 forEach 循环遍历它们并进行 https.get() 调用。

我看到的问题是 https 调用似乎不会发生(或者,至少不会触发它们的回调函数),直到整个 forEach 循环完成,它们似乎都在运行。

这是我正在使用的代码:

const https = require('https')

const urls = [
  'https://www.example.com/',
  'https://www.example.org/',
  'https://www.example.net/',
  'https://www.example.com/',
  'https://www.example.org/',
  'https://www.example.net/',
  'https://www.example.com/',
  'https://www.example.com/',
  'https://www.example.com/',
  'https://www.example.com/',
  'https://www.example.org/',
  'https://www.example.net/',
]

function processResponse(response) 
  console.log(`$Date.now() - response.statusCode`)


urls.forEach((url) => 
  console.log(`Getting: $Date.now() - $url`)
  https.get(url, (response) => 
    processResponse(response)
  )
)

输出始终是这样的:

Getting: 1637980537771 - https://www.example.com/
Getting: 1637980537810 - https://www.example.org/
Getting: 1637980537811 - https://www.example.net/
Getting: 1637980537813 - https://www.example.com/
Getting: 1637980537816 - https://www.example.org/
Getting: 1637980537818 - https://www.example.net/
Getting: 1637980537820 - https://www.example.com/
Getting: 1637980537821 - https://www.example.com/
Getting: 1637980537823 - https://www.example.com/
Getting: 1637980537827 - https://www.example.com/
Getting: 1637980537830 - https://www.example.org/
Getting: 1637980537832 - https://www.example.net/
1637980537989 - 200
1637980537992 - 200
1637980537995 - 200
1637980537997 - 200
1637980538005 - 200
1637980538006 - 200
1637980538022 - 200
1637980538023 - 200
1637980538024 - 200
1637980538026 - 200
1637980538032 - 200
1637980538035 - 200

状态码的时间戳总是在 forEach 循环的最后一项之后。这就是我不明白的。

由于有数千个 URL 和损坏的 URL,我想确保每个 URL 都是单独处理的,这样我就不会在事情发生意外时失去进展。

我愿意使用其他模块/方法,但我也想知道这里发生了什么,因为它不符合我的心智模型。

【问题讨论】:

【参考方案1】:

您的https.get() 是一个非阻塞函数。它不等待完成。它只是开始操作并立即返回。因此,您的 .forEach() 循环最终会启动所有操作,然后某个时间之后,每个操作都会完成并调用其回调。

如果您想要一个循环等待每个异步操作的循环,那么您应该使用常规的for 循环并使用返回承诺的异步操作。然后,您可以在返回承诺的异步操作上使用await,它会暂停循环。

例如,您可以通过使用 async/await 和返回承诺的 http 库来做到这一点:

const got = require('got');

const urls = [
  'https://www.example.com/',
  'https://www.example.org/',
  'https://www.example.net/',
  'https://www.example.com/',
  'https://www.example.org/',
  'https://www.example.net/',
  'https://www.example.com/',
  'https://www.example.com/',
  'https://www.example.com/',
  'https://www.example.com/',
  'https://www.example.org/',
  'https://www.example.net/',
]

async function runAll() 
   for (let url of urls) 
      console.log(`Getting: $Date.now() - $url`)
       try 
           let result = await got(url);
           console.log(`$Date.now() - $result.statusCode`)
         catch(e) 
            console.log(`$Date.now() - $e.message - $e.response.statusCode`);
        

   


runAll().then(() => 
    console.log("all done");
).catch(err => 
    console.log(err);
);

我愿意使用其他模块/方法,但我也想知道这里发生了什么,因为它不符合我的心智模型。

https.get() 是非阻塞的。它开始操作,然后立即返回。这允许您的循环继续进行而无需等待完成。这就是非阻塞异步操作在 javascript 中的典型工作方式。

【讨论】:

以上是关于当我期望它在 forEach 中时,为啥 node.js 不等待? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

当我尝试将数据保存在 txt (java) 中时,为啥我会得到这些符号

当我将 UIView 动画放在不同类的 containerView 中时,为啥我的 UIView 动画不起作用?

当我在网格/面中实现索引时,为啥它会返回 OpenGL 错误?

当我尝试将用户上传的图像保存在视图中时,为啥 ImageField 中的 upload_to 根本不起作用

当实体在不同的项目中时,为啥 Mediator 不解析方法?

为啥 pool.map 会删除数据操作?