JavaScript '비동기 처리'란?

Sohyeon Bak·2023년 1월 3일
0

JavaScript

목록 보기
1/1

Callback Function

  • 특정 동작을 실행할 때 실행하는 함수
  • 비동기 동작에서
  • 모든 콜백 함수가 비동기 처리를 위하는 것은 아님(그러나 비동기 처리를 위하는 경우가 많음)
    • setTimeout, setInterval
    • 이벤트 처리
    • node.js 파일 입출력 API, 네트워크 API

Callback Hell

  • 여러 비동기 처리에 콜백 함수를 사용할 때 발생
  • 코드가 직관적으로 이해하기 어려울 정도로 복잡해짐

Promise Object

  • 비동기 동작의 처리를 담당하는 객체
  • new Promise 객체에 전달되는 함수 executor(실행자, 실행 함수)는 new Promise가 만들어질 때 자동으로 실행된다.
    • 자동으로 실행되는 executor에서 원하는 일이 처리된다.
    • executor에서 인자로 넘겨준 콜백(resolve, reject) 중 하나는 반드시 호출되어야 한다.
  • 이행(fulfilled), 거부(rejected), 대기(pending) 상태를 통해 비동기 동작 처리
    • 이행(fulfilled), 거부(rejected)의 상태는 '처리된(settled) 프라미스'
    • 동기식 함수의 성공(=이행), 실패(=거부)에 추가된 한가지 상태는 '대기(pending) 상태 프라미스'
  • Promise.resolve()로 성공한 객체 생성
    • 일이 성공적으로 끝난 경우 그 결과를 나타내는 value와 함께 호출(fulfilled promise-약속이 이행된 프라미스)
       	Promise.resolve(100);
    			new Promise(resolve => resolve(100));```
  • Promise.reject()로 실패한 객체 생성
    • 에러 발생 시 에러 객체를 나타내는 error와 함께 호출
       Promise.reject('error!');
    			new Promise((_, reject) => reject('error!'));```
  • 처리가 완료된 프라미스에 resolve나 reject를 또 호출하면 무시된다.

.then(성공시콜백)

  • .then의 첫번째 인수는 프라미스가 이행(fulfilled)되었을 때 실행되는 함수이고, 이곳에서 실행 결과를 받는다.
  • .then의 두번째 인수는 프라미스가 거부(rejected)되었을 때 실행되는 함수이고, 이곳에서 에러를 받는다.
promise.then(
  	result => alert(result), // 1초 후 "완료!"를 출력
  	error => alert(error) // 실행되지 않음
);
  • 콜백에서 반환된 promise 객체는 다음 then에서 처리
fetch('http://example.com/movies.json')
	.then((res) => res.json()) //promise object type
	.then((data) => console.log(data)) // object type
	.catch(err => console.err(err));
  • promise 객체가 아닌 값을 반환할 때는 성공한 promise처럼 생각
fetch('http://example.com/movies.json')
	.then((res) => res.status) //object type
	.then((data) => console.log(data))
	.catch(err => console.err(err));
  • "성공시콜백"이 함수가 아닐 때 → 받은 값 그대로 취급
fetch('http://example.com/movies.json')
	.then(value => value)
	.then((data) => console.log(data));

fetch('http://example.com/movies.json')
	.then('data')
	.then((data) => console.log(data));

.catch(실패시콜백)

  • .then(undefined, 실패시콜백)의 단축 문법
  • .catch 후에도 .then을 계속 사용할 수 있다.
    • 위 아래 코드는 같은 동작을 한다.
    new Promise((resolve, reject) => {
		console.log("시작")
		resolve();
		console.log("시작2")
	})
	.then(() => {
		throw new Error("실패!")
		console.log("")
	})
	.catch(() => {
		console.error("실패시 실행")
	})
	.then(() => {
		console.log("이건 꼭 실행하세요.")
	})
	new Promise((resolve, reject) => {
		console.log("시작")
		resolve();
		console.log("시작2")
	})
	.then(() => {
		throw new Error("실패!")
		console.log("")
	})
	.then(undefined, () => {
		console.error("실패시 실행")
	})
	.then(() => {
		console.log("이건 꼭 실행하세요.")
	})

.finally(결정시콜백)

  • .then(결정시콜백)과 비슷한 느낌
  • 결정시콜백에서 반환되는 값은 무시된다.
  • 쓸모가 없어진 로딩 인디케이터를 멈추는 경우와 같이 결과가 어떻든 마무리가 필요할 때 사용된다.
    • finally에서는 프라미스가 이행되었는지, 거부되었는지 알 수 없다.
    • finally는 자동으로 다음 핸들러의 결과와 에러를 전달한다.
    new Promise((resolve, reject) => {
  		setTimeout(() => resolve("결과"), 2000)
	})
 	.finally(() => alert("프라미스가 준비되었습니다."))
  	.then(result => alert(result));// <-- .then에서 result를 다룰 수 있음
new Promise((resolve, reject) => {
  		throw new Error("에러 발생!");
	})
  	.finally(() => alert("프라미스가 준비되었습니다."))
  	.catch(err => alert(err)); // <-- .catch에서 에러 객체를 다룰 수 있음

Thenable

  • .then만 있으면 나머지 두 개는 구현/대체 가능
  • 호환성을 위해 객체에 .then()이 있으면 promise 처리가 가능

Promise 다중 처리

  • 배열(iterable)로 전달받은 promise 객체가 있을 때
    • promise.any: 가장 먼저 이행된 값을 이행, 모두 실패하면 실패
    • promise.all: 모두 이행되면 값 배열로 전달하며 이행, 하나라도 실패하면 실패
    • promise.race: 가장 먼저 결정된 값에 따라 결정
    • promise.allSettled: 모두 결정되면 결과 객체 배열이 전달되며 이행
      • .status: "fulfilled" 또는 "rejected"
      • .value: "fulfilled"된 경우에만 존재
      • .reason: "rejected"된 경우에만 존재

마이크로태스크 큐

  • 프라미스 핸들러 .then/.catch/.finally는 항상 비동기로 실행된다.
    • 콘솔 창에 "코드 종료", "프라미스 성공!" 순서로 찍힌다.
    let promise = Promise.resolve();
		promise.then(() => alert("프라미스 성공!"));
		alert("코드 종료");
  • 마이크로태스크 큐는 먼저 들어온 작업을 먼저 실행한다(FIFO)
  • 실행할 것이 아무것도 남아있지 않을 때만 마이크로태스크 큐에 있는 작업이 실행된다.

  • 프라미스 핸들러는 항상 내부 큐를 통과하게 된다.
  • 모든 프라미스 동작은 마이크로태스크 큐라 불리는 내부 프라미스 잡(promise job) 큐에 들어가 처리되기 때문에 프라미스 핸들링은 항상 비동기로 처리된다.

Async-Await

Async 함수

  • 항상 Promise를 반환하는 함수
    • async가 앞에 붙는 함수는 항상 프라미스를 반환한다.
    • 프라미스가 아닌 값을 반환해도 이행 상태의 프라미스(resolved promise)로 값을 감싸 이행된 프라미스가 반환된다.
        async function f() {
  			return 1;
		}

		f().then(alert); // 1
  • 명시적으로 프라미스 반환도 가능
        async function f() {
  			return Promise.resolve(1);
		}

		f().then(alert); // 1
  • 또는 await 연산자를 내부에 사용하는 함수
  • 항상 Promise를 반환해야하는 것과 await 연산자를 내부에 쓸 필요가 없으면 async를 사용할 필요가 없다.

Await 연산자

  • Promise 상태가 결정될 때까지 대기
    async function one() {
		return new Promise(resolve => setTimeout(() => resolve(1230)))
			.then(v => v+4)
			.catch(e => console.error(e));
	}
	const num = await one();
	console.log(num) // 1234 출력
  • 사실 Thenable을 실행하는데, 그 외의 값도 가능
    const num = await {
		then(fulfill) { fulfill(123) }
	}
	console.log(num)
	console.log(await "hello");
  • awaitpromise.then 보다 더 세련되게 프라미스의 결과 값을 얻을 수 있게 해준다.
    • 비동기 함수의 실행 순서를 마치 동기실처럼 제어
  • 에러 처리는 try/catch를 사용한다.
  • await을 만나면 실행을 중단하고 `microtask queue에 넣는다.
    • 비동기로 동작한다는 뜻
  • async 함수 내부나 모듈의 top-level에서만 사용 가능

async/await과 promise.then/.catch
- async/await을 사용하면 await이 대기처리를 해주기 때문에 then이 거의 사용되지 않는다.
- 또한 .catch보다 try/catch를 사용할 수 있게 된다.
- 그렇지만 문법적 제약(함수안에서 await을 사용할 수 있는) 때문에 .then/.catch를 추가해 처리하지 못한 에러나 최종 결과를 도출 할 수 있다.

async/await는 Promise.all과도 함께 쓸 수 있다.

비동기 처리 유의사항

  • 비동기 함수 간에는 실행 순서 보장 안되는 게 기본
  • async 함수 내에서는 await을 사용해 실행 순서 보장
  • 동시 처리가 필요하다면 promise 정적 함수 활용
  • 비동기 처리가 필요하면 async/await을 최대로 활용
  • DOM 이벤트 핸들러로 async 함수를 사용할 수 있다.
  • 비동기는 성능과 관련있다.
    • 네트워크 처리, 파일 입출력하는 기간동안 작업을 위임해 진행할 수 있게 도와준다.
  • 기능별 async 함수 만들고, 실행순서 보장 할 수 있는 모듈을 만드는게 좋다.

출처

[패스트캠퍼스] <개발 세미나 : D-day Vol. 2> 자바스크립트 비동기 처리
모던 자바스크립트 - 프라미스와 async, await

profile
정리하고 기억하는 곳

0개의 댓글