js 비동기

황지웅·2022년 3월 13일
0

javascript

목록 보기
9/9

동기와 비동기


동기와 비동기란?

1.동기적(synchrounus)

  • 순차적으로 진행됨(하나의 작업이 끝나야, 그다음 작업을 진행함)
  • blocking(하나의 작업이 끝날 때까지,이어지는 작업을 "막는 것")

2.비 동기적(Asynchronous)

  • 순차적으로 진행되지 않음(무거운 작업일경우 빼놓고,그다음 작업으로 넘어감)
  • non-blocking(어떤 작업이 끝나지 않아도, 그다음 작업으로 넘어가는 것을 막지않음)
    <비동기의 주요 사례>
  • DOM Elemennt의 이벤트 핸들러
    • 마우스,키보드 입력(click,keydown 등)
    • 페이지 로딩(DOMContentLoaded 등)
  • 타이머
    • 타이머 API(setTimeout등)
    • 애니메이션 API(requestAnimationFrame)
  • 서버에 자원 요청 및 응답
    • fetch API
    • AJAX(XHR)

비동기적 실행이유와 작동원리

`

1. 이유

  • 자바스크립트는 기본적으로 싱글쓰레드 방식으로 동작하기 때문에 한 번에 한 가지 일만 수행할 수 있다
  • 현재의 웹은 너무나도 커지고 복잡해졌기 때문에 사용자의 동시 다발적인 요청에 빠르게 응답하기 위하여 비동기적 프로그래밍이 불가피해졌다.
  • callback 또는 promise ,async awit를 이용해 구현할 수 있다

2.작동원리(이벤트 루프)

  • 비 동기로 동작하는 핵심요소는 자바스크립트 언어가 아니라 브라우저가 가지고 있다.(Node에서는 libuv 라이브러리 등)
  • 브라우저는 Web APIs, Callback Queue, Event Loop 등으로 구성된다
  • 자바스크립트의 힙에는 메모리가 할당 되며(객체), call stack은 실행된 코드의 환경을 저장하는 자료구조이고 함수 호출치 call stack에 push된다
setTimeout(function exec() {
  console.log('second')
}, 1000);
  • 위 코드 실행시 setTimeout이 call stack에 들어와 실행되면 Browser API인 timer를 호출한후 call stack에서 제거된다 스케줄링한 시간이 지나면 callback queue에 추가되고 call stack이 모두 호출되어 비어있을 때 call stack으로 이동시킨다

3.정리

  • 사용자의 동시 다발적인 요청에 빠르게 응답하기 위하여 비동기적 프로그래밍이 필요하다
  • call back과 promise , async awit 를 이용해 비동기적으로 코드를 구현 할수있다

call back과 promise


콜백으로만 코드작성하기 =>콜백지옥

const f1 =(callback)=>{
  setTimeout(()=>{
    console.log("1번주문 완료");
	callback()
  },1000)
}

const f2= (callback)=>{
  setTimeout(()=>{
    console.log("2번주문 완료");
    callback();
  },3000);
}

const f3= (callback)=>{
  setTimeout(()=>{
    console.log("3번주문완료");
    callback();
  },2000)
}
//콜백지옥의 예시 <= 좋지못함
console.log("시작")
f1(()=>{
  f2(()=>{
    f3(()=>{
     console.log('끝')
    })
  })
})

promise 사용방법

//producer code
const pr = new Promise((resolve,reject)=>{
  setTimeuute(()=>{
    resolve('ok')
  //reject(new Error('err...'))
  },1000)
});
//consumer code
pr
.then(result=>{console.log(result)})//성공시(resolve()호출)에 실행됨
.catch(error=>{console.log(error)})// 실패시(reject()호출)에 실행됨
.finally(()=>{console.log('end')})//무조건 출력됨

callback hell promise로 바꾸기

//producer code
const f1 =() =>{
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve("1번주문완료")  
    },1000)
  });
};

const f2 = (message) =>{
  console.log(message);
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve("2번주문완료")  
    },3000)
  })
}

const f3 =(message) =>{
  console.log(message);
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve("3번주문완료")  
    },2000)
  })
}
//resolve()는 리턴이든 함수본문내에서든 호출만 되면된다

//Consumer code
//promises chaining
f1()
.then(result =>f2(result))
.then(result =>f3(result))
.then(result =>console.log(result))
.catch(error =>console.log(error))
.finally(()=>console.log("end"))
//then의 인수에는 함수의 호출이아닌 함수자체를 전달해야한다
//then안의 함수의 인자는 resolve의 인자로 넘어온다
//만약 중간에 실패하면 중간 then을 넘어서 바로 cathch로간다
//finally는 실패하든 성공하든 실행된다
////전달되는 인자가 하나일때는 생략방법이있다
f1()
.then(f2)
.then(f3)
.then(console.log)
.catch(console.log)
.finally(()=>console.log("end"))

프러미스 체이닝(promise chaining )

const fetchNumber =new Promise((reslove,reject)=>{
  setTimeout(()=>reslove(1),1000);
});

fetchNumber
.then(num => num*2)
.then(num=> num*3)
.then(num=>{
  return new Promise((resolve,reject)=>{
     setTimeout(()=>resolve(num-1),2000);
  });
})
.then(num=>console.log(num))
//then에서 값을 다른 then으로 전달할때는 전달받은 인자를 함수에서 return시켜야한다
//promise.then은 promise를 리턴한다
//promise.then에서 새로운 프러미스를 전달해도된다

promise all

const f1 =() =>{
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve("1번주문완료")  
    },1000)
  });
};

const f2 = () =>{
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve("2번주문완료")  
    },3000)
  })
}

const f3 =() =>{
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve("3번주문완료")  
    },2000)
  })
}

Promise.all([f1(),f2(),f3()])
.then((reslove)=>{console.log(reslove)})
//all의 인수로 배열을받는다
//all안의 작업들이 모두 끝이나면 결과를 담은 배열을 리턴한다
//모든작업이 동시에 진행되고, 모두 성공되어야만 결과를 리턴한다.
//하나라도 누락되면 안되는경우 사용하기 좋다

promise.race

const f1 =() =>{
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve("1번주문완료")  
    },1000)
  });
};

const f2 = () =>{
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve("2번주문완료")  
    },3000)
  })
}

const f3 =() =>{
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve("3번주문완료")  
    },2000)
  })
}

Promise.race([f1(),f2(),f3()])
.then(console.log)
//가장 빠르게 끝난 작업하나만 리턴한다

async &awit


async & awit 사용방법

async function getName1() {
  return 'Mike';
}
//async를 함수앞에 붙이면 이 함수는 프러미스를 반환한다
//state는 fulfilled가되고 result는 "Mike"가 된다

async function getName2() {
  return Promise.resolve("Tom")
}
//state는 fulfilled가되고 result는 "Tom"이 된다
//앞에 async를 안붙여도 되더라 어느게 보편적이지?
// return Promise.resolve("Tom") vs (차이점이뭐지?)
//return new promise((resolve,reject)=>{resolve("Tom")})

async function getName3() {
  throw new Error('err')
}
getName3()
.catch(err=>{console.log(err)})
//throw,try,catch공부
function getName(name){
  return new Promise((resolve,reject)=>{
      setTimeout(()=>{
        resolve(name)
      },1000)
    })
}

async function showName(){
  const result =await getName("Mike")
  console.log(result)
}
showName(); //1초후 "Mike"가 콘솔에 찍힘
//await키워드는 async함수 내부에서만 사용할 수 있다
//await키워드는 오른쪽으넨 promis가 오고 그 프로미스가 처리될때까지 기다린다

async, await로 콜백지옥 바꿔보기

const f1 =() =>{
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve("1번주문완료")  
    },1000)
  });
};

const f2 = (message) =>{
  console.log(message);
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve("2번주문완료")  
    },3000)
  })
}

const f3 =(message) =>{
  console.log(message);
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve("3번주문완료")  
    },2000)
  })
}
//resolve()는 리턴이든 함수본문내에서든 호출만 되면된다

async function order(){
  const result1 =await f1();
  const result2 =await f2(result1);
  const result3= await f3(result2)
  console.log(result3);
  console.log("종료")
}
//만약 reject가 호출되는경우
//async function order(){
//  try{
//    const result1 =await f1();
//    const result2 =await f2(result1);
//    const result3= await f3(result2)
//    console.log(result3); 
//  }catch(e){
//    console.log(e) 
// }
//  console.log("종료")
//}
order();
  • 비동기적으로 실행되는 코드내부에서 마치 동기적인 실행처럼 볼 수 있어 가독성이 매우좋음

질문에 답해보기

  • Promise 실행 함수가 가지고 있는 두 개의 파라미터 resolve 와 reject 는 각각 무엇을 의미하나요?
    • 성공시 resolve 실패시 reject를 이용해 값을 전달
  • resolve, reject함수에는 인자를 넘길 수 있습니다. 이때 넘기는 인자는 어떻게 사용할 수 있나요?
    • resolve,reject의 인자에 값을 넣으면 then이나 cahtch 로 받을 수 있다
  • new Promise()를 통해 생성한 Promise 인스턴스에는 어떤 메서드가 존재하나요? 각각은 어떤 용도인가요?
    • then,cahtch,finally등 컨슈머 코드에 사용된다
  • Promise.prototype.then 메서드는 무엇을 리턴하나요?
    • promise를 리턴하고 then 인자안의 함수에서 리턴된 값이 프러미스의 vlaue가 된다 상태는 fulfiled
  • Promise.prototype.catch 메서드는 무엇을 리턴하나요?
    • 프러미스를 리턴하고 상태는 rejected/ catch로 전달받을 수 있다
  • Promise의 세 가지 상태는 각각 무엇이며, 어떤 의미를 가지나요?
    • pending 대기상태 ,fulfiled 이행된 상태 ,rejected 거부실패된 상태
  • await 키워드 다음에 등장하는 함수 실행은 어떤 타입을 리턴할 경우에만 의미가 있나요?
    • 프러미스를 리턴하는 경우에만 의미가 있다
  • await 키워드를 사용할 경우, 어떤 값이 리턴되나요?
    프러미스가 리턴된다
  • Promise.all 의 인자는 어떠한 형태인가요?
    • 배열이다
  • Promise.all 을 사용할 경우에 then 메서드의 파라미터는 어떠한 형태인가요?
    • 함수다
  • Promise.all 에 두 개의 Promise 요청이 전달되고, 만일 그중 하나가 rejected 상태가 되는 경우, then 메서드를 따라가나요, 아니면 catch 메서드를 따라가나요?
    • catch

0개의 댓글