JavaScript 응용4 복습

Udemy - 한입크기로 잘라 먹는 리액트

Promise Async Await Resolve Rejected 메소드체이닝 then catch fulfilled pending



JavaScript 응용4


📌 Promise | 콜백지옥 탈출

✔️ 핵심 Point

  • 자바스크립트의 비동기처리를 핸들링하는 함수로부터 분리할 수 있음
  • Promise를 사용하면 더 이상 비동기처리 함수에 콜백을 줄지어 전달할 필요가 없어서 더 쉽고 빠르고 직관적으로 비동기처리를 만들수 있게 됨

📝 비동기 작업이 가질 수 있는 3가지 상태

  • Pending (대기상태) : 현재 비동기작업이 진행중이거나 시작할 수 없는 문제가 발생한 상태
  • Fulfilled (성공) : 비동기작업이 의도한대로 정상적으로 실행되는 상태
  • Rejected (실패) : 비동기작업이 모종의 이유로 실패했음을 의미하는 상태 (ex. 서버가 응답하지 않거나, 자동으로 취소되는 등)
  • 비동기작업의 경우 한번 성공하거나 실패하면 그걸로 끝난다고 보면 됨.

📝 비동기 작업 상태 변화

  • 비동기 작업이 성공했을 경우
    Resolve (해결) : Pending (대기상태) > Fulfilled (성공)
  • 비동기 작업이 실패했을 경우
    Reject (거부) : Pending (대기상태) > Rejected (실패)

📝 예제 : 2초 뒤에 전달받은 값이 음수 또는 양수인지 판단하기

function isPositive(number, resolve, reject) {
	setTimeout( () => {
    	if(type of number === 'number'){
        
        	// 성공 resolve
          	resolve(number >0 ? "양수" : "음수") // 삼항연산자
            
        } else {
        
        	// 실패 reject
            reject("주어진 값이 숫자형 값이 아닙니다.")
            
        }
    }, 2000)
}

isPositive(10, (res)=>{
		console.log("성공적으로 수행됨 :", res);
	},
	(err)=>{
    	console.log("실패하였음 :", err);
	}
); // 성공적으로 수행됨 : 양수

// 만약 10이 아니라 []을 전달하면 2초 뒤 실패하였음 : 주어진 값이 숫자형 값이 아닙니다

📝 Promise를 사용해서 비동기처리

function isPositive(number, resolve, reject) {
  setTimeout( () => {
    if(typeof number === 'number'){
      // 성공 -> resolve
      resolve( number >=0 ? "양수" : "음수" )
    } else {
      // 실패 -> reject
      reject( "주어진 값이 숫자형이 아닙니다.");
    }
  }, 2000); 
}

// number 파라미터를 똑같이 받음
// executor는 실행자라는 뜻이로 "비동기처리를 실질적으로 수행하는 함수"

function isPositiveP(number){
	const executor = (resolve, reject) => {
    	setTimeout( ()=> {
          if(typeof number === 'number'){
            // 성공 -> resolve
            console.log(number;)
            resolve( number >=0 ? "양수" : "음수" )
          } else {
            // 실패 -> reject
            reject( "주어진 값이 숫자형이 아닙니다.");
          }        	
        }, 2000);
    };
    
    // 실행시키는 방법
    // 비동기 작업을 저장할 asyncTask 상수를 만들고
    // 새로운 Promise 객체를 생성해 생성자로
    // 비동기작업의 실질적인 수행자인 executor를 전달하는 순간 바로 수행됨
    
    const asyncTask = new Promise(excutor); 
    return asyncTask; 
    // 반환하면 isPositiveP의 값이 Promise로 바뀐 것 확인 가능
    // function isPositiveP(number: any) : Promise<any>
}

// 이 함수가 반환하는 객체를 res 상수에 저장
const res = isPositiveP(101); // 101

// Promise 객체의 비동기처리를 사용하는 방법
// then 콜백함수, catch 콜백함수
// then 메서드 - 작업 성공 resolve
// catch 메서드 - 작업 실패 reject

res
  .then((res)=>{
      console.log("작업 성공 :", res);
  )}
  .catch((err)=>{
      console.log("작업 실패 :", err);
  })

// 작업 성공 : 양수
// []을 전달하게 되면 작업 실패 : 주어진 값이 숫자형 값이 아닙니다

📝 Promise를 사용해서 콜백지옥 탈출

function taskA(a, b, cb){
	setTimeout( () => {
    	const res = a + b;
        cb(res);
    }, 3000);
}

function taskB(a, cb){
	setTimeout( () => {
    	const res = a * 2;
        cb(res);
    }, 1000);
}

function taskC(a, cb){
	setTimeout( () => {
    	const res = a * -1;
        cb(res);
    }, 2000);
}

// 콜백지옥
taskA(3, 4, (res) => {
	console.log("task A :", a_res);
    taskB(a_res, (b_res) => {
    	console.log("task B :", b_res);
        taskC(b_res, (c_res) => {
        console.log("task C :", c_res);
        });
    });
});

// 3초 뒤 task A : 7
// 1초 뒤 task B : 14
// 2초 뒤 task C : -14

📝 존재하는 비동기작업인 taskA, taskB, taskC를 Promise를 사용해서 비동기처리하는 함수로 바꾸기

// Promise를 사용하므로 cb는 필요가 없어져서 지워줌
// 비동기처리를 executor로 넣어주기
// 어떤 함수가 Promise 객체를 반환한다는 건 그 함수는 비동기적으로 동작을 하고,
// 지금 반환한 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);
        }, 1000);   
  	});
}

function taskC(a){
	return new Promise((resolve, reject) => {
        setTimeout( () => {
            const res = a * -1;
            resolve(res);
        }, 2000);
	});
}

taskA(5, 1).then((a_res)=>{
	console.log("A RESULT :", a_res);
    taskB(a_res).then((b_res)=>{
    	console.log("B RESULT :", b_res);
        taskC(b_res).then((c_res)=>{
        	console.log("C RESULT :", c_res);
        });
    }); 
});

// A RESULT : 6
// B RESULT : 12
// C RESULT : -12

// 그런데 then 사용법 때문에 콜백지옥식으로 나온 코드가 나왔는데 이것을 바꿔보면
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);
        }, 1000);   
  	});
}

function taskC(a){
	return new Promise((resolve, reject) => {
        setTimeout( () => {
            const res = a * -1;
            resolve(res);
        }, 2000);
	});
}


// then 메서드 계속 연결 => then chaining
// taskB, C 결과값 반환해 인자로 전달

taskA(5, 1).then((a_res)=>{
	console.log("A RESULT :", a_res);
	return taskB(a_res); 
})
.then((b_res) => { 
	console.log("B RESULT :", b_res);
 	return taskC(b_res);
}
.then((c_res) => {
	console.log("C RESULT :", c_res);
});


// A RESULT : 6
// B RESULT : 12
// C RESULT : -12


// Promise를 사용하면 코드를 체이닝으로 늘여쓸 수 있음
// 콜백지옥과 달리 1) 중간에 다른 작업을 끼워넣어도 동일하게 실행가능,
// 2) 좀 더 가독성있는 코드 작성을 도와준다

const bPromiseResult = taskA(5, 1).then((a_res)=>{
	console.log("A RESULT :", a_res);
	return taskB(a_res); 
});

console.log("blahblahblahblahblah");
console.log("blahblahblahblahblah");
console.log("blahblahblahblahblah");

bPromiseResult
.then((b_res) => { 
	console.log("B RESULT :", b_res);
 	return taskC(b_res);
}
.then((c_res) => {
	console.log("C RESULT :", c_res);
});

// (3) blahblahblahblahblah
// A RESULT : 6
// B RESULT : 12
// C RESULT : -12

📌 async / await | 직관적인 비동기 처리

✔️ 핵심 Point

  • Promise를 더 가독성있고 더 쉽게 이용하도록 도와주는 async와 await

📝 async

function hello() {
	return "hello";
}

async function helloAsync(){
	return "hello Async"; 
}

// function helloAsync : Promise<string>

console.log(hello()); // hello
console.log(helloAsynce()); // Promise {<pending>}

// 함수 앞에 async를 붙여주게 되면 Promise 객체로 출력
// (Promise를 리턴하고 있다)

// helloAsynce가 Promise라는 것은 then을 써서 호출도 가능하다는 이야기가 되는데,

helloAsync().then((res)=>{
	console.log(res);
});

// hello Async 
// async를 붙이고 리턴만 해도 Promise를 리턴하면서 
// resolve를 return "hello Async" 값이 res에 들어갈 수 있게 되는 것임
// async를 붙임으로써 그 함수가 Promise를 반환하도록 하게 해줌

📝 await

// 비동기함수는 Promise를 리턴한다

function delay (ms) {
	return new Promise((resolve)=>{
    	// resolve를 콜백함수 자체로 전달 가능  
    	setTimeout(resolve, ms);
    })
}

async function helloAsync() {
	// return delay(3000).then(()=>{
    // 	return "hello Async";
    // });
	
    await delay(3000);
    return "hello Async";
}

helloAsync().then((res)=>{
	console.log(res);
});

// 3초 후 hello Async
// 📝 await 함수를 비동기 함수인 delay 앞에 붙이면 동기적으로 호출됨
// (동기적인 함수처럼 동작하게)
// 📝 await 코드라인에서는 모두 동기적으로 수행하게 된다
// 📝 await은 async가 붙은 함수 안에서만 사용 가능

📝 main

function delay (ms) {
	return new Promise((resolve)=>{ 
    	setTimeout(resolve, ms);
    })
}

async function helloAsync() {
    await delay(3000);
    return "hello Async";
}

async function main() {
  // helloAsync().then((res)=>{
  //    console.log(res);
  // });
  
  const res = await helloAsync();
  console.log(res);
}

main();

// hello Async
  • async와 await을 활용해서 Promise 반환하는 함수를 마치 동기적인 함수를 호출하여 사용한 것과 마찬가지로 사용할 수 있도록 만들어줌

📚 Promise, async-await부터는 강의 내용이 어려워서 서칭 🕵️‍

📌 참고자료

출처

비동기처리 Point : 동시에 여러 작업을 해볼 수 있다

동기적 처리 특징

  • 동시에 여러 작업을 수행할 수 없다.
  • 흐름을 예측하기 쉽다. 먼저 수행되고 나중에 수행되는 것들이 명확하다.

비동기적 처리 특징

  • 동시에 여러 작업을 수행할 수 있다.
  • 흐름을 예측하기 어렵다. (무엇이 먼저 완료될 지 보장할 수 없다.)

setTimeout

  • 시간이 좀 걸리지만 기다리기만 하면 되는 작업
  • setTimeout은 인자로 들어온 콜백 함수를 예약하기만 하고 바로 끝난다.
  • setTimeout에 의해 기다리는 동작은 본래의 코드 흐름과는 상관 없이 따로따로 독립적으로 돌아간다.
  • 이렇게 따로따로 독립적으로 돌아가는 작업을 비동기 작업이라고 한다.

콜백지옥

비동기 작업은 여러 작업을 동시에 수행할 수 있는 장점이 있지만, 의존성이 길게 이어져 있는 비동기 작업들을 처리할 때 곤혹에 치를 수 있다. 왜냐하면 비동기 작업이 시작되는 시점은 함수 호출이며, 또한 이 함수 호출 시점에 다음 작업(콜백 함수)도 넘겨줘야 하기 때문에.

Promise

  • 비동기 작업의 단위
  • new Promise(...) 로 Promise 객체를 새롭게 만들고, 생성자는 특별한 함수 하나(executor)를 인자로 받는다.
  • executor는 첫번째 인수로 resolve이며, 두번째 인수로 reject를 받는다. (국룰)
  • resolve는 executor 내에서 호출할 수 있는 또 다른 함수로, resolve를 호출하게 된다면 “이 비동기 작업이 성공했어!” 라는 뜻.
  • reject 또한 executor 내에서 호출할 수 있는 또 다른 함수로, reject 를 호출하게 된다면 “이 비동기 작업이 실패했어…” 라는 뜻.
  • 복잡해보이지만 뒤에 나오는 async-await를 설명하기 위해서 알아야 함
  • Promise 는 비동기 작업을 생성/시작하는 부분(new Promise(...))과 작업 이후의 동작 지정 부분(then, catch)을 분리함으로써 기존의 러프한 비동기 작업보다 유연한 설계를 가능토록 함.

then 메소드와 catch 메소드

  • 이 작업이 성공하거나 실패하는 순간에 대한 뒤처리 작업
  • then 메소드는 해당 Promise 가 성공했을 때의 동작을 지정 (인자로 함수를 받음)
  • catch 메소드는 해당 Promise 가 실패했을 때의 동작을 지정 (인자로 함수를 받음)
  • 메소드 체이닝 (연결가능)
  • resolve, reject 함수에 인자를 전달함으로써 then 및 catch 함수에서 비동기 작업으로부터 정보를 얻을 수 있게 되는 것임

async-await 비동기작업을 손쉽게 만들기

  • async 함수 : async 키워드는 함수를 선언할 때 붙여줄 수 있다
  • async 함수의 리턴 값은 무조건 Promise이다. (비동기 처리 메서드가 꼭 프로미스 객체를 반환해야 await가 의도한 대로 동작)
  • await 함수의 제약조건 : async 함수에서만 사용가능
  • await은 Promise가 완료될 때까지 기다린다
  • await은 Promise가 resolve 한 값을 내놓는다
  • 해당 Promise에서 reject가 발생한다면 예외가 발생

async-await 기본문법

async function 함수명() {
  await 비동기_처리_메서드_명();
}

실용 예제 - 각각 사용자와 할 일 목록을 받아오는 HTTP 통신 코드

function fetchUser() {
  var url = 'https://jsonplaceholder.typicode.com/users/1'
  return fetch(url).then(function(response) {
    return response.json();
  });
}

function fetchTodo() {
  var url = 'https://jsonplaceholder.typicode.com/todos/1';
  return fetch(url).then(function(response) {
    return response.json();
  });
}

// 위 함수들을 실행하면 각각 사용자 정보와 할 일 정보가 담긴 프로미스 객체가 반환됩니다.
// 이 두 함수를 이용하여 할 일 제목을 출력해보겠습니다. 살펴볼 예제 코드의 로직은 아래와 같습니다.

// 두 함수를 이용하여 할 일 제목을 출력

// 1. fetchUser()를 이용하여 사용자 정보 호출
// 2. 받아온 사용자 아이디가 1이면 할 일 정보 호출
// 3. 받아온 할 일 정보의 제목을 콘솔에 출력


async function logTodoTitle() {
  var user = await fetchUser();
  if (user.id === 1) {
    var todo = await fetchTodo();
    console.log(todo.title); // delectus aut autem
  }
}
profile
필요한 내용을 공부하고 저장합니다.

0개의 댓글