js中的finally一定会执行吗?

1,748 阅读3分钟

背景

在我们程序开发中,我们的代码会出现这种或那种的错误,我们使用try...catch进行捕获。如果需要不管是成功还是失败都需要执行,我们可能需要finally

那么有一个问题,无论是否发生错误,在finally中的代码一定会执行吗?

下面我们看一个案例:

1. 案例

场景:请求一个接口,如果接口没有正确返回,我们使用try...finally包裹代码,代码如下:

function getMember(num) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (num === 1) { 
                resolve(num) 
            }
            if (num === 0) { 
                reject() 
            }
        }, 2000)
    })
}

async function init() {
    try {
        console.log('打印***start')
        await getMember(0)
        console.log('打印***end')
    } catch (err) {
        console.log('打印***err')
    } finally {
        console.log('打印***finally')
    }
}

结果如下:

image.png

上述案例中,如果请求传入的num由另外一个接口返回,num的值不是0或者1,上述的getMember就一直处于pengding状态,接下来的finally也不会执行。

如何处理,才能执行finally操作?

上述代码可以完善如下:

function getMember(num) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (num === 1) {
                resolve(num);
            } else if (num === 0) {
                reject(new Error('Num is 0'));
            } else {
                // 默认情况,也解决Promise  
                resolve('Some default value');
            }
        }, 2000);
    });
}

async function init() {
    try {
        console.log('打印***start');
        const result = await getMember(2); // 传递一个非0非1的值  
        console.log('打印***end', result);
    } catch (err) {
        console.log('打印***err', err);
    } finally {
        console.log('打印***finally'); // 这行总是会被执行  
    }
}

init();

修改后的例子中,无论num的值是什么,Promise都会被解决(要么通过resolve,要么通过reject),,确保Promise被正常处理,才能确保finally执行。

2. try...catch注意点

2.1 仅对运行时的 error 有效

要使得 try...catch 能工作,代码必须是可执行的。换句话说,它必须是有效的 JavaScript 代码。

如果代码包含语法错误,那么 try..catch 将无法正常工作,例如含有不匹配的花括号:

try {
    {
{
} catch (err) {
    alert("引擎无法理解这段代码,它是无效的");
}

结果如下:

image.png

JavaScript 引擎首先会读取代码,然后运行它。在读取阶段发生的错误被称为“解析时间(parse-time)”错误,并且无法恢复(从该代码内部)。这是因为引擎无法理解该代码。

所以,try...catch 只能处理有效代码中出现的错误。这类错误被称为“运行时的错误(runtime errors)”,有时被称为“异常(exceptions)”。

2.2 try...catch 同步执行

如果在定时代码中发生异常,例如在 setTimeout 中,则 try...catch 不会捕获到异常:

try {
    setTimeout(function () {
        noSuchVariable; // 脚本将在这里停止运行
    }, 1000);
} catch (err) {
    alert("不工作");
} finally {
    console.log('打印***finally')
}

结果如下:

image.png

因为 try...catch 包裹了计划要执行的函数,该函数本身要稍后才执行,这时引擎已经离开了 try...catch 结构。

为了捕获到计划的(scheduled)函数中的异常,那么 try...catch 必须在这个函数内:

try {
    setTimeout(function () {
        try {
            noSuchVariable; // 脚本将在这里停止运行
        } catch (error) {
            console.log(error)
        }
    }, 1000);
} catch (err) {
    alert("不工作");
} finally {
    console.log('打印***finally')
}

结果如下: image.png

总结

在使用try...catch...finally的时候,无论是否发生异常(即是否执行了catch块),finally块中的代码总是会被执行,除非在trycatchfinally块中发生了阻止程序继续执行的情况(如Promsie一直处理pending状态)。

如有错误,请指正O^O!