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

Catch block does not execute immediately after exception is thrown in try block

我有一些 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() 抛出异常后立即处理吗?

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

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)
    }
  }
) 

如果它正在处理多行,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也无所谓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
  });
})()