평소에 프론트화면에서 fetch나, jQuery.ajax를 많이 사용하는지라 세세한 원리에 대해서 생각해본적은 없습니다. 이 포스트에서 작동 원리나 fetch가 반환하는 promise 객체에 대해서 알아보려고 합니다.
javascript는 Single Thread에서 작동합니다. 따라서 Mult Thread의 개념에서 여러개의 작업을 동시에 실행 시키는 것이 불가능 합니다. 단순히 작성된 순서대로 진행하죠. 이 방식을 동기 방식이라고 합니다. 비동기는 반대입니다. 여러 개의 작업을 동시에 실행시키는 것이 가능합니다.

동기방식은 위에서 설명하듯 console의 결과가 A,B,C 순서대로 발생합니다.
function taskA(a, b, cb) {
const res = a + b;
cb(res);
}
function taskB(a, cb) {
const res = a * 2;
cb(res);
}
function taskC(a, cb) {
const res = a * -1;
cb(res);
}
taskA(3, 4, (res) => {console.log("A RESULT : ", res)});
taskB(7, (res) => {console.log("B RESULT : ", res)});
taskC(1, (res) => {console.log("C RESULT : ", res)});
비동기의 대한 예제입니다. setTimeout은 내장함수로서 자체적으로 비동기의 특성을 가지고 있습니다. 따라서 예제에 사용하기 적절합니다. 코드의 실행결과는 delay(ms) 순서대로 C,B,A 가 됩니다.
function taskA(a, b, cb) {
setTimeout(() => {
const res = a + b;
cb(res);
}, 3000);
}
function taskB(a, cb) {
setTimeout(() => {
const res = a * 2;
cb(res);
}, 2000);
}
function taskC(a, cb) {
setTimeout(() => {
const res = a * -1;
cb(res);
}, 1000);
}
taskA(3, 4, (res) => {console.log("A RESULT : ", res)});
taskB(7, (res) => {console.log("B RESULT : ", res)});
taskC(1, (res) => {console.log("C RESULT : ", res)});
위에 비동기 프로세스를 콜백 변수를 이용하여 순자적으로 진행되게 하는 것도 가능 합니다.
taskA(3, 4, (res_A) => {
console.log("A RESULT : ", res_A)
taskB(res_A, (res_B) => {
console.log("B RESULT : ", res_B)
taskC(res_B, (res_C) => {
console.log("C RESULT : ", res_C)
});
});
});
위에 예제를 실행하면 순서는 다시 A,B,C 순서대로 작동합니다. 비동기 프로세스지만, 콜백에서 결과가 전달이 되어야 다음 기능이 실행이 되기 때문이죠. 이처럼 동기프로세스와 같이, 결과가 나와야 다음으로 진행하는 것이 가능합니다. 위 방법은 예시에서는 3개로 했지만, 함수의 갯수가 많아지면 보기 불편해질 수도 있습니다. 그래서 나온게 Promise 입니다.
Promise는 성공을 담당하는 resolve와 실패를 담당하는reject로 구분되어있습니다.
둘다 콜백의 개념으로 사용하기 때문에 다음과 같은 코드로 설명할 수 있겠습니다.
function isPositive(number, resolve, reject) {
setTimeout(() => {
if(typeof number === "number") {
resolve(number >= 0 ? "양수" : "음수");
} else {
reject("숫자가 아니에요");
}
})
};
isPositive(
10,
(res) => {console.log("sucess : ", res)},
(err) => {console.log("error : ", err)}
);
그럼 이제 이것을 본격적으로 Promise 객체로 바꿔보겠습니다.
new Promise안에 해당 함수를 넣어주고 return 해주면 됩니다.
사용하실때는 then(resolve), catch(reject)로 사용이 가능합니다.
function isPositive(number) {
const result = new Promise((resolve, reject) => {
setTimeout(() => {
if(typeof number === "number") {
resolve(number >= 0 ? "양수" : "음수");
} else {
reject("숫자가 아니에요");
}
},2000)
});
return result;
};
isPositive('199')
.then((res)=> console.log(res))
.catch((res)=> console.log(res))
자 이제 다시 위에서 사용하였던 taskA, B, C를 정리 해봅시다.
함수를 Promise로 return 해주고 then을 이용하여 chain 형식으로 사용하면 됩니다.
이해하시는건 어렵지 않으실겁니다. 결국 return 받는 객체(B,C)가 Promise이므로 .then()을 그대로 이어받아서 사용할 수 있게 되는 겁니다. .catch()문은 사용을 안하므로 안만들었지만, 필요하면 작성하시면 됩니다.
function taskA(a, b) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const res = a + b;
resolve(res);
}, 3000);
});
}
function taskB(a) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const res = a * 2;
resolve(res);
}, 2000);
});
}
function taskC(a) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const res = a * -1;
resolve(res);
}, 1000);
});
}
taskA(3,4)
.then((res_A) => {console.log(res_A); return taskB(res_A);})
.then((res_B) => {console.log(res_B); return taskC(res_B);})
.then((res_C) => {console.log(res_C);})
async 는 함수앞에 붙이는 키워드이며 함수를 비동기, Promise()객체화 시킵니다.
async function hellAsync() {
return "hello anync";
}
hellAsync().then((res) => console.log(res));
이처럼 async는 비동기가 가능하게 만듭니다. 반대로 이러한 함수에 동기처럼 흘러가게 순차적으로 만드는 키워드도 있습니다. await 입니다. await는 anync(Promise)로 선언된 함수에만 사용이 가능 합니다.
function delay(ms) {
console.log("start delay");
return new Promise((resolve) => {setTimeout(resolve, ms)});
}
async function hellAsync(ms) {
await delay(ms); // await 지우고도 해보세요.
return "hello anync delay: "+ms +"ms" ;
}
hellAsync(5000).then((res) => console.log(res));
마지막으로는 우리가 익히 알고 있는 fetch사용법입니다. 이 부분은 이미 알고 있다는 가정에서 별다른 설명은 없이 넘어가겠습니다.
fetch('https://jsonplaceholder.typicode.com/todos')
.then(response => response.json())
.then(json => console.log(json))
//ajax사용법 jQuery import해야 사용가능
$.ajax({
type : "get",
url : "https://jsonplaceholder.typicode.com/todos",
//data : params,
success : function(data){
console.log(JSON.stringify(data));
},
error : function(XMLHttpRequest, textStatus, errorThrown){
console.log("fail");
}
});
비구조 동적 import 참고 : https://yceffort.kr/2021/06/dynamic-import-esmodule