我正在深入研究节点7 async / await功能,并在这样的代码中磕磕绊绊
async function main() {
try {
var quote = await getQuote();
console.log(quote);
} catch(error) {
console.error(error);
}
}
这似乎是使用async / await解析/拒绝或返回/抛出的唯一可能,但是,v8不会优化try / catch块中的代码?!
有替代方案吗?
答案 0 :(得分:95)
替代方案:
async function main() {
try {
var quote = await getQuote();
console.log(quote);
} catch (error) {
console.error(error);
}
}
将是这样的,明确地使用promises:
function main() {
getQuote().then((quote) => {
console.log(quote);
}).catch((error) => {
console.error(error);
});
}
或类似的东西,使用延续传递样式:
function main() {
getQuote((error, quote) => {
if (error) {
console.error(error);
} else {
console.log(quote);
}
});
}
您的原始代码所做的是暂停执行并等待getQuote()
返回的承诺结算。然后它继续执行并将返回的值写入var quote
,然后如果promise被解析则打印它,或者抛出异常并运行catch块,如果promise被拒绝则打印错误。
您可以像使用第二个示例一样直接使用Promise API执行相同的操作。
现在,为了表现。我们来试试吧!
我刚刚编写了此代码 - f1()
将1
作为返回值,f2()
抛出1
作为例外:
function f1() {
return 1;
}
function f2() {
throw 1;
}
现在让我们拨打相同的代码数百万次,首先使用f1()
:
var sum = 0;
for (var i = 0; i < 1e6; i++) {
try {
sum += f1();
} catch (e) {
sum += e;
}
}
console.log(sum);
然后让f1()
更改为f2()
:
var sum = 0;
for (var i = 0; i < 1e6; i++) {
try {
sum += f2();
} catch (e) {
sum += e;
}
}
console.log(sum);
这是我为f1
得到的结果:
$ time node throw-test.js
1000000
real 0m0.073s
user 0m0.070s
sys 0m0.004s
这是我为f2
获得的:
$ time node throw-test.js
1000000
real 0m0.632s
user 0m0.629s
sys 0m0.004s
在一个单线程进程中,您似乎可以在一秒钟内完成200万次抛出。如果你做的不止于此,你可能需要担心它。
我不担心Node中的那些事情。如果这样的事情被大量使用,那么它最终将由V8或SpiderMonkey或Chakra团队进行优化,每个人都会遵循 - 它并不像它没有被优化为原则,它是不是问题。
即使它没有经过优化,我仍然认为如果你在Node中最大化你的CPU那么你应该用C编写你的数字运算 - 这是什么除其他外,原生插件用于。或者像node.native这样的东西比Node.js更适合这项工作。
我想知道什么是需要抛出这么多例外的用例。通常抛出异常而不是返回值是一个例外。
答案 1 :(得分:12)
答案 2 :(得分:10)
['abcddd', 'bcddd', 'cddd', 'ddd', 'dd', 'd']
['abc', 'bcd', 'cdd', 'ddd']
或者,不是声明可能的var来保持顶部的错误,而是
async function main() {
var getQuoteError
var quote = await getQuote().catch(err => { getQuoteError = err }
if (getQuoteError) return console.error(err)
console.log(quote)
}
虽然如果抛出TypeError或Reference错误之类的东西,这将无效。您可以通过
确保它是一个常规错误if (quote instanceof Error) {
// ...
}
我对此的偏好是将所有内容包装在一个大的try-catch块中,其中创建了多个promise会使得特定于创建它的promise的错误处理变得很麻烦。替代方案是多个try-catch块我觉得同样很麻烦
答案 3 :(得分:10)
因为async / await使用了底层的promises,你可以编写一个像这样的小实用函数:
export function catchEm(promise) {
return promise.then(data => [null, data])
.catch(err => [err]);
}
然后在需要捕获一些错误时导入它,并包装你的异步函数,该函数返回一个promise。
import catchEm from 'utility';
async performAsyncWork() {
const [err, data] = await catchEm(asyncFunction(arg1, arg2));
if (err) {
// handle errors
} else {
// use data
}
}
答案 4 :(得分:1)
我想这样做:)
const sthError = () => Promise.reject('sth error');
const test = opts => {
return (async () => {
// do sth
await sthError();
return 'ok';
})().catch(err => {
console.error(err); // error will be catched there
});
};
test().then(ret => {
console.log(ret);
});
与使用co
const test = opts => {
return co(function*() {
// do sth
yield sthError();
return 'ok';
}).catch(err => {
console.error(err);
});
};
答案 5 :(得分:1)
更清洁的替代方法如下:
由于每个异步功能从技术上来说都是一个承诺
您可以在通过await调用函数时将捕获添加到函数中
async function a(){
let error;
// log the error on the parent
await b().catch((err)=>console.log('b.failed'))
// change an error variable
await c().catch((err)=>{error=true; console.log(err)})
// return whatever you want
return error ? d() : null;
}
a().catch(()=>console.log('main program failed'))
无需尝试捕获,因为已处理所有承诺错误,并且您没有代码错误,因此可以在父级中忽略它!
让我们说您正在使用mongodb,如果出现错误,您可能更喜欢在调用它的函数中处理它,而不是制作包装器或使用try catch。
答案 6 :(得分:0)
catch
是危险的。整个堆栈中抛出的任何错误都会被捕获,而不仅仅是这个承诺中的错误(可能不是您想要的)。
promise的第二个参数已经是拒绝/失败回调。改用它会更好,更安全。
这是我为处理此问题而写的一种打字稿类型安全的单行代码:
function wait<R, E>(promise: Promise<R>): [R | null, E | null] {
return (promise.then((data: R) => [data, null], (err: E) => [null, err]) as any) as [R, E];
}
// Usage
const [currUser, currUserError] = await wait<GetCurrentUser_user, GetCurrentUser_errors>(
apiClient.getCurrentUser()
);