编写阻止 JavaScript

huangapple go评论96阅读模式
英文:

Write blocking javascript

问题

以下是您要翻译的内容:

我有一个小型服务器,我正在尝试从中获取一堆文件;大约100个要获取。我正在使用JS通过forEach循环来获取这些文件。不幸的是,这大约100个请求会导致服务器崩溃。

  1. links.forEach((link) => {
  2. const name = link.split("/")[5];
  3. const file = fs.createWriteStream(name);
  4. const request = http.get(link, (res) => {
  5. res.pipe(file);
  6. file.on("finish", () => {
  7. file.close();
  8. });
  9. });
  10. });

我正在尝试编写同步阻塞的JavaScript;我希望在开始下一个获取操作之前文件写入完成。我一直在尝试使用生成器、while循环和fs.writeFileSync。我还使用setTimeout来模拟慢速网络调用(这样我就不必在每次崩溃后重新设置服务器了)。

看起来我漏掉了一些概念。这是我的一个非常天真的实验,我认为应该需要3秒,但实际只需要1秒。经过思考,很明显所有的超时都是同时发生的。

  1. function writeSlow(path) {
  2. setTimeout(function () {
  3. console.log("write now");
  4. fs.writeFileSync(path, "lorem");
  5. }, 1000);
  6. }
  7. writeSlow("junk/one");
  8. writeSlow("junk/two");
  9. writeSlow("junk/three");

我进行了一些阅读,确信使用Promises是正确的方法,但似乎也不起作用:

  1. function sleep(ms) {
  2. return new Promise((resolve) => setTimeout(resolve, ms));
  3. }
  4. async function run() {
  5. arr.forEach(async (str) => {
  6. await sleep(1000);
  7. fs.writeFileSync("junk/" + str + ".txt", "lorem");
  8. });
  9. }

我的期望,或者我试图达到的目标,是在实验性代码中,我可以监视文件系统,每秒钟会出现一个新文件。

(编辑)
我寻求的实际最终结果是,只有在上一个http请求完成后,才会触发下一个请求。

英文:

I have a tiny server I'm trying to fetch a bunch of files from; ~100 to fetch. I'm using JS to fetch the files using a forEach loop. Unfortunately, the ~100 requests knocks over the server.

  1. links.forEach((link) => {
  2. const name = link.split("/")[5];
  3. const file = fs.createWriteStream(name);
  4. const request = http.get(link, (res) => {
  5. res.pipe(file);
  6. file.on("finish", () => {
  7. file.close();
  8. });
  9. });
  10. });

I'm trying to write synchronous blocking JavaScript; I want the file writing to complete before beginning the next fetching action. I've been experimenting with generators, while loops, and fs.writeFileSync. I've also been using setTimeout to emulate a slow network call (just so I don't have to reset the server after knocking it over each time).

It seems like I'm missing some concept. Here's my very naive experiment that I thought should take 3 seconds, but only takes 1. After thinking about it, it's clear all the timeouts are happening at once.

  1. function writeSlow(path) {
  2. setTimeout(function () {
  3. console.log("write now");
  4. fs.writeFileSync(path, "lorem");
  5. }, 1000);
  6. }
  7. writeSlow("junk/one");
  8. writeSlow("junk/two");
  9. writeSlow("junk/three");

I did some reading and was convinced using Promises was the way to go, but this doesn't appear to work either:

  1. function sleep(ms) {
  2. return new Promise((resolve) => setTimeout(resolve, ms));
  3. }
  4. async function run() {
  5. arr.forEach(async (str) => {
  6. await sleep(1000);
  7. fs.writeFileSync("junk/" + str + ".txt", "lorem");
  8. });
  9. }

My expectation, or what I'm trying to get to, with the experimental code, is the point where I can watch the filesystem, and every second, a new file appears.

(edit)
The actual end result I'm looking for is for the next http request to only fire once the last one completes.

答案1

得分: 1

以下是翻译好的部分:

  1. 你可以编写一个异步循环
  2. function loop(links, i) {
  3. if (i >= links.length) return; // all done
  4. const link = links[i];
  5. const name = link.split("/")[5];
  6. const file = fs.createWriteStream(name);
  7. const request = http.get(link, (res) => {
  8. res.pipe(file);
  9. file.on("finish", () => {
  10. file.close();
  11. loop(links, i+1); // Continue with next
  12. });
  13. });
  14. }
  15. // Start the asynchronous loop:
  16. loop(links, 0);
  17. 或者你可以切换到基于Promise的库或者像这样将你的基于回调的函数promisify未经测试):
  18. // Promisify your callback-based functions
  19. const asyncWrite = (name, res) => new Promise((resolve) => {
  20. const file = fs.createWriteStream(name);
  21. res.pipe(file);
  22. file.on("finish", () => {
  23. file.close();
  24. resolve();
  25. });
  26. });
  27. const asyncHttpGet = (link) => new Promise((resolve) =>
  28. http.get(link, resolve)
  29. );
  30. // ... and use them:
  31. async function writeAllLinks(links) {
  32. for (const link of links) {
  33. await asyncWrite(link.split("/")[5], await asyncHttpGet(link));
  34. }
  35. }
  36. // Start the asynchronous loop:
  37. writeAllLinks(links).then( /* .... */ );
  38. 你可能想要添加错误处理...
英文:

You could write an asynchronous loop:

  1. function loop(links, i) {
  2. if (i >= links.length) return; // all done
  3. const link = links[i];
  4. const name = link.split("/")[5];
  5. const file = fs.createWriteStream(name);
  6. const request = http.get(link, (res) => {
  7. res.pipe(file);
  8. file.on("finish", () => {
  9. file.close();
  10. loop(links, i+1); // Continue with next
  11. });
  12. });
  13. }
  14. // Start the asynchronous loop:
  15. loop(links, 0);

Alternatively you could switch to promise-based libraries, or else, promisfy the callback-based functions you have, like so (not tested):

  1. // Promisify your callback-based functions
  2. const asyncWrite = (name, res) => new Promise((resolve) => {
  3. const file = fs.createWriteStream(name);
  4. res.pipe(file);
  5. file.on("finish", () => {
  6. file.close();
  7. resolve();
  8. });
  9. });
  10. const asyncHttpGet = (link) => new Promise((resolve) =>
  11. http.get(link, resolve)
  12. );
  13. // ... and use them:
  14. async function writeAllLinks(links) {
  15. for (const link of links) {
  16. await asyncWrite(link.split("/")[5], await asyncHttpGet(link));
  17. }
  18. }
  19. // Start the asynchronous loop:
  20. writeAllLinks(links).then( /* .... */ );

You would probably want to add error handling...

答案2

得分: 0

forEach 不像你可能认为的那样在使用 async/await 时表现。查看此 StackOverflow 帖子以获取更多信息:使用 async/await 与 forEach 循环

相反,在你的情况下(如你在上面看到的),你可以使用标准的 for 循环:

  1. function sleep(ms, fileName) {
  2. return new Promise(resolve => setTimeout(() => resolve(fileName), ms))
  3. }
  4. const files = ["file1", "file2", "file3"];
  5. async function run() {
  6. for(let i = 0; i < files.length; i++) {
  7. const fileName = await sleep(1000, files[i])
  8. console.log(fileName, new Date().toLocaleTimeString())
  9. }
  10. }
  11. run();
英文:

forEach doesn't behave like you may think it will with async/await. Take a look at this post on SO for more information:Using async/await with a forEach loop

Instead, in your case (as you will see above), you can use a standard for loop:

  1. function sleep(ms, fileName) {
  2. return new Promise(resolve =&gt; setTimeout(() =&gt; resolve(fileName), ms))
  3. }
  4. const files = [&quot;file1&quot;, &quot;file2&quot;, &quot;file3&quot;];
  5. async function run() {
  6. for(let i = 0; i &lt; files.length; i ++) {
  7. const fileName = await sleep(1000, files[i])
  8. console.log(fileName, new Date().toLocaleTimeString())
  9. }
  10. }
  11. run();

huangapple
  • 本文由 发表于 2023年6月25日 20:59:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/76550523.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定