프로미스화

프로미스화

콜백을 받은 함수를 프라미스를 반환하는 함수로 바꾸는 것을 ‘프라미스화(promisification)’ 이라고 한다.

콜백 예시

1
2
3
4
5
6
7
8
9
10
11
12
function loadScript(src, callback) {
let script = document.createElement('script');
script.src = src;

script.onload = () => callback(null, script);
script.onerror = () => callback(new Error(`${src}를 불러오는 도중에 에러가 발생함`));

document.head.append(script);
}

// usage:
// loadScript('path/script.js', (err, script) => {...})

이제 이 콜백을 프로미스화 할 것임. 해당 함수는 콜백을 인수로 받지 않고 src만 인수로 받도록 함.

1
2
3
4
5
6
7
8
9
10
11
let loadScriptPromise = function(src) {
return new Promise((resolve, reject) => {
loadScript(src, (err, script) => {
if (err) reject(err);
else resolve(script);
})
})
}

// usage:
// loadScriptPromise('path/script.js').then(...)

loadScriptPromiseloadScript에 모든 일을 위임한다.

그런데 실무에서는 여러 함수를 프라미스화 해야 할 것이다. 래퍼 함수를 만들 도록 한다. 프라미스화를 적용할 함수 f를 받고 래퍼 함수를 반환하는 함수 promisefy(f)를 만든다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function promisify(f) {
return function (...args) { // 래퍼 함수를 반환함
return new Promise((resolve, reject) => {
function callback(err, result) { // f에 사용할 커스텀 콜백
if (err) {
reject(err);
} else {
resolve(result);
}
}

args.push(callback); // 위에서 만든 커스텀 콜백을 함수 f의 인수 끝에 추가함.

f.call(this, ...args); // 기존 함수를 호출
});
};
};

// 사용법:
let loadScriptPromise = promisify(loadScript);
loadScriptPromise(...).then(...);

함수 f가 두 개를 초과하는 인수를 가진 콜백, callback(err, res1, res2, ...) 을 받는 경우?

promisify(f, true) 형태로 호출하면, 프로미스 결과는 콜백의 성공 케이스 (result)를 담은 배열 [res1, res2, ...] 이 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 콜백의 성공 결과를 담은 배열을 얻게 해주는 promisify(f, true)
function promisify(f, manyArgs = false) {
return function (...args) {
return new Promise((resolve, reject) => {
function callback(err, ...results) { // f에 사용할 커스텀 콜백
if (err) {
reject(err);
} else {
// manyArgs가 구체적으로 명시되었다면, 콜백의 성공 케이스와 함께 이행 상태가 됩니다.
resolve(manyArgs ? results : results[0]);
}
}

args.push(callback);

f.call(this, ...args);
});
};
};

// 사용법:
f = promisify(f, true);
f(...).then(arrayOfResults => ..., err => ...)

es6-promisify 같은 프라미스화를 도화주는 모듈도 있다.
또한, node.js에선 내장 함수 util.promisify를 사용해 프라미스화를 할 수 있다.