티스토리 뷰

자바스크립트 애플리케이션은 단일 스레드에서 동작합니다. 즉 자바스크립트는 한 번에 한 가지 일만 할 수 있습니다. 

자바스크립트는 비동기적 프로그래밍에 대처하기 위해 처음에는 콜백을, 다음에는 프라미스 마지막은 제너레이터를 추가시켰습니다.

 

각 방법에 대해 들어가기 전 이해가 쉽도록 비유적인 표현을 책에서 명시하고 있습니다.

 

예약하지 않고 분주한 음식점에 방문한 경우, 콜백이란 음식점은 줄을 서서 기다리지 않도록 당신의 전화번호를 받아서 자리가 나면 전화해줍니다. 프라미스라는 음식점의 경우는 자리가 났을 때 진동하는 호출기를 당신에게 넘겨줍니다. 실행되어야 하는 타이밍을 알려주는 수단을 콜백의 경우 당신이 음식점에게, 프라미스의 경우 음식점이 당신에게 준다는 비유를 명시하면 좀 더 쉽게 이해할 수 있습니다.

 

콜백

콜백은 간단히 말해 나중에 호출할 함수입니다. 콜백함수 자체에는 특별할 것이 없고 일반적인 JS의 함수일 뿐입니다. 콜백함수는 일반적으로 다른 함수에 넘기거나 객체의 프로퍼티로 사용합니다.

 

/스코프와 비동기적 실행

함수를 호출하면 클로저가 만들어지고 매개변수를 포함해 함수 안에서 만든 변수는 무언가가 자신에 접근할 수 있는 한 계속 존재합니다.

function countdown() {
	let i;
    console.log("Countdown");
    for(i=5;i>=0;i--){
    	setTimeout(function() {
        	console.log(i === 0 ? "Go" : i);
            }, (5-i)*1000);
    }
}

이 예제의 결과는 5초부터 카운트다운 하기를 바라던 예상과는 달리 -1만 여섯 번 반복됩니다. 변수가 for 루프 밖에서 선언됬기 때문에 for루프가 실행을 마치고 i의 값이 -1이 된 후에야 콜백이 시작됩니다. 이 문제는 간단히 클로저 내부에서 변수를 선언함으로서 해결할 수 있습니다. 이 예제를 통해 비동기적 코드를 사용할 때 콜백이 어느 스코프에 선언됬는지 확인해볼 필요에 대해 알 수 있습니다.

 

 

/오류 우선 콜백

콜백을 사용하면 예외 처리가 어려워지기 때문에(차후 설명) 이에 대한 해결방법으로 콜백의 첫 번째 매개변수에 에러 객체를 쓰는(에러가 null이나 undefined이면 에러가 없는 것) 오류 우선 콜백이라는 패턴이 나타났습니다. 

const fs = require('rs');

const fname = 'may_or_may_not_exist.txt';
fs.readFile(fname, function(err, data) {
	if(err) return console.error(`error reading file ${fname} ${err.message}`);
    }
}

에러가 있다면 빠져나오기 위해 로그를 찍고 리턴합니다.

 

/콜백 헬

콜백을 사용해 비동기적으로 실행할 수 있지만 한 번에 여러가지를 기다려야 한다면 콜백을 관리하기 어려워집니다. 예를 들어 A파일의 콘텐츠를 읽으면 B콘텐츠를 읽으면 C콘텐츠를.... 이렇게 반복적인 비동기작업이 필요하다면 끝없는 중괄호에 둘러쌓이게 될 것입니다.

 

다른 문제로는 직관적이지 못한 예외처리입니다.

const fs = require('fs');
function readSketchFile() {
	try {
    	fs.readFile('does_not_exist.txt', function(err, data){
        	if(err) throw err;
        });
	} catch(err) {
    	console.log("error");
    }
}
readSketchFile();

try문에서 에러를 throw해줘서 catch문에서 정상적인 예외처리가 될 것 같지만 그렇지 않습니다. try...catch 블록은 같은 함수 안에서만 동작합니다. fs.readFile이 콜백으로 호출하는 익명함수에서 예외가 일어났기 때문에 readSketchFile 함수안에 있는 try..catch문은 예외처리를 할 수 없습니다.

 

프라미스

프라미스는 위 언급한 문제점을 해결하기 위해 만들어졌습니다. 프라미스가 콜백을 대체하는 것은 아닙니다. 프라미스에서도 콜백을 사용합니다. 다만 콜백을 예측가능한 패턴으로 사용할 수 있게 하며 콜백만 사용했을시의 문제점을 상당수 해결합니다.

 

프라미스는 프라미스 기반 비동기적 함수를 호출하면 그 함수는 Promis 인스턴스를 반환합니다. 프라미스는 성공하거나(fullfilled) 실패하거나(rejected) 두 가지 뿐입니다. 프라미스가 성공하거나 실패하면 그 프라미스를 결정됐다"(settled) 합니다.

프라미스는 객체임으로 어디든 전달할 수 있습니다. 비동기적 처리를 다른 함수에서 처리하고 싶다면 프라미스를 넘기기만 하면 됩니다.

function countdown(seconds){
	return new Promise(function(resolve, reject) {
    	for(let i = seconds; i >= 0; i--){
        	setTimeout(function(){
            	if(i>0) console.log(i + '...');
                else resolve(console.log("GO!"));
            }, (seconds-i)*1000);
        }
     });
}

 

/프라미스 사용

위 예제를 통해 프라미스를 사용하는 방법을 알아봅시다.

countdown(5).then(
	function(){
    	console.log("countdown completed succesfully");
    },
    function(err){
    	console.log("countdown experienced an error: " + err.message);
    }
);

위 예제는 반환된 프라미스를 변수에 할당하지 않고 then 핸들러를 바로 호출했습니다. then 핸들러는 성공 콜백과 에러 콜백을 받습니다.

프라미스는 catch 핸들러도 지원하기 때문에 핸들러를 둘로 나눠서 써도 됩니다.

...
p.catch(function(err){
	console.log("countdown experienced an error: " + err.message);
});

프라미스는 비동기적 작업이 성공 또는 실패하도록 확정하는 메커니즘을 제공하지만 진행상황을 알려주진 않습니다. 진행사항을 알기 위해서는 이벤트에 대해 알아볼 필요가 있습니다. 예제가 백엔드 중점으로 서술되어 있기 때문에 이부분은 생략하겠습니다.

 

/프라미스 체인

프라미스에는 체인으로 연결할 수 있다는 장점이 있습니다. 즉, 프라미스가 완료되면 다른 프라미스를 반환하는 함수를 즉시 호출할 수 있습니다.

'Running JS 요약' 카테고리의 다른 글

JS - 함수와 추상적 사고  (0) 2021.01.21
JS - 이터레이터와 제너레이터  (0) 2021.01.20
JS - 맵과 셋 / 예외와 에러 처리  (0) 2021.01.18
JS - 객체와 객체지향 프로그래밍  (0) 2021.01.11
JS - 배열  (0) 2021.01.06
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
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
글 보관함