【问题标题】:Catch block does not execute immediately after exception is thrown in try block在 try 块中抛出异常后,catch 块不会立即执行
【发布时间】:2021-11-17 17:46:08
【问题描述】:

我有一些 Javascript 代码,它的行为与我预期的不一样。谁能告诉我这里发生了什么?

这是一个简化版:

    let recordsProcessed = 0
    await parser(fileBuffer,

      // Process row
      async (row: Record<string, any>) => {
        recordsProcessed += 1
        try {
          console.log('Processing record', recordsProcessed)
          await processRow(row)
        } catch (e) {
          console.log('Failure at record', recordsProcessed)
        }
      }
    ) 

    async parser(fileBuffer: Buffer, rowCb: Function, ...) : Promise<number> {
      ... 
      return new Promise((resolve, reject) => {
        parseFile(fileBuffer, options)
          .on('error', (error:any) => reject(error))
          .on('data', async row => await rowCb(row))
          .on('end', (count: any) => resolve(count))
      })
      ...
    }

这里的 parser() 是一个异步函数,但它也调用了一些传递给它的回调(我这里只展示了一个,但有多个)。它为文件中的每一行调用 rowCb() 回调。

这是异步回调中的 try/catch 块,它的行为不符合我的预期。我正在使用一个包含三行的测试文件,这将导致对 processRow() 的每次调用都会引发异常。所以,我希望 console.logs 的输出是:

Processing record 1
Failure at record 1
Processing record 2
Failure at record 2
Processing record 3
Failure at record 3

但我却得到了这个:

Processing record 1
Processing record 2
Processing record 3
Failure at record 3
Failure at record 3
Failure at record 3

为什么会这样?既然我在等待processRow(),它不应该和try/catch块在同一个范围内,因此catch()应该在processRow()抛出异常后立即处理吗?

【问题讨论】:

    标签: javascript asynchronous async-await asynccallback


    【解决方案1】:

    您需要在解析器中添加一个新变量来获取当前记录的编号。您应该使用该变量而不是全局变量。

    let recordsProcessed = 0
    await parser(fileBuffer,
      // Process row
      async (row: Record<string, any>) => {
        recordsProcessed += 1
        let thisRecordNum = recordsProcessed;
        try {
          console.log('Processing record', thisRecordNum)
          await processRow(row)
        } catch (e) {
          console.log('Failure at record', thisRecordNum)
        }
      }
    ) 
    

    【讨论】:

    • 我的问题并不完全清楚。您的更改确实会导致“记录失败”值正确,但我仍然得到三个“处理记录”日志项,然后是三个“记录失败”项。我真正的问题是,为什么每个“记录失败”日志不直接出现在每个“处理记录”日志项之后?
    • @user2943799 哦,那是因为该函数在出错后立即重新运行。新线程在原线程能够处理错误之前到达console.log('Processing...,进入catch函数,最后记录console.log('Failure...
    【解决方案2】:

    如果它处理多行,parseFile() 必须有一些循环。目前尚不清楚它是您的代码还是来自某个库,但该循环要么期望与异步回调一起使用,要么不。也许那些未显示的options 也会影响这一点。

    如果它使用带有await 的循环,输出将是您所期望的:

    async function thrower(i) {
      throw "throwing " + i;
    }
    
    let somevariable = 0;
    async function wrapper(i) {
      try {
        somevariable++;
        console.log("calling", i, "(" + somevariable + ")");
        await thrower(i);
      } catch (x) {
        console.log("caught", x, "(" + somevariable + ")");
      }
    }
    
    (async function() {
      for await (let i of [1, 2, 3])     // <-- async-aware loop
        wrapper(i);
    })()

    但是,如果它不使用await,那么当wrapper() 遇到它自己的await 行时,循环会立即进行:

    async function thrower(i) {
      throw "throwing " + i;
    }
    
    let somevariable = 0;
    async function wrapper(i) {
      try {
        somevariable++;
        console.log("calling", i, "(" + somevariable + ")");
        await thrower(i);
      } catch (x) {
        console.log("caught", x, "(" + somevariable + ")");
      }
    }
    
    (async function() {
      for (let i of [1, 2, 3])           // <-- async-unaware loop
        wrapper(i);
    })()

    而且如果是古代的forEach(),那就算尝试await也没关系:

    async function thrower(i) {
      throw "throwing " + i;
    }
    
    let somevariable = 0;
    async function wrapper(i) {
      try {
        somevariable++;
        console.log("calling", i, "(" + somevariable + ")");
        await thrower(i);
      } catch (x) {
        console.log("caught", x, "(" + somevariable + ")");
      }
    }
    
    (async function() {
      //[1, 2, 3].forEach(wrapper); // <- would be enough to produce the same output
      [1, 2, 3].forEach(async function(i){
        await wrapper(i);           // <- absolutely futile attempt to wait,
                                    //    forEach just can't work asynchronously
      });
    })()

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-18
      • 1970-01-01
      • 2011-03-25
      • 2012-02-20
      • 2017-01-30
      • 2020-12-16
      • 1970-01-01
      • 2020-12-11
      相关资源
      最近更新 更多