在 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块中抛出异常吗