Async
node.js의 장점으로 꼽는 점 중 하나가 non-Blocking으로써 비동기적으로 작동한다는 점이다.
그렇기 때문에 node.js의 대부분의 기능들은 콜백 패턴을 이용한 비동기 처리를 사용는데, 이러한 점 때문에 실제 개발에 콜백 지옥으로 인해 난관을 겪곤 한다.
콜백 지옥이란 콜백 패턴을 이용하는 함수에, 연달아 추가 기능을 사용할 경우 콜백이 계속해서 중첩되는 상태를 말한다.
task1(a, function(err, result1){ task2(b, function(err, result2){ task3(c, function(err, result3){ task4(d, function(err, result4){ //함수 실행 }); }); }); });
위 예제는 err에 대한 처리와 각 파라미터에 대한 알고리즘을 베재한 코드이다.
4중첩이지만 실제 코드를 node.js로 코딩할땐 흔히 4중첩이상은 흔히 볼 수 있다.
이러한 콜백 지옥을 해결하기 위해서 다양한 모듈이 있다.
자바스크립트의 Promise 패턴을 이용한 방법(대부분의 모듈은 Promise를 지원한다.), 혹은 Async나 Step이라는 모듈로 흐름을 제어하는 방법이다.
여기서는 Async모듈을 사용하는 방법을 정리하고자 한다.
async : https://github.com/caolan/async
docs : http://caolan.github.io/async/
설치 : npm install async --save
async 모듈의 메소드는 크게 콜렉션, 흐름제어, 유틸 3종류로 나뉘어진다. 주로 사용하는건 콜렉션과 흐름제어이고, 이 포스팅 자체는 흐름제어가 주제이므로 이를 주로 다루겠다.
흐름 제어 메서드
async의 흐름 제어 메서드는 정말 다양한 종류가 있지만, 내가 주로 사용한 메서드는 다음과 같다.
- 순차 실행 : series, waterfall
- 병렬 실행 : parallels
순차 실행
순차 실행은 이름 그대로 메서드들을 순차적으로 실행하는 메서드이다. 여기엔 인자를 전달하지 않고 순차적으로만 실행하는 메서드인 series와,
다음 메서드에 인자를 전달할 수 있는 waterfall 메서드가 있다.
1. series
시리즈 메서드는 다음과 같은 형태를 가진다.
async.series([task1,task2,task3], function(err, results){ // series 완료 콜백 });
첫번째 파라미터는 실행할 함수들을 순서대로 배열에 넣어 삽입한다, 두번째 메서드는 이 serise가 완료되었을때 실행되는 콜백 함수를 넣는데,
이 콜백에는 실행 메서드들 중 실패하는 경우 err에 인자를 전달하며 그 뒤 테스크들의 실행을 중단하고 콜백 함수를 호출한다.
모두 성공할 경우 콜백 함수의 results에 배열에 있는 각 테스크들의 실행 결과를 배열로 전달받는다.
배열에 들어가는 메서드들은 callback을 인자로 가지는 함수 형태로 만들어야 한다.
function task1(callback){ //실패시 if(err){ callback(err); } //성공시 callback(null, '성공1'); }
성공 시에는 callback인자를 함수로 호출하는데, 첫 인자를 null로 전달하면 되고
실패 시에는 첫 null에 err를 전달하면 된다.
async.series([ function (callback) { if (err) { callback('실패1'); } callback(null, '성공1'); }, function (callback) { if (err) { callback('실패2'); } callback(null, '성공2'); }, function (callback) { if (err) { callback('실패3'); } callback(null, '성공3'); }], function(err, results) { // serise 완료 콜백 if(err){ //err시 err난 테스크에서 전달한 err값을 전달받는다 console.log(err); } //모두 성공시, 성공 값들을 전달받는다 //예) ['성공1','성공2','성공3'] console.log(reuslts); });
2. waterfall
serise와 waterfall은 둘다 순차 실행이지만
serise는 테스크들이 독립적으로 실행되어 모든 결과를 모아 배열로 콜백에 전달하는 반면에,
waterfall은 각 테스크들의 값을 다음 인자로 전달한다는 점에서 다르다.
async.waterfall([task1,task2,task3], function(err, result){ // waterfall 완료 콜백 });
메서드의 형태는 searise와 얼핏 비슷하나, task에서 다음 콜백을 호출 할 때 인자를 전달 할 수 있다.
function task1(callback){ //실패 시 if(err){ callback(err); } //성공 시 callback(null, '성공', '1'); } function task2(arg1, arg2, callback){ //실패 시 if(err){ callback(err); } //성공 시 callback(null,'성공2'); }
async.waterfall([ function (callback) { if (err) { callback('실패1'); } callback(null, '성공','1'); }, function (arg1, arg2, callback) { if (err) { callback('실패2'); } //arg1 : '성공', arg2 : 1 console.log(arg1, arg2); callback(null, '성공2'); }, function (arg1, callback) { if (err) { callback('실패3'); } //arg1 : '성공2' console.log(arg1); callback(null, '성공3'); }], function(err, result) { // waterfall 완료 콜백 if(err){ //err시 err난 테스크에서 전달한 err값을 전달받는다 console.log(err); } //모두 성공시, 마지막에 결과 값을 전달 받는다. //result : '성공3' console.log(reuslt); });
병렬 실행
parallel
parallel도 앞서 본 메서드들과 같이 task의 배열과 콜백을 인자로 받는다. 다만 순차 실행이 아닌 전달 받은 task들을 병렬, 즉 동시에 실행한다.
그리고 모든 테스크들이 전부 다 끝난 다음에서야 콜백 함수가 실행된다.
async.parallel([task1,task2,task3], function(err, results){ // parallel 완료 콜백 });
동시 실행이기 때문에 각 task들은 serise와 같이 독립적으로 실행되며, results에는 모든 함수의 결과가 저장된다.
메서드의 형태는 serise와 비슷하다.
async.parallel([ function (callback) { if (err) { callback('실패1'); } callback(null, '성공1'); }, function (callback) { if (err) { callback('실패2'); } callback(null, '성공2'); }, function (callback) { if (err) { callback('실패3'); } callback(null, '성공3'); }], function(err, results) { // parallel 완료 콜백 if(err){ //err시 err난 테스크에서 전달한 err값을 전달받는다 console.log(err); } //모두 성공시, 성공 값들을 전달받는다 //예) ['성공1','성공2','성공3'] console.log(reuslts); });
node.js를 이용한 프로젝트를 진행하면서 비동기 처리에 따른 콜백 지옥을 벗어나고자 많은 고민이 있었다.
async 모듈, 그리고 promise 만으로도 대부분의 콜백 지옥을 해소 할 수 있다고 하므로 반복해서 숙지하도록 노력해야겠다.
'Javascript > node.js' 카테고리의 다른 글
node.js 모듈 (0) | 2016.03.30 |
---|---|
node.js 시작하기 (0) | 2016.03.01 |