노드에서 오류 처리(비동기)
비동기 시나리오에서 오류 처리
In the previous article we looked at error handling in sync scenarios, where errors are handled with try/catch
blocks when an error is throw using the throw
keyword. Asynchronous syntax and patterns are focussed on callbacks, Promise abstractions and the async/await
syntax.
There are three ways to handle errors in async scenarios (not mutually inclusive):
- Rejection
- Try/Catch
- Propagation
배제
So, when an error occurs in a synchronous function it's an exception, but when an error occurs in a Promise
its an asynchronous error or a promise rejection. Basically, exceptions are synchronous errors and rejections are asynchronous errors.
Let's go back to our divideByTwo()
function and convert it to return a promise:
function divideByTwo(amount) {
return new Promise((resolve, reject) => {
if (typeof amount !== 'number') {
reject(new TypeError('amount must be a number'));
return;
}
if (amount <= 0) {
reject(new RangeError('amount must be greater than zero'));
return;
}
if (amount % 2) {
reject(new OddError('amount'));
return;
}
resolve(amount / 2);
});
}
divideByTwo(3);
The promise is created using the Promise
constructor. The function passed to the Promise
is called tether function , it takes two arguments resolve
and reject
. When the operation is successfully, resolve
is called, and in case of an error reject
is called. The error is passed into reject
for each error case so that the promise will reject on invalid input.
When running the above code the output will be:
(node:44616) UnhandledPromiseRejectionWarning: OddError [ERR_MUST_BE_EVEN]: amount must be even
# ... stack trace
Promise
must use the catch
method to catch rejections. Read more about Promises in the article Understanding Promises in Node.js .핸들러를 사용하도록
divideByTwo
함수를 수정해 보겠습니다.divideByTwo(3)
.then(result => {
console.log('result', result);
})
.catch(err => {
if (err.code === 'ERR_AMOUNT_MUST_BE_NUMBER') {
console.error('wrong type');
} else if (err.code === 'ERRO_AMOUNT_MUST_EXCEED_ZERO') {
console.error('out of range');
} else if (err.code === 'ERR_MUST_BE_EVEN') {
console.error('cannot be odd');
} else {
console.error('Unknown error', err);
}
});
기능은 이제 이전 기사의 synchronous non-promise based code 과 동일합니다.
throw
프라미스 핸들러 내부에 나타나면 오류가 아니라 거부가 됩니다. then
및 catch
핸들러는 핸들러 내에서 throw
의 결과로 거부하는 새 약속을 반환합니다.비동기 시도/캐치
The async/await
syntax supports try/catch
of rejections, which means that try/catch
can be used on asynchronous promise-based APIs instead of the then
and catch
handlers.
Let's convert the example code to use the try/catch
pattern:
async function run() {
try {
const result = await divideByTwo(1);
console.log('result', result);
} catch (err) {
if (err.code === 'ERR_AMOUNT_MUST_BE_NUMBER') {
console.error('wrong type');
} else if (err.code === 'ERR_AMOUNT_MUST_EXCEED_ZERO') {
console.error('out of range');
} else if (err.code === 'ERR_MUST_BE_EVEN') {
console.error('cannot be odd');
} else {
console.error('Unknown error', err);
}
}
}
run();
The only difference between the synchronous handling is the wrapping in an async function and calling divideByTwo()
with await
, so that the async function can handle the promise automatically.
Using an async
function with try/catch
around an awaited promise is syntactic sugar. The catch
block is basically the same as the catch
handler. An async function always returns a promise that resolves unless a rejection occurs. This also would mean we can convert the divideByTwo
function from returning a promise to simply throw again. Essentially the code is the synchronous version with the async
keyword.
async function divideByTwo(amount) {
if (typeof amount !== 'number')
throw new TypeError('amount must be a number');
if (amount <= 0)
throw new RangeError('amount must be greater than zero');
if (amount % 2) throw new OddError('amount');
return amount / 2;
}
The above code has the same functionality as the synchronous version, but now we can perform other asynchronous tasks, like fetching some data or writing a file.
The errors in all of these examples are developer errors. In an asynchronous context operation errors are more likely to encounter. For example, a POST request fails for some reason, and the data couldn't have been written to the database. The pattern for handling operational errors is the same. We can await
an async operation and catch any errors and handle accordingly (send request again, return error message, do something else, etc.).
번식
Another way of handling errors is propagation. Error propagation is where, instead of handling the error where it occurs, the caller is responsible for error handling. When using async/await
functions, and we want to propagate an error we simply rethrow it.
Let's refactor the function to propagate unknown errors:
class OddError extends Error {
constructor(varName = '') {
super(varName + ' must be even');
this.code = 'ERR_MUST_BE_EVEN';
}
get name() {
return 'OddError [' + this.code + ']';
}
}
function codify(err, code) {
err.code = code;
return err;
}
async function divideByTwo(amount) {
if (typeof amount !== 'number')
throw codify(
new TypeError('amount must be a number'),
'ERR_AMOUNT_MUST_BE_NUMBER',
);
if (amount <= 0)
throw codify(
new RangeError('amount must be greater than zero'),
'ERR_AMOUNT_MUST_EXCEED_ZERO',
);
if (amount % 2) throw new OddError('amount');
// uncomment next line to see error propagation
// throw Error('propagate - some other error');;
return amount / 2;
}
async function run() {
try {
const result = await divideByTwo(4);
console.log('result', result);
} catch (err) {
if (err.code === 'ERR_AMOUNT_MUST_BE_NUMBER') {
throw Error('wrong type');
} else if (err.code === 'ERRO_AMOUNT_MUST_EXCEED_ZERO') {
throw Error('out of range');
} else if (err.code === 'ERR_MUST_BE_EVEN') {
throw Error('cannot be odd');
} else {
throw err;
}
}
}
run().catch(err => {
console.error('Error caught', err);
});
Unknown errors are propagated from the divideByTwo()
function, to the catch
block and then up to the run
function with the catch
handler. Try to run the code after uncommenting the throw Error('some other error');
in the divideByTwo()
function to unconditionally throw an error. The output will be something like this: Error caught Error: propagate - some other error
.
If and when an error is propagated depends highly on the context. A reason to propagate an error might be when error handling strategies have failed at a certain level. An example would a failed network request, which was retried for several times before propagating.
In general, try to propagate errors for handling at the highest level possible. This would be the main file in a module, and in an application the entry point file.
TL;DR
- Exceptions are synchronous errors and rejections are asynchronous errors.
- A promise rejection has to be handled. The
catch
handler handles the promise rejection. - There are three ways to handle errors in async scenarios: Rejection, Try/Catch and Propagation
- The
async/await
syntax supportstry/catch
of rejections. -
try/catch
can be used on asynchronous promise-based APIs instead of thethen
andcatch
handlers. - Error propagation is where, instead of handling the error where it occurs, the caller is responsible for error handling.
- Error propagation depends on the context. When propagated it should be to the highest level possible.
Thanks for reading and if you have any questions , use the comment function or send me a message .
If you want to know more about Node Node Tutorials .참조(큰 감사):
JSNAD , MDN Errors , MDN throw , Node.js Error Codes , Joyent
Reference
이 문제에 관하여(노드에서 오류 처리(비동기)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/mariokandut/handling-errors-in-node-asynchronous-1kb4텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)