在 try 块中抛出异常后,catch 块不会立即执行

Posted

技术标签:

【中文标题】在 try 块中抛出异常后,catch 块不会立即执行【英文标题】:Catch block does not execute immediately after exception is thrown in try block 【发布时间】: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()抛出异常后立即处理吗?

【问题讨论】:

【参考方案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
  );
)()

【讨论】:

以上是关于在 try 块中抛出异常后,catch 块不会立即执行的主要内容,如果未能解决你的问题,请参考以下文章

JAVA语言如何进行异常处理,关键字throws,throw,try,catch,finally分别代表啥意义在try块中抛出异常吗

php的异常处理,一个try代码块中抛出了多个异常,怎么全都捕获,并输出来?下面的代码为只输出一个异常?

在 PHP Try Catch 块中抛出异常

Java 异常处理

在PHP Try Catch块中抛出异常

如果在 catch 块中抛出异常,是不是会执行 finally 块? [复制]