[2021.08.12] 핵심 자바스크립트 개념1

이다은·2021년 8월 13일
1
post-thumbnail

📌 동기와 비동기의 이해

💡 동기(Synchronouse)

  • 현재 실행 중인 코드가 끝나야 다음 코드를 실행하는 것
  • 현재 실행중인 task가 종료될때까지, 다음 task가 대기하는 방식
  • 순차적으로 실행
    ➡ (장점) task 실행 순서가 보장된다.
    ➡ (단점) task가 블로킹(blocking) 된다.
// blocking 예시
const arr = [];
for (let i = 0; i < 1000; i++) {
  arr.push(i);
  // 1000번 반복하면서 실행할 더더더더더 복잡한 코드
}
alert("안녕하세요);  // 확인 누를 때까지 console 안나옴
console.log(arr);

💡 비동기(Asynchronouse)

  • 현재 실행 중인 코드가 완료되지 않아도, 다음 코드로 넘어감
  • 비동기 task는 실행하라고 브라우저에 맡겨놓고, 다음 task로 넘어감
    ➡ (장점) task가 블로킹(blocking) 되지 않는다.
    ➡ (단점) task 실행 순서가 보장된다.
// 화면 그리기 1
axios.get(url)
  .then(response => {
    // api 호출하고 응답받으면 실행
  })
// 화면 그리기 2
// 다른 로직~~
  1. 화면그리기 1 하고,
  2. axios api 호출하고 (언제 응답이 올지는 모름)
  3. 화면그리기 2 하다가
  4. 응답이 오면 로직을 실행하고,
  5. 다시 다른 로직 진행한다.

💡 비동기 처리가 필요한 이유

> 이벤트루프 작성내용 참고

  • 자바스크립트 엔진은 한 번에 하나의 task만 실행할 수 있는 Single Thread.
  • Single Thread는 한 번에 하나의 task만 실행할 수 있기 때문에 처리에 시간이 걸리는 task를 실행하는 경우 Blocking(작업 중단)이 발생한다. ➡ 콜백함수의 등장!!

📌 콜백함수 (Callback Function; cb)

  • 함수의 매개변수가 함수일 때, 매개변수로 받은 함수를 콜백함수라고 부른다.
  • 콜백 헬, 콜백 지옥 발생 ➡ function()안에 function()안에 function()안에 계속 코드 작성..
// [1] 콜백함수 예시 => 동기 함수 => closeFn이 콜백함수!
function showMessage(msg, closeFn) {
  closeFn(true);
}

// [2] 콜백함수 예시 => 동기 함수 =>  map 함수의 인자 el => el*2 함수가 콜백함수!
[1,2,3].map(el => el*2);

// [3] 콜백함수 예시 => 비동기 함수 => e => {} 함수가 콜백함수!
window.addEventListener('keydown', e => {
  // 로직
});

// [4] 콜백함수 예시 => 비동기 함수 => setTimeout에 넘긴 익명함수가 콜백함수!
setTimeout(function(){ 
  alert("Hello"); 
}, 3000);

📌 Promise ?

  • 비동기 동작을 처리하기 위해 ES6에 도입되었다.
  • Promise는 클래스다.
  • Promise 클래스를 인스턴스화 해서 promise 객체를 만든다.
  • 반환된 promise로 원하는 비동기 동작을 처리한다.
  • Promise는 state, resolve, reject 함수를 이해하면 된다.

💡 1. resolve, reject

  • resolve : 성공했을 때 실행할 함수
let promise = new Promise(function(resolve, reject) {
  // 여기 비동기 로직을 작성!
  // 시켜놓고 언제 완료될지 모르는 로직!
  setTimeout(function() {
    resolve('hello world');// resolve 함수에 인자 넘기면
  }, 2000);
});

// resolve로 넘기는 콜백함수의 매개변수로 받을 수 있다.
promise.then(function(msg) {
  console.log(msg);  // 2초 뒤에 hello world!
});

reject : 실패했을 때 실행할 함수

let promise = new Promise(function(resolve, reject) {
  setTimeout(function() {
    reject('으악!'); // 실패했다고 가정하고 reject 호출
  }, 2000);
});

// then 인자 둘 다 cb 함수
// 첫 번째는 성공했을 때 실행할 resolve 함수 정의
// 두 번째는 실패했을 때 실행할 reject 함수 정의
promise.then(function() {
    console.log('resolve');
  }, function(msg) {
    console.log('reject', msg);
});

Q1. Promise안에 resolve() 함수가 여러개 있으면 어떻게 동작할까?

let promise = new Promise(function(resolve, reject) {
    setTimeout(function() {
        resolve(1); // ✅ 첫 번째 resolve 만 실행됨
        resolve(2);
    }, 1000);
});

Q2. Promise안에 resolve(), reject() 함수가 여러개 있으면 어떻게 동작할까?

let promise = new Promise(function(resolve, reject) {
    setTimeout(function() {
        reject(1); // ✅ 첫 번째 reject 만 실행됨
        resolve(2);
        resolve(3);
    }, 1000);
});

Q3. Promise안에 실행순서 맞추기!

let promise = new Promise(function(resolve, reject) {
    setTimeout(function() {
        console.log(1);
        resolve(2);
        console.log(3);
        resolve(4);
    }, 1000);
});

promise.then(function(data) {
    console.log(data);
});
  1. console.log(1)
  2. console.log(3)
  3. resolve(2) 실행 & console.log(2)
    ✅ ➡ 정답: 1 -> 3 -> 2

💡 2. chaining

  • 여러 개의 비동기 작업을 순차적으로 해야하는 경우
  • 순차적으로 각각의 작업이 이전 단계 비동기 작업을 성공하고 나서,
    그 결과값을 이용하여 다음 비동기 작업을 실행해야 하는 경우
  • then의 콜백함수(=handler function)는 여러 타입의 값을 반환할 수 있다.
let promise = new Promise(function(resolve, reject) {
    setTimeout(function() {
        resolve(1);
    }, 1000);
});

promise
  .then(function(first) {  
    console.log('first', first); // ✅ 'first',1
    return 22;  //값 2를 리턴
  })
  .then(function(second) { //second에 22가 들어온다.
    console.log('second', second); // ✅ 'second',22
    return new Promise(function(resolve, reject) { // new Promise를 리턴
      setTimeout(function() {
        resolve(333);
      }, 1000);
    });
  })
  .then(function(third) {
    console.log('third', third); // 1초 뒤 333 실행됨 // ✅  'third', 333
  });

💡 3. state

promise 객체는 "상태"를 갖고 있다.

  1. Pending(대기): 비동기 처리 로직이 아직 완료되지 않은 상태
  2. Fulfilled(이행): 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
  3. Rejected(실패): 비동기 처리가 실패하거나 오류가 발생한 상태

💡 4. 에러 처리

reject handler와 catch 방식은 똑같다. catch문이 가독성이 더 좋다.

let promise = new Promise(function(resolve, reject) {
  setTimeout(function() {
    reject('으악!');
  }, 2000);
});

promise
  .then(function() {})
  .catch(function(err) {  // ✅ catch
    console.log(err);
  });

Q1. console.log 정답은? ➡ 4,5

function job() {
    return new Promise(function(resolve, reject) {
        reject(); // reject를 실행
    });
}

job()
  .then(function() {
    console.log(1);
  })
  .then(function() {
    console.log(2);
  })
  .then(function() {
    console.log(3);
  })
  .catch(function() {
    console.log(4); // ✅ 4 출력
  })
  .then(function() {
    console.log(5); // ✅ 5 출력
  });

Q2. console.log 정답은? ➡ success, error, Error caught

function job(state) {
    return new Promise(function(resolve, reject) {
        if (state) {
            resolve('success');
        } else {
            reject('error');
        }
    });
}

job(true)
  .then(function(data) {
    console.log(data); // ✅ job(true) 이므로 resolve 실행 && 'success' 출력
    return job(false); // return 값으로 job(false) 반환
  })
  .catch(function(error) { 
    console.log(error); // ✅ job(false) 이므로 reject 실행 && 'error' 출력
    return 'Error caught'; // return 값으로 string 반환
  })
  .then(function(data) {
    console.log(data); // ✅ 'Error caught'값을 전달 받음 && 'Error caught' 출력
    return job(true); // return 값으로 job(true) 반환
  })
  .catch(function(error) {
    console.log(error); //-> job(true)의 resolve가 실행되므로 catch는 실행되지 않는다.
  });

💡 5. Promise.finally() / Promise.all()

  • Promise.finally()

    • finally() 메서드는 결과에 관계없이, promise가 처리되면 무언가를 프로세싱 또는 정리를 수행하려는 경우에 유용하다.
    • finally 콜백은 어떠한 인수도 전달받지 않는다.
      ➡ promise가 이행되었는지 또는 거부되었는지를 판단할 수 없기 때문!
  • Promise.all()

    • 여러 프로미스를 다 하고 그 다음에 하고 싶은 경우에 유용하다.

예제1 - Promise.all()

let urls = [
  'https://api.github.com/users/iliakan',
  'https://api.github.com/users/remy',
  'https://api.github.com/users/jeresig'
];

// map every url to the promise of the fetch
let requests = urls.map(url => fetch(url));

// Promise.all waits until all jobs are resolved
Promise
  .all(requests)
  .then(responses => responses.forEach(
    response => alert(`${response.url}: ${response.status}`)
));

예제2 - Promise.all() - 모든 promise가 resolve 될 때

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 3000, 'foo');
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values); // 3초 뒤 > Array [3, 42, "foo"] 
});

예제3 - Promise.all() - reject되는 promise가 있다면?
Promise.all()은 배열 내 요소 중 어느 하나라도 거부하면 즉시 거부!!

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(reject, 3000, 'foo');
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values); // 3초 뒤 > `all:1 Uncaught (in promise) foo` 에러반환
});

📌 async & await

  • async function은 ES8에 도입되었으며, 비동기 함수를 선언한다.

  • async function은 promise를 반환한다.

  • await를 쓰려면 async 함수 안에 꼭 있어야 한다!


  • (변경 전)
    .then().then().then() ~ 가독성이 떨어진다.

axios('https://api.test.com/proudcts')
  .then(function(response) {
    let firstProductId = response.products[0].id;
    return axios('https://api.test.com/proudct/comments?id='+firstProductId);
  })
  .then(function(response) {
    let firstCommentId = response.comments[0].id;
    return axios('https://api.test.com/proudct/comment/likes?id='+firstCommentId)
  })
  .then(function(response) {
    let likes = response.likes;
    let likesCount = likes.length;
});
  • (변경 후) async/await 문법으로 수정하여 가독성을 높인다.
(async () => {
 try {
   let productResponse = await fetch('https://api.test.com/proudcts');
   let firstProductId = productResponse.products[0].id;
   
   let commentResponse = await fetch('https://api.test.com/proudct/comments?id='+firstProductId);
   let firstCommentId = commentResponse.comments[0].id;
   
   let likeResponse = await fetch('https://api.test.com/proudct/comment/likes?id='+firstCommentId);
   let likesCount = likeResponse.likes.length;
   
 } catch(error) {
   console.log(error);
 }
})();
  • fetch/axios 함수는 promise를 반환한다
  • axios()new Promise()는 똑같다.
profile
단단_프로트엔드개발자!

0개의 댓글