Today I Learned 2023.02.23. [코어 자바스크립트 3]

Dongchan Alex Kim·2023년 2월 23일
0

Today I Learned

목록 보기
17/31
post-thumbnail

자바스크립트 엔진은 싱글 스레드로 동작한다.

자바스크립트의 메인 스레드인 이벤트 루프가 싱글 스레드이므로 자바스크립트를 싱글 스레드 언어라고 부른다.
그러나 이벤트루프만 독립적으로 실행되는 것이 아닌, 브라우저와 같은 멀티 스레드 환경에서 실행됨 -> 런타임은 싱글 스레드가 아니다.

  1. 동기식으로 코드 한줄 한줄 실행 -> 앞의 작업시간이 길수록 시간 및 자원의 낭비가 심해짐.

  2. setTimeout함수가 실행되면 callstak에 setTimeout함수가 추가된다.

  3. wep API가 setTimeout함수를 처리하고 Task Queue로 인자로 받은 callback함수를 전달한다.

  4. 이대로 내비둔 채로, 다음 동기줄이 실행된다.

  5. 동기줄이 모두 실행되어 callstack이 비워진 걸 확인한 event loop는 task Queue에 있던 callback함수를 call stack으로 옮겨 작업을 수행한다.

비동기 / 동기

const fulfilled = new Promise((resolve,reject) => {
	setTimeout(()=> {
    	if(Math.random() > 0.5) resolve(1);
      	else reject(2);
    },3000);
}).then(res => console.log(res))
.catch(err => console.log(err))

setTimeout 함수 이후의 태스크를 블로킹하지 않고 곧바로 실행한다.

  • 이처럼 현재 실행 중인 태스크가 종료되지 않은 상태라 해도 다음 태스크를 곧바로 실행하는 방식을 비동기처리 라고 한다.
    자바스크립트의 동시성(concurrency)을 지원하는 것이 바로 이벤트루프이다.

비동기 처리를 위한 콜백 패턴의 단점

비동기 함수 내부의 비동기로 동작하는 코드는 비동기 함수가 종료된 이후에 완료된다.
-> 따라서 비동기 함수 내부의 비동기로 동작하는 코드에서 처리 결과를 외부로 반환하거나 상위 스코프릐 변수에 할당하면 기대대로 동작 X

let g = 0;

setTimeout(()=> {g = 100;},0);
console.log(g)//0

프로미스

비동기 함수인 promise는 함수 내부에서 프로미스를 생성하고 반환한다.

  • 성공하면 비동기 처리 결과를 resolve함수에 인수로 전달하면서 호출하고,
  • 비동기 처리가 실패하면 에러를 reject함수에 인수를 전달하면서 호출한다.

프로미스의 상태state 정보
1. pending
2. fulfilled
3. rejected

프로미스 후속 처리 메서드

promise.then()
promise.catch()
promise.finally()
프로미스 비동기 처리에서 발생한 에러는 then 메서드의 두번째 콜백 함수로 처리할 수 있다.

그치만, then 메서드의 두번째 콜백 함수를 전달하는 것보다 catch메서드를 사용하는 것이 가독성이 좋고 명확하다.

프로미스 체이닝

const url = 'https://jsonplaceholder.typicode.com';

promiseGet(`${url}/post/1`)
//취득한 post의 userID로 user정보를 취득
	.then(({userId})=> promiseGet(`${url}/users/${userId}`))
	.then(userInfo => console.log(userInfo))
	.catch(err => console.error(err));
// async & await
const url = 'https://jsonplaceholder.typicode.com'

asyn()=>{
	//id가 1인 post의 userId를 취득
  	const {userId} = await promiseGet(`${url}/post/1`);
  	const userInfo = await promiseGet(`${url}/users/${userId}`)
    
    console.log(userInfo)
}

then() -> then() -> catch() 순서로 후속 처리 메서드를 호출했다.
then catch finally 후속 처리 메서드는 언제나 프로미스를 반환하므로 연속적으로 호출할 수 있다.
이를 promise chaining이라고 한다.

프로미스의 정적 메서드

promise.resolve()
promise.reject()
promise.all()
promise.race()
promise.allSettled()

Promise는 주로 생성자 함수로 사용되지만, 함수도 객체이므로 메서드를 가질 수 있다.

  • promise.all() : 전달받은 모든 프로미스가 fulfilled 상태이면 모든 처리결과를 배열에 저장해 새로운 프로미스로 반환한다.

  • promise.race() : 가장 먼저 fulfilled상태가 된 프로미스의 처리결과를 resolve하는 새로운 프로미스로 반환한다.

  • promise.allSettled() : 전달받은 프로미스가 모두 settled상태 (비동기 처리가 수행된 상태, 즉 fulfilled 또는 rejected 상태)가 되면 처리결과를 배열로 반환한다.

마이크로태스크 큐

setTimeout(()=> console.log(1),0);

Promise.resolve()
	.then(()=> console.log(2))
	.then(()=> console.log(3))
// 2 -> 3 -> 1

그 이유는 프로미스의 후속처리 메서드의 콜백 함수는 태스크 큐가 아니라 마이크로태스크 큐에 저장되기 때문이다.
-> 마이크로테스크 큐는 태스크 큐보다 우선순위가 높다!!!!

Async & Await

마치 동기처럼 프로미스를 사용할 수 있는 문법이다.
프로미스의 후속처리 메서드 없이,
마치 동기처럼 프로미스가 처리 결과를 반환하도록 구현할 수 있다는 것이다.

async

async 함수는 async 키워드를 이용해서 정의하며, 언제나 프로미스를 반환한다.

await

프로미스가 settled(비동기 처리가 수행된 상태)가 될 때까지 대기하다가,
settled상태가 되면 프로미스가 resolve한 처리 결과를 반환한다.
-> await는 반드시 프로미스 앞에서 사용해야 한다.

const fetch = require('node-fetch');

const getGithubUserName = async id => {
	const res = await fetch(`https://api.github.com/users/${id}`);
  	const { name } = await res.json();
  	console.log(name)
}

getGithubUserName('Dongckim');

fetch 함수가 수행한 http요청에 대한 서버의 응답이 도착해서 fetch함수가 반환한 프로미스가 settled상태가 될때까지 대기이다.
-> 이후 프로미스가 settled상태가 되면 프로미스가 resolve한 처리 결과가 res 변수에 할당된다.

에러 처리

비동기 함수의 콜백 함수를 호출한 것은 비동기 함수가 아니기 때문에 try...catch문으로 에러를 캐치할 수 없다.

하지만 async & await 문에서는 try...catch문을 사용할 수 있다.
콜백 함수를 인수를 전달받는 비동기 함수와는 달리 프로미스를 반환하는 비동기 함수는 명시적으로 호출할 수 있기 때문이다.

const fetch = require('node-fetch');

const foo = async() => {
	try{
    	const wrongurl = 'https://wrongurl.com'
        
       	const response = await fetch(wrongurl);
      	const data = await response.json();
      	console.log(data)
    }catch(err){
    	console.log(err); //TypeError : Failed to fetch
    }
}
foo()

Async 함수 내에서 catch문에서 사용해서 에러처리를 하지 않으면, async함수는 발생한 에러를 reject하는 프로미스를 반환한다.

profile
Disciplined, Be systemic

0개의 댓글