优秀博文翻译系列
原文翻译自: Jake Archibald Blog
下面的js函数,哪个return 会执行?
function test() {
return 'one';
return 'two';
return 'three';
}
你可能会说:“会执行第一个。”
但是我将尝试去说服你,将是“最后一个”return得到执行。
无需担心,上面的函数一定是返回one,但是在这个例子中,第一个return声明阻止了其他Return语句的执行。也就是说,此处实际上最后一个被执行的Return语句是return 'one',并且它是胜出者。当然,它确实是第一个声明的return,但我还是对的^_^。
我知道你在想:“闭嘴吧。”,但是请容我慢慢道来。
finally语句
finally 语句是这样一种东西:
function finallyTest() {
try {
console.log('one');
return 'three';
} catch (err) {
console.log('error');
} finally {
console.log('two');
}
}
console.log(finallyTest());
console.log('four');
上面的函数依次打印 'one', 'two', 'three', 'four'。finally代码块中的内容总是会在try/catch执行结束后被执行,即使try/catch被return返回。
我之前不经常在JavaScript中用finally,直到最近,我发现我需要在一个async函数中像这样使用它:
async function someAsyncThing() {
startSpinner();
try {
await asyncWork();
} catch (err) {
if (err.name === 'AbortError') return;
showErrorUI();
} finally {
stopSpinner();
}
}
无论如何,激动人心的是,finally给了我们在一次函数调用中执行多次return的机会:
function manyHappyReturns() {
try {
return 'one';
} finally {
try {
return 'two';
} finally {
return 'three';
}
}
}
并且,执行 manyHappyReturns() 的结果是 'three'
最后一个Return总是获胜者。
并不是在函数中最后一个出现的return会获胜,那将令人发疯。而是最后一个被执行的return语句。
最后一个Return会胜出,和最后一个变量赋值会胜出的原理相同。事实上,这一现象很像变量赋值的过程:return赋值给函数的结果,所以后面的return覆盖了前面的return。在Java和Python中也是这样。感谢Daniel Ehrenberg 让我理解了这个小技巧。
副作用是,从finally中执行return,会清除抛出的Error:
function catchThis() {
try {
throw Error('boom');
} finally {
return 'phew';
}
}
执行catchThis() 的结果是: 'phew'.
额外福利: Promises
async函数与上面的普通函数在这一特性上表现相同(除了返回值被包装成promise)。然而,promise.finally()表现与之不同。
const promise = Promise.resolve('one').finally(() => 'two');
这里,promise被实现为'one'。这可能是因为promise的响应是回调函数,并且回调函数的调用者(在这里是promise本身),根本无法分辨一个函数到底是执行了return undefined还是完全没有执行return语句。因此,它不能模拟上面普通的finally特性,而仅仅是选择忽略了它。
虽然, promise.finally确实影响了promise被实现的时机:
const wait = (ms) => new Promise((r) => setTimeout(() => r(), ms));
const promise = Promise.resolve('one').finally(async () => {
await wait(2000);
return 'two';
});
在这里,promise仍然被实现为'one',但是却是在2s之后。