이번 포스팅에서는 복수 개의 비동기 함수를 실행하고, 그 중 발생하는 오류에 대해서 예외처리하는 방법을 다룹니다.
이 글은 2019년도 진행된 FEConf Korea의 유인동님 강의 내용을 기반으로 하고 있습니다. (링크)
(주제에 대한 내용 뿐만 아니라 Promise, Generator 등 예외처리에 대한 지향점도 포괄하고 있으니 영상도 보시면 좋습니다.)
1. 예제 내용 설명
1) n개의 비동기 작업을 수행할 자료가 있음.
2) 순차적으로 수행되어야 하며, 오류가 발생한 경우 이 후 순번의 비동기 처리는 수행되지 않아야 함.
2. 비동기 함수 예제 (Timeout) - 사용자 정의 함수
1) 비동기로 수행되는 'setTimeout' 함수를 통해 2초 동안 작업을 수행한다고 가정하는 함수 정의.
2) 2초 동작 수행 후, 전달받은 자료의 SEQ값과 만기일(EXPRIE_DT)에 대한 문자열을 반환.
1 2 3 4 5 6 7 8 9 10 11 12 13 | const UserAsyncFunc = function(task){ return new Promise((resolve, reject)=>{ let expireDt = new Intl.DateTimeFormat("ko").format(new Date(task.EXPIRE_DT)); /* Aync Function ... */ setTimeout(()=> { resolve(`Finish Work ... ${task.SEQ} : ${expireDt}`); }, 2000); }); } |
3. 비동기 함수를 처리할 실행자 함수 정의
[# 1안]
단순 반복을 통해 비동기 사용자함수 (UserAsyncFunc)를 수행. await을 통해 지연 처리.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /** * n개 작업을 수행 * * @param {*} jobs */ const ExcuteAsyncJobs = async function(jobs) { /* 1안 */ for(const task of jobs){ await UserAsyncFunc(task) .then(msg => console.log(msg)) } } |
[# 2안]
작업목록 단위(jobs)로 반복했던 '1안'과 달리, 작업수행 단위(Worker)로 반복. (Generator 함수)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /** * n개 작업을 수행 * * @param {*} jobs */ const ExcuteAsyncJobs = async function(jobs) { /* 2안 */ function* Worker(executor, iter){ /* 데이터 셋 개수 만큼 반복 */ for(const i of iter){ // yield i instanceof Promise ? i.then(executor) : executor(i); yield executor(i); } } for await (const result of Worker(UserAsyncFunc, jobs)){ console.log(result); } } |
1,2안 모두 같은 결과를 만들어 내는 반복문이지만, 예제와 같이 단순한 코드가 아닐 경우엔 2안이 가시성이 좋아보입니다.
4. 실행 결과
1 2 3 4 5 6 7 8 9 10 | const jobs = [ { SEQ: '001', PRODUCT: 'Apple', PRICE: '1000', EXPIRE_DT: '2024-11-13'}, { SEQ: '002', PRODUCT: 'Banan', PRICE: '2000', EXPIRE_DT: '2024-07-09'}, { SEQ: '003', PRODUCT: 'Tomato', PRICE: '2500', EXPIRE_DT: '2023-06-08'}, { SEQ: '004', PRODUCT: 'Melon', PRICE: '4500', EXPIRE_DT: '2024-09-11'} ] ExcuteAsyncJobs(jobs) .then(msg => console.log('success')) .catch(error => console.log(error)); |
5. 예외 처리
2번 째 행의 자료에 대한 만기일(EXPIRE_DT)를 날짜형이 아닌 값('apple')로 지정하여,
앞서 정의한 사용자 함수 (UserAsyncFunc)의 날짜형 변환(new Intl.DateTimeFormat)의 오류 발생을 유도합니다.
실행자 함수(ExcuteAsyncJobs)의 catch절을 제외한 내용입니다.
[예제]
1 2 3 4 5 6 7 8 9 | const jobs_err = [ { SEQ: '001', PRODUCT: 'Apple', PRICE: '1000', EXPIRE_DT: '2024-11-13'}, { SEQ: '002', PRODUCT: 'Banan', PRICE: '2000', EXPIRE_DT: 'apple'}, // Error Case { SEQ: '003', PRODUCT: 'Tomato', PRICE: '2500', EXPIRE_DT: '2023-06-08'}, { SEQ: '004', PRODUCT: 'Melon', PRICE: '4500', EXPIRE_DT: '2024-09-11'} ] ExcuteAsyncJobs(jobs_err) .then(msg => console.log('success')); |
[결과]
6. 결론
소개글의 영상은 보신 분은 아시겠지만, 주안점은 사용자 함수(UserAsyncFunc)의 예외처리를 실행자 함수(ExcuteAsyncJobs)에서 하지 않고 실행자를 호출하는 곳에서 처리한다는 것입니다.
ExcuteAsyncJobs 함수에서 'try..catch' 문을 통해 예외처리 할 수 있으나, 사용처에서 명확한 오류를 확인하기 위해서는 오류를 방치하는 것에 의의를 둔다고 할 수 있겠습니다.
7. 전체 코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | export default async () => { const jobs = [ { SEQ: '001', PRODUCT: 'Apple', PRICE: '1000', EXPIRE_DT: '2024-11-13'}, { SEQ: '002', PRODUCT: 'Banan', PRICE: '2000', EXPIRE_DT: '2024-07-09'}, { SEQ: '003', PRODUCT: 'Tomato', PRICE: '2500', EXPIRE_DT: '2023-06-08'}, { SEQ: '004', PRODUCT: 'Melon', PRICE: '4500', EXPIRE_DT: '2024-09-11'} ] const jobs_err = [ { SEQ: '001', PRODUCT: 'Apple', PRICE: '1000', EXPIRE_DT: '2024-11-13'}, { SEQ: '002', PRODUCT: 'Banan', PRICE: '2000', EXPIRE_DT: 'apple'}, // Error Case { SEQ: '003', PRODUCT: 'Tomato', PRICE: '2500', EXPIRE_DT: '2023-06-08'}, { SEQ: '004', PRODUCT: 'Melon', PRICE: '4500', EXPIRE_DT: '2024-09-11'} ] ExcuteAsyncJobs(jobs_err) .then(msg => console.log('success')) .catch(error => console.log(error)); } const UserAsyncFunc = function(task){ return new Promise((resolve, reject)=>{ let expireDt = new Intl.DateTimeFormat("ko").format(new Date(task.EXPIRE_DT)); /* Aync Function ... */ setTimeout(()=> { resolve(`Finish Work ... ${task.SEQ} : ${expireDt}`); }, 2000); }); } /** * n개 작업을 수행 * * @param {*} jobs */ const ExcuteAsyncJobs = async function(jobs) { /* 1안 */ // for(const task of jobs){ // await UserAsyncFunc(task) // .then(msg => console.log(msg)) // } /* 2안 */ function* Worker(executor, iter){ /* 데이터 셋 개수 만큼 반복 */ for(const i of iter){ // yield i instanceof Promise ? i.then(executor) : executor(i); yield executor(i); } } for await (const result of Worker(UserAsyncFunc, jobs)){ console.log(result); } } |
0 댓글