Javascript for循环中异步函数的顺序

Javascript for循环中异步函数的顺序,javascript,asynchronous,Javascript,Asynchronous,考虑以下for循环 import * as fs from 'fs' function listAllJs() { let files = [ 'abc.js', 'bcd.js', 'e', 'main.js', 'maincopy.ts', 'mainfixedbug.ts', 'package-lock.json', 'package.json' ] for (let i = 0; i<files.length; i++) { let file = file

考虑以下for循环

import * as fs from 'fs'

function listAllJs() {
  let files = [ 'abc.js', 'bcd.js', 'e', 'main.js', 'maincopy.ts', 'mainfixedbug.ts', 'package-lock.json', 'package.json' ]
    for (let i = 0; i<files.length; i++) {
      let file = files[i]
      fs.stat(file, (err, stat) => {
        console.log(i, file)
      });
    }
  
}

listAllJs()

或其他可能的组合,如1 3 2 4 6 7 5

索引不是按升序排列的,终端将打印出不同的顺序。
但是当我在异步函数之前添加console.log(i)时

function listAllJs() {
  let files = [ 'abc.js', 'bcd.js', 'e', 'main.js', 'maincopy.ts', 'mainfixedbug.ts', 'package-lock.json', 'package.json' ]
    for (let i = 0; i<files.length; i++) {
      let file = files[i]
      console.log(i)
      fs.stat(file, (err, stat) => {
        console.log(i, file)
      });
    }
}

我知道这类问题很没用,但我正在学习异步函数,我真的想知道它背后的原因。有什么解释吗?

“stat”正在后台运行,您的程序将在不等待完成的情况下继续运行。因此,它的多个实例同时运行,这使得哪个实例将首先打印输出变得不可预测。您的“console.log(i)”可能需要足够长的时间来确定哪个先完成。

“stat”在后台运行,您的程序将继续运行,而不等待它完成。因此,它的多个实例同时运行,这使得哪个实例将首先打印输出变得不可预测。您的“console.log(i)”可能需要足够长的时间来确定哪个先完成。

无法保证运行和触发回调函数所需的时间

当您的循环只调用它时,各种调用非常接近,并且
fs.stat
运行的时间段重叠。有时一个开始晚的人会结束得更快

当您添加
console.log
语句时,会使每个循环的时间稍微延长一点

在您的特定测试用例中,在您的计算机上,当您的计算机处于您正在品尝的负载下时,会发生这种情况,使完成循环电路所需的时间略长于获取数据所需的时间
fs.stat

由于对
fs.stat
的调用间隔更大,因此它们恰好按顺序完成


你不能依赖这种行为


如果您想让他们按顺序返回,则:

  • 用标记
    listAllJs
  • 。承诺应该解析为您想要的值,并且您不应该将该值记录在回调中
  • 等待
    承诺并收集其返回值并记录该值
  • 这样


    无法保证运行并触发回调函数需要多长时间

    当您的循环只调用它时,各种调用非常接近,并且
    fs.stat
    运行的时间段重叠。有时一个开始晚的人会结束得更快

    当您添加
    console.log
    语句时,会使每个循环的时间稍微延长一点

    在您的特定测试用例中,在您的计算机上,当您的计算机处于您正在品尝的负载下时,会发生这种情况,使完成循环电路所需的时间略长于获取数据所需的时间
    fs.stat

    由于对
    fs.stat
    的调用间隔更大,因此它们恰好按顺序完成


    你不能依赖这种行为


    如果您想让他们按顺序返回,则:

  • 用标记
    listAllJs
  • 。承诺应该解析为您想要的值,并且您不应该将该值记录在回调中
  • 等待
    承诺并收集其返回值并记录该值
  • 这样


    fs/promises和fs.Dirent

    这是一个高效、无阻塞的
    ls
    程序,使用节点的快速对象和模块。这种方法允许您跳过每个路径上浪费的
    fs.exist
    fs.stat
    调用-

    // main.js
    import { readdir } from "fs/promises"
    import { join } from "path"
    
    async function* ls (path = ".")
    { yield path
      for (const dirent of await readdir(path, { withFileTypes: true }))
        if (dirent.isDirectory())
          yield* ls(join(path, dirent.name))
        else
          yield join(path, dirent.name)
    }
    
    async function* empty () {}
    
    async function toArray (iter = empty())
    { let r = []
      for await (const x of iter)
        r.push(x)
      return r
    }
    
    toArray(ls(".")).then(console.log, console.error)
    
    让我们获取一些示例文件,这样我们就可以看到
    ls
    正在工作-

    $warn add immutable#(只是一些示例包)
    $node main.js
    
    [
    '.',
    'main.js',
    “节点_模块”,
    “节点\单元/纱线完整性”,
    “节点_模块/不可变”,
    “节点\模块/不可变/许可证”,
    “node_modules/immutable/README.md”,
    “节点\模块/不可变/可控”,
    “节点\模块/不可变/控制/游标”,
    'node_modules/immutable/contrib/cursor/README.md',
    '节点\模块/不可变/contrib/cursor/\测试\',
    '节点\模块/immutable/contrib/cursor/\测试\测试\ cursor.ts.skip',
    “node_modules/immutable/contrib/cursor/index.d.ts”,
    'node_modules/immutable/contrib/cursor/index.js',
    “节点\模块/不可变/dist”,
    'node_modules/immutable/dist/immutable nonambient.d.ts',
    'node_modules/immutable/dist/immutable.d.ts',
    'node_modules/immutable/dist/immutable.es.js',
    'node_modules/immutable/dist/immutable.js',
    'node_modules/immutable/dist/immutable.js.flow',
    'node_modules/immutable/dist/immutable.min.js',
    “node_modules/immutable/package.json”,
    'package.json',
    “纱线,锁”
    ]
    
    我们只想过滤
    文件-

    
    从“路径”导入{extname}
    异步函数*过滤器(iter=empty(),test=x=>x)
    {等待(国际热核实验堆常数x)
    if(布尔(测试(x)))
    产量x
    }
    const lsJs=(path=“.”)=>
    过滤器//
    提取
    ? 过滤器(ls(路径),p=>extname(p)==ext)
    :ls(路径)
    toArray(lsExt(“.”,“.json”)。然后(console.log,console.error)
    // => ...
    
    [
    “node_modules/immutable/package.json”,
    'package.json'
    ]
    

    调试承担太多责任的大函数是很困难的。解决这个问题使我们的程序更容易编写,我们的函数也具有高度的可重用性。下一步是在模块中定义我们的一组功能。有关利用异步生成器的补充说明和其他方法,请参阅。

    fs/promises和fs.Dirent

    这是一个高效、无阻塞的
    ls
    程序,使用节点的快速对象和模块。这种方法允许您跳过浪费的
    fs.exist
    fs.stat
    调用
    function listAllJs() {
      let files = [ 'abc.js', 'bcd.js', 'e', 'main.js', 'maincopy.ts', 'mainfixedbug.ts', 'package-lock.json', 'package.json' ]
        for (let i = 0; i<files.length; i++) {
          let file = files[i]
          console.log(i)
          fs.stat(file, (err, stat) => {
            console.log(i, file)
          });
        }
    }
    
    0
    1
    2
    3
    4
    5
    6
    7
    0 abc.js
    1 bcd.js
    2 e
    3 main.js
    4 maincopy.ts
    5 mainfixedbug.ts
    6 package-lock.json
    7 package.json
    
    function getStat(file) {
        return new Promise( (res, rej) => {
           fs.stat(file, (err, stat) => {
             res(file);
           });
        };
    }
    
    function listAllJs() {
      let files = [ 'abc.js', 'bcd.js', 'e', 'main.js', 'maincopy.ts', 'mainfixedbug.ts', 'package-lock.json', 'package.json' ]
        for (let i = 0; i<files.length; i++) {
          let file = files[i];
          const stat = await getStat(file);
          console.log(i, stat)
        }
    }
    
    function listAllJs() {
      let files = [ 'abc.js', 'bcd.js', 'e', 'main.js', 'maincopy.ts', 'mainfixedbug.ts', 'package-lock.json', 'package.json' ];
        const promises = [[];
        for (let i = 0; i<files.length; i++) {
          let file = files[i];
          const stat = getStat(file);
          promises.push(stat);
        }
        const stats = await Promise.all(promises);
        stats.forEach( (stat, index) {
            console.log(index, stat)
        });
    }
    
    // main.js
    import { readdir } from "fs/promises"
    import { join } from "path"
    
    async function* ls (path = ".")
    { yield path
      for (const dirent of await readdir(path, { withFileTypes: true }))
        if (dirent.isDirectory())
          yield* ls(join(path, dirent.name))
        else
          yield join(path, dirent.name)
    }
    
    async function* empty () {}
    
    async function toArray (iter = empty())
    { let r = []
      for await (const x of iter)
        r.push(x)
      return r
    }
    
    toArray(ls(".")).then(console.log, console.error)