안녕하세요. 이번 시간에는 제이쿼리의 프로미스 기능과 deferred 객체에 대해 알아보겠습니다. 지난 AJAX 시간에도 살짝 다뤘는데요. 프로미스를 사용하면 비동기 프로그래밍을 할 때 코드를 간결하게 관리할 수 있습니다.
제이쿼리 프로미스는 기본적으로 공식 구현을 따르지 않아서 문제가 있습니다. 에러 처리와 프로미스 실행 순서에 관한 문제입니다. 하지만 복잡한 프로미스를 구현하지 않는 이상은 그 문제가 발생하지 않기 때문에 그냥 사용하셔도 됩니다. (제이쿼리 3.0에서는 그 문제가 해결되었습니다)
제이쿼리는 프로미스를 사용할 수 있게 Deferred라는 객체를 제공합니다. 이 객체를 사용하면 일반 코드도 프로미스처럼 사용할 수 있습니다.
일단 제이쿼리 프로미스의 기본 구조는 프로미스 객체가 완료되었을 때 done 메소드가 호출되고 실패했을 때는 fail, 완료되었건 실패했건 행동이 끝났으면 always가 호출됩니다. 콜백 기반 함수가 다음과 같이 있다고 가정해봅시다.
var longAndComplicatedFunction = function(cb) {
console.log('시작');
try {
// 로직
cb(null, '성공');
} catch (err) {
cb(err);
} finally {
console.log('완료');
}
};
longAndComplicatedFunction(function (err, result) {
if (err) {
return console.error(err);
}
console.log(result);
});
이를 프로미스 기반으로 수정해봅시다..
$.Deferred
var longAndComplicatedFunction = function() {
var deferred = $.Deferred();
console.log('시작');
try {
// 로직
deferred.resolve('성공');
} catch (err) {
deferred.reject(err);
}
return deferred.promise();
};
longAndComplicatedFunction().done(function(message) {
console.log(message);
}).fail(function(error) {
console.error(error);
}).always(function() {
console.log('완료!');
});
이렇게 $.Deferred()
로 deferred 객체를 만들고, 성공했을 때에는 resolve, 실패했을 때에는 reject 메소드를 호출하면 resolve는 done으로, reject는 fail로 연결됩니다. longAndComplicatedFunction
함수에서 deferred.promise()
를 return하는 것을 잊지 마세요!
참고로 done이나 fail로 구분하지 않고 한 번에 처리하려면 then 메소드가 있습니다. 첫 번째 인자는 성공 시 콜백이고, 두 번째 인자는 실패 시 콜백입니다. then도 연달아 쓸 수 있습니다.
longAndComplicatedFunction().then(function(message) {
console.log(message);
}, function(error) {
console.error(error);
}).then(function() {
console.log('완료!');
});
여기까지는 흔한 제이쿼리 프로미스 방식이고 ES2015의 프로미스와도 상당히 유사합니다. 제이쿼리의 장점은 추가 메소드를 제공하는 겁니다.
$.when
$.when은 여러 개의 비동기 프로미스 함수를 동시에 처리할 수 있게 해줍니다. Promise.all
과 비슷합니다.
$.when(longAndComplicatedFunction(), longerAndMoreComplicatedFunction()).done(function(result1, result2) {
console.log(result1, result2);
});
$.when 안에 여러 개의 프로미스 함수를 넣어줍니다. 함수들이 모두 종료되었을 때 연결해둔 done 메소드의 콜백이 실행됩니다. longAndComplicatedFunction의 결과는 result1으로, longerAndMoreComplicatedFunction의 결과는 result2로 연결됩니다.
여러 개의 프로미스를 동시에 처리할 수 있기 때문에 편리합니다. 특히 선행 조건으로 비동기 함수 여러개가 필요한 경우 $.when을 쓰면 효과적으로 코딩을 할 수 있습니다. done 메소드에서 비동기 함수의 결과들을 한 번에 받을 수 있으니까요.
이상으로 제이쿼리 강좌를 마치겠습니다. 혹시나 제이쿼리에 변경 사항이나 추가 사항이 있다면 그 때 포스팅하겠습니다!
얘기한 대로라면 위 예제에서 로직이 정상적으로 동작했다고 가정할때 출력순서가
1. '다음 행동'
2. '성공'
3. '완료'
이렇게 나와야 '비동기' 라고 할 수 있죠
그런데 실제로는
1. '성공'
2. '완료'
3. '다음 행동'
순으로 출력됩니다
즉 deferred 가 비동기로 동작하는것이 아닌
'약 50초가 걸리는 매우 복잡한 코드' 를 애초에 비동기로 작성했을때에
비로소 말씀하신 것처럼 비동기로 동작하겠죠
결과적으로 deferred, promise 가 비동기를 구현해 주는것이 아닙니다
애초에 약 50초 가량 걸리는 복잡한 코드를 비동기로 작성을 해야 하므로
그 경우에는 deferred 를 사용하지 않았을 때에도 동일한 결과를 가져 올 수 있습니다.
즉 deferred와 promise 의 사용의 이유는 '복잡한 비동기 구현'에 있는것이 아닌
로직이 정상적으로 잘 처리되었는지 그에대한 콜백을 좀 더 심플하고 논리적으로 구현하는데에
있습니다.
이거 비동기 구현으로 잘못알고 있는개발자들 많은거 같더라구요