[개발자되기: 비동기] Day-22

Kyoorim LEE·2022년 5월 27일
0

비동기

blocking

하나의 작업이 끝날 때까지 이어지는 작업을 '막는 것'

동기적(synchronous)

시작시점과 완료시점이 같은 상황

Node.js

non-blocking

ex) 커피 주문이 blocking 되지 않고 언제든지 주문을 받을 수 있음

비동기적(synchronous)

ex) 커피가 완성되는 즉시 커피를 주문하게 되면 1번 손님의 주문완료시점과 2번 손님의 주문 시작시점이 같을 필요가 없음

Javascript에서 비동기적 실행이 유용하게 쓰이는 상황

  • 백그라운드 실행, 로딩 창 작업 시
  • 인터넷에서 서버요청 보내고 응답 기다리는 작업
  • 큰용량의 파일 로딩 시

비동기 호출(Asynchronous call)

콜백함수

다른 함수(A)의 전달인자(arguments)로 넘겨주는 함수
- parameter를 넘겨받는 함수A는 callback 함수B를 필요에 따라 즉시 실행(synchronously)할 수도 있고 아니면 나중에 (asynchronously) 실행할 수도 있다

function B() {
  console.log('called at the back!');
}

function A(callback) {
  callback(); // callback === B
}

A(B);

반복 실행하는 함수 (iterator) - callback in action

[1,2,3].map(function(element, index) {
  return element * element;
});

이벤트에 따른 함수 (event handler) - callback in action

document.querySelector('#btn').addEventListener('click', function(e) {
  console.log('button clicked');
});

※ 주의사항 ※

함수 실행을 연결하는 것이 아니라 함수 자체를 연결하는 것임

document.querySelector('#btn').onclick = handleClick; // OK
document.querySelector('#btn').onclick = function() {
  handle.Click();
}; // OK
document.querySelector('#btn').onclick = handleClick.bind(); // OK
document.querySelector('#btn').onclick = handleClick(); 
// NOOOO => 이 경우 함수를 실행시키면 'undefined'가 나옴.
// 함수실행과 연결하는 것이 아니라 함수 자체와 연결하는 것임을 명시하자!!

blocking vs non-blocking

blockingnon-blocking
전화문자
하던 일을 멈추고 받아야함확인 후 나중에 답장 가능
요청에 대한 결과가 동시에 일어남요청에 대한 결과가 동시에 일어나지 않음, delay 일어남

비동기의 주요 사례

DOM Element의 이벤트 핸들러

  • 마우스, 키보드 입력(click, keydown)
  • 페이지 로딩(DOMContentLoaded)

타이머

  • 타이머 API(setTimeout)
  • 에니메이션 API(requestAnimationFrame)

서버에 자원요청 및 응답

  • fetch API
  • AJAX (XHR)

비동기 JavaScript

why async?

동기 상태에서는 한가지 작업요청이 실행되기 전까지 client에서 아무것도 못하고 무조건 기다려야하는 상황이 발생하는 반면 비동기 상태에서는 작업요청을 하고 실행되기 전까지 내가 원하는 작업을 진행할 수 있다 => 시간 단축

callback

callback을 통해 async를 제어할 수 있음

callback 없을 때

const printString = (string) => {
  setTimeout(
  	() => {
      console.log(string)
    },
  	Math.floor(Math.random() * 100) + 1
  )
}

const printAll = () => {
  printString("A")
  printString("B")
  printString("C")
}
printAll() 
// Math.random()함수로 인해 A,B,C 가 순서대로 나오지 않음

callback 함수 사용시

const printString = (string, callback) => {
  setTimeout(
  	() => {
      console.log(string)
      callback()
    },
  	Math.floor(Math.random() * 100) + 1
  )
}

const printAll = () => {
  printString("A", ()) => {
  	printString("B", ()) => {
  		printString("C", () => {})
    })
  })
}
printAll() 
// printString("A", ())에서 콜백함수가 실현되고 그게 실현이 다 되면 B를 부르고 그 다음에 C를 부르고..
// A, B, C 순서대로 진행됨

단점: callback hell => 코드가 너무 늘어나고 지저분해짐

Promise (feat. 드림엘리)

how to deal with callback hell 👿
state: pending -> fulfilled or rejected

성공적으로 받은 데이터는 resolve 콜백함수로 전달함

Producer vs Consumer

  1. Producer 측면
    when new Promise is created, the executor runs automatically
const promise = new Promise((resolve, reject) => {
  console.log('doing someting...');
  setTimeout(() => {
    resolve('ellie');
    // reject(new Error('no network')); // 에러 발생 시 
  }, 2000);
}), 
  1. Customers : .then, .catch, `finally'
promise.then(value => {
  console.log(value);
});
// 에러 발생시
//promise.catch(error => {
//  console.log(error);
//});

finally: 성공, 실패와 상관없이 마지막에 호출됨

promise.finally(() => {
  console.log('finally')'
});

또 다른 예시

const printString = (string, callback) => {
  return new Promise(resolve, reject) => {
  setTimeout(
  	() => {
      console.log(string)
      resolve()
    },
  	Math.floor(Math.random() * 100) + 1
  	)
  })
}

const printAll = () => {
  printString("A") 
  	.then (() => { // A가 다 실행되면 그때 () 콜백함수를 실행시켜라 
  		return printString("B")
  })
  	.then (() => { // B가 다 실행되면 그때 () 콜백함수를 실행시켜라 
    	return printString("C")
  })
}
printAll() 
// reject인 경우 .catch로 진행됨 (.then 대신에)
// 콜백을 쓸 때 보다 가독성이 좋음
// 동작은 콜백과 동일하게 이뤄짐

promise chaining

const fetchNumber = new Promise((resolve, reject) => {
  setTimetout(() => resolve(1), 1000);
});

fetchNumber
	.then(num => num * 2) // num <- 1 들어가서 num === 2 
	.then(num => num * 3) // num === 6
	.then(num => { // num === 6
  		return new Promise((resolve, reject) => {
          setTimeout(() => resolve(num - 1), 1000); // 6 - 1
        });
	})
	.then(num => console.log(num)); // num === 5

Error handling

const getHen = () => 
	new Promise((resolve, reject) => {
      setTimeout(() => resolve('🐓'), 1000);
    });
const getEgg = hen => 
	new Promise((resolve, reject) => {
      setTimeout(() => resolve(`${hen} => 🥚`), 1000);
      // setTimeout(() => reject(new Error(`error! ${hen} => 🥚`), 1000); <=== 에러 발생시
    });
const cook = egg => 
	new Promise((resolve, reject) => {
      setTimeout(() => resolve(`${egg} => 🍳`), 1000);
    });

getHen()
	.then(hen => getEgg(hen)) // .then(getEgg)과 동일
	// 에러 발생시 아래줄로 대체 가능
	// .catch(error => {
	//		return `🥖`;
	// })
	.then(egg => cook(egg)) // .then(cook)
	.then(meal => console.log(meal)); // .then(console.log)
	// 에러 발생시 아래줄 추가
	//.catch(console.log)
               

async - await (feat. 드림엘리)

promise를 좀더 간결하고 동기적으로 실행되게 보이는 것처럼 만들어 줄 수 있음
clear style of using promise

synthetic sugar 🍩

async

//function fetchUser() {
//  return new Promise((resolve, reject) => {
//    // do network request in 10 secs...
//    resolve('ellie');
//  });
//}
async function fetchUser() {
		do network request in 10 secs...
	return 'ellie';
}
// async라는 키워드를 함수 앞에 쓰기만 하면 됨! promise를 간단하게 쓸 수 있는 syntatic suger :) 

const user = fetchUser();
user.then(console.log);
console.log(user);

await

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

async function getApple() {
  await delay (3000); // 3초간 있다가 사과를 리턴 
  return '🍎';
}

async function getBanana() {
  await delay (3000); // 3초 있다가 바나나 리턴
  return '🍌';
}

// 콜백지옥의 예시
function pickFruits() {
  return getApple()
  	.then(apple => {
    	return getBanana().then(banana => `${apple} + ${banana}`);
  });
}
//async로 간단하게 만들어주기
async function pickFruits() {
  const applePromise = getApple(); // 동시에 병렬적으로 실행시켜줌
  const bananaPromise = getBanana(); // "
  const apple = await applePromise;
  const banana = await bananaPromise;
  return `${apple} + ${banana}`;
}

pickFruits().then(console.log)

useful promise APIs

위에 사과와 바나나를 동시에 병렬적으로 출력하기위한 더 간단한 코드
Promise.all

function pickAllFruits() {
  return Promise.all([getApple(), getBanana()]).then(fruits => fruits.join(' + ')
	);
}
pickAllFruits().then(console.log)

Promise.race

function pickOnlyOne() {
  return Promise.race([getApple(), getBanana()]);
}
pickOnlyOne().then(console.log);

타이머 API

setTimeout(callback, milisecond)

일정 시간 후에 함수를 실행
자바스크립트 내장 비동기 함수

  • 매개변수(parameter): 실행할 콜백함수, 콜백함수 실행 전 기다려야 할 시간
  • 리턴값: 임의의 타이머 ID
setTimeout(function () {
  console.log('1초 후 실행');
}, 1000);
// 123

clearTimeout(timerId)

setTimeout 타이머를 종료

  • 매개변수(parameter): 타이머 ID
  • 리턴값: 없음
const timer = setTimeout(function () {
  console.log('10초 후 실행'); 
}, 10000);
clearTimeout(timer);
// setTimeout이 종료됨

setInterval(callback, milisecond)

일정 시간의 간격을 가지고 함수를 반복적으로 실행

  • 매개변수(parameter): 실행할 콜백함수, 반복적으로 함수를 실행시키기 위한 시간 간격
  • 리턴값: 임의의 타이머ID
setInterval(function () {
  console.log('1초마다 실행');
}, 1000);
// 345

clearInterval(timerId)

setInterval 타이머를 종료

  • 매개변수: 타이머ID
  • 리턴값: 없음
const timer = setInterval(function() {
  console.log('1초마다 실행');
}, 1000);
clearInterval(timer);
// setInterval이 종료됨
profile
oneThing

0개의 댓글