1. synchronous & asynchronous
01. JavaScirpt is synchronous
- js는 동기적인 아이이다!
- hoisting : variable&constant, Declarative function(선언식 함수) 등이 제일 위로 올라가는 것
- Execute the code block in order after hoisting
- 호이스팅이 된 이후부터 코드가 우리가 작성한 순서에 맞춰서 하나하나 동기적으로 실행
02. asynchronous(비동기적)
- 비동기적으로 언제 코드가 실행될지 예측할 수 없다
- setTimeout(Timehanler(callbackFn),Timeout);
- 브라우저에서 지원, 지정한 시간(Timeout)이 지나면 우리가 전달한 함수(Timehanler,콜백함수)를 호출
- 콜백함수 : 우리가 전달해준 함수를 나중에 니가 불러줘
- 동시에 여러작업을 같이 진행하고 작업 중에 함수 호출도 가능 하다
- Ajax Web API 요청, 파일읽기, 암호화/복호화, 작업예약 등의 작업을 할 때 사용
console.log('1');
setTimeout(()=> console.log(2) ,1000);
console.log('3');
// 1 3 2
03. Syncronous callback
- 즉각적으로 동기적으로 실행
- 절차지향적으로 앞에 작업이 끝나지 않으면 다음 작업으로 못 넘어가고 앞에 작업이 끝이 나야 다음 작업으로 넘어감
function printImmediately(print) {
print();
}
printImmediately(()=>console.log('hello')); // hello
04. Asyncronous callback
function printWithDelay(print, timeout) {
setTimeout(print,timeout);
}
printWithDelay(()=>console.log('async callback'), 2000); // 2초 후 async callback 출력
- 비동기 처리 전, work()가 실행이 끝난 후 '다음 작업' 출력
function work() {
// 현재 날짜를 숫자로 표시
const start = Date.now();
for(let i = 0; i < 1000000000; i++) {
}
const end = Date.now();
console.log(end - start + 'ms');
}
work();
console.log('다음 작업');
// 비동기 처리, setTimeout
// setTimeout을 하게 되면 함수 내의 작업이 백그라운드에서 실행
// 백그라운데서 작업이 이루어 지기 때문에 다른 작업이 진행 됨
function work() {
setTimeout(()=>{
const start = Date.now();
for(let i = 0; i < 1000000000; i++) {
}
const end = Date.now();
console.log(end - start + 'ms');
},0)
}
console.log('작업 시작')
work();
console.log('다음 작업');
// 만약에 work 함수가 끝난 후 작업을 하고 싶을 시 callback 함수를 파라메터로 전달
function work(callback) {
setTimeout(()=>{
const start = Date.now();
for(let i = 0; i < 1000000000; i++) {
}
const end = Date.now();
console.log(end - start + 'ms');
callback(emd - start)
},0)
}
console.log('작업 시작')
work((ms) => {
console.log('작업이 끝났어요');
console.log(ms + 'ms 걸렸다고 해요')
});
console.log('다음 작업');
중간 테스트
console.log('1');
setTimeout(()=> console.log(2) ,1000);
console.log('3');
printImmediately(()=>console.log('hello'));
printWithDelay(()=>console.log('async callback'), 2000);
// 1 3 hello 2 async callback
JS Engine
- 선언된 함수는 hoisting 된다
- print Immediately, print With Delay는 최상단으로 이동
- 중간테스트 순서로 출력
- 1 3 hello 2 async callback
Callback Helllllllll
- 콜백 체인의 문제점
- callbackFn 내부에서 callbackFn 그 안에서 callbackFn or Fn
- 가독성이 떨어진다
class UserStorage {
loginUser(id, password, onSuccess, onError) {
setTimeout(()=>{
if(
(id === 'ellie' && password === 'dream') ||
(id === 'coder' && password === 'academy')
) {
onSuccess(id);
} else {
onError(new Error('not found'));
}
},2000);
}
getRoles(user, onSuccess, onError) {
setTimeout(()=>{
if (user==='ellie') {
onSuccess({name : 'ellie', role : 'admin'});
} else {
onerror(new Error('no access'))
}
},1000);
}
}
// 백엔드 통신 클래스 생성
const userStorage = new UserStorage();
// 아이디 & 비번 받아오기
const id = prompt('enter your id');
const password = prompt('enter your password');
userStorage.loginUser(
id,
password,
user => {
userStorage.getRoles(
user,
userWithRole => {
alert(`Hello ${userWithRole.name}, you have a ${userWithRole.role} role`)
},
error => {
console.log(error);
}
);
},
error => {
console.log(error);
}
);
2. Promise
- JS(ES6)에서 제공하는 비동기를 간편하게 처리할 수 있게 도와주는 Object
- 비동기 작업 후에 작업을 해야 된다할 때는 callback함수를 사용했지만 비동기 작업이 많아 질 경우 코드가 복잡해짐 처음에는 라이브러리였지만 기능의 유용성으로 js 자체에 도입
- 정해진 장시간의 기능을 수행하고나서 정상적으로 기능이 수행이 되었다면 성공의 메시지와 함께 처리된 결과를 반환
- 만약에 에러가 난다면 에러를 내어 준다
- 단점
- 에러를 잡을 때 어떤 부분에서 에러가 발행하는지 파악이 힘들다
- 특정 조건에 따라 분기를 나누는게 어렵다
- 특정 값을 공유해가면서 작업하기가 번거로워 async await를 사용함
- Promise is a JavaScript Object for asynchronous operation
- state, 상태, 프로세스가 무거운 오퍼레이션을 수행하고 있는 중인지 아니면 기능을 수행을 성공했는지 실패했는지 catch
- producer & consumer의 차이, 우리가 원하는 데이터를 프로듀싱(제공)하는 사람과 제공된 데이터를 사용하는 사람(컨슈머)의 차이점을 잘 알아야한다
- State : pending(오퍼레이션 수행 중) -> fullfilled(성공) state or rejected(에러) state
- Producer(데이터를 만드는) vs Consumer(데이터 소비)
// 01) setTimeout
function increaseAndPrint(n, callback){
setTimeout(()=>{
cont increased = n + 1;
console.log(increased);
if(callback) {
callback(increased);
}
},1000)
}
//callback 지옥
increasedAndPrint(0, n => {
increaseAndPrint(n, n => {
increaseAndPrint(n, n=> {
increaseAndPrint(n, n=>{
increaseAndPrint(n, n=>{
console.log('작업 끝')
})
})
})
})
})
-02) promise
// promise는 성공 할 수 도 있고 실패할 수 도 있다
// 성공시 resolve 호출, 실패 시 reject 호출
const myPromise = new Promise((resolve, reject) => {
// 구현.....
setTimeout(()=>{
//1초 뒤에 성공
//resolve('result');
//1초 뒤에 실패
reject(new Error());
},1000)
});
// .then은 비동기 작업이 끝나고 나서 실행
// .catch는 error를 잡을 때 사용
myPromise.then(result => {
console.log(result);
}).catch(e => {
console.error(e);
})
-03) Promise를 만드는 함수 작성
function increaseAndPrint(n) {
return new Promise((resolve, reject)=>{
setTimeout(()=>{
const value = n + 1;
if( value === 5 ){
const error = new Error();
error.name = 'ValueOsFoveError';
reject(error);
return;
}
console.log(value);
resolve(value);
},1000);
});
}
increaseAndPrint(0).then(n => {
console.log('result : ', n);
})
//then을 연달아 사용할 수 도있다
increaseAndPrint(0).then(n => {
return increaseAndPrint(n);
}).then( n => {
return increaseAndPrint(n);
}).then(n => {
return increaseAndPrint(n);
}).then(n => {
return increaseAndPrint(n);
}).then(n => {
return increaseAndPrint(n);
}).catch( e => {
console.error(e);
})
// 위에 작업 간략화
increaseAndPrint(0).then(increaseAndPrint)
.then(increaseAndPrint)
.then(increaseAndPrint)
.then(increaseAndPrint)
.then(increaseAndPrint)
.catch(e => {
console.error(e);
})
01. Producer
- new <T> (executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): Promise<T>;
- excuotr라는 callbackFn를 전달, callbackFN에는 또다른 콜백함수를 받는다
- resolve 기능을 정상적으로 수행해서 최종 데이터를 전달할 콜백 함수
- reject 기능을 수행하다가 중간에 문제가 생기면 호출 할 콜백함수
- when new Promise is created, the excutor runs auntomatically.
- promise를 만드는 순간 우리가 전달한 excute 함수가 바로 실행
const promise = new Promise((resolve, reject)=> {
// doing some heavy work (metwork, read files)
console.log('doing sonething...');
setTimeout(()=>{
// 기능을 제대로 수행을 하였다면 resolve라는 callback함수 실행
// 성공적으로 네트워크에서 받아온 파일에서 읽어온
가공할 데이터를 resolve라는 callback함수를 통해서 전달
resolve('ellie');
// 실패시 reject 호출
reject(new Error('no network'));
}, 2000);
});
// 만약에 network 요청을 사용자가 요구했을 때 만 해야 된다면,
즉 사용자가 버튼을 눌렀을 때 네트워크 요청을 해야된다
// 이렇게 작성하게 된다면 사용자가 요청을 하지 않았는데 불필요한 네트워크 통신이 이루어 질 수 있다
// 그래서 프로미스를 만드는순간 그 안에 전달한 excute로 전달한 함수가
바로 실행이 되기 때문에 유의해서 사용해야 한다
02. Consumers: then, catch, finally
- promise가 정상적으로 작동을 한다면 then 그러면 값을 받아올 거야, 값을 받아와서 우리가 원하는 기능을 수행하는 callbackFn를 실행
- promise가 정상적으로 작동을 못했을 경우 catch error을 잡아서 원인을 callbackFn로 실행
- finally는 실행 결과 상관 없이 실행이 된다
promise
.then((value)=>{
// ellie 호출
// resolve로 전달한 data를 value로 갖고 옴
console.log(value);
})
.catch(error=>{
// 만약 error가 나타나면 reject로 전달한 error를 error로 사용
console.log(error)
})
// then에서도 promise를 전달하기 때문에 catch로 에러를 잡을 수 있다
.finally(()=>{
console.log('finally')
})
03. Promise chaining
const fetchNumber = new Promise((resolve, reject)=>{
setTimeout(() => {
resolve(1)
}, 1000);
})
fetchNumber
.then(num=>num*2) // 2
.then(num=>num*3) // 6
.then(num=>{
return new Promise((resolve,reject)=>{
setTimeout(() => {
resolve(num-1) // 5
}, 1000);
})
})
.then(num=>console.log(num)); // 5
// then은 값을 바로 전달 할 수 있고 또 다른 비동기 promise를 전달할 수도 있다
04. Error Handling
const getHen = () => new Promise((resolve, reject) => {
setTimeout(()=>resolve('chicken'), 1000);
});
const getEgg = hen => new Promise((resolve, reject) => {
// setTimeout(()=>resolve(`${hen} => egg`), 1000);
setTimeout(()=>reject(new Error(`error! ${hen} => egg`)), 1000);
});
const cook = egg => new Promise((resolve,reject) => {
setTimeout(()=>resolve(`${egg} => cook`),1000);
});
getHen()
// .then(hen => getEgg(hen))
.then(getEgg(hen))
// 계란을 받아올 때 문제가 생겨서 다른 재료로 대처하고 싶을 때
.catch(error => {
return 'bread'
})
// .then(egg => cook(egg))
.then(cook(egg))
//.then(meal => console.log(meal));
.then(console.log)
// chicken => egg => cook
.catch(console.log);
// * callback 함수를 전달할 때 받아 오는 data를 바로 전달 할 때 생략할 수 있다
// 한가지만 받아서 그대로 전달하면 따로 data를 선언 안해도 된다
05.Callback to Promise
class UserStorage {
loginUser(id, password) {
return new Promise((resolve, reject) => {
setTimeout(()=>{
if(
(id === 'ellie' && password === 'dream') ||
(id === 'coder' && password === 'academy')
) {
resolve(id);
} else {
reject(new Error('not found'))
}
},2000);
})
}
getRoles(user) {
return new Promise((resolve,reject)=>{
setTimeout(()=>{
if (user==='ellie') {
resolve({name : 'ellie', role : 'admin'});
} else {
reject(new Error('no access'));
}
},1000);
})
}
}
// 백엔드 통신 클래스 생성
const userStorage = new UserStorage();
// 아이디 & 비번 받아오기
const id = prompt('enter your id');
const password = prompt('enter your password');
userStorage
.loginUser(id, password)
.then(user => userStorage.getRoles(user))
.then(user => alert(`Hello ${user.name}, yoh have a ${user.role} role`))
.catch(error => console.log(error));
3. async&await
- Promise를 간단하게 사용하게 해주고 동기적으로 실행되는것 처럼 보이게 할 수 있다(ES8도입)
- 기존에 있던 Promise 위에 조금 더 간편한 api를 제공(syntactic sugar)
- Class도 prototype을 base로 한 static sugar이다
- async&await
- clear style of using promise :)
- throw : 에러발생
- try catch : 에러 잡을 때
- promise
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function makeError() {
await sleep(1000);
const error = new Error();
throw error;
}
// 단순 비동기 처리
async function process() {
console.log("안녕하세요!");
// promise 작업 앞에는 await를 붙여 준다
await sleep(1000);
console.log('반갑습니다');
// async를 사용하면 promise를 반환
return true;
}
// error 잡기
async function process() {
try{
await makeError();
}catch(e){
console.error(e);
}
}
process().then(value => {
console.log(value);
});
//에러 호출
process();
01. async
function fetchUser() {
// do network request in 10 secs...
return 'ellie';
}
const user = fetchUser();
console.log(user);
// fn이 호출되고 나서 10초 후 데이터를 받은 다음에야 다음 코드 실행
// Promise
function fetchUser() {
return new Promise((resolve, reject) => {
resolve('ellie');
});
}
const user = fetchUser();
user.then(console.log);
// async
// 함수 앞에 async 키워드를 작성해주면 된다
async function fetchUser() {
return 'ellie';
}
const user = fetchUser();
user.then(console.log);
02. await
- async가 붙은 함수 안에서만 사용 가능
- Promise 앞에 사용
function delay(ms) {
return Promise((resolve, reject) => {
setTimeout(resolve,ms);
})
}
async function getApple(){
await delay(3000);
return 'apple';
}
async function getBanana(){
await delay(3000);
return 'banana';
}
// getBanana promise
function getBanana() {
return delay(3000).then(()=>'banana');
}
// 과일을 다 갖고 오는 함수
// callback HELLLLLLLLLLLLLLLLLLLLLLL
function pickFruits() {
return getApple()
.then(apple => {
return getBanana()
.then(banana => `${apple}+${banana}`)
})
}
pickFruits().then(console.log);
async function pickFruits() {
const apple = await getApple();
const banan = await getBanana();
return `${apple} + ${banana}`;
}
// 위 코드의 문제점은 getApple, getBanana는 각 각 3초간의 시간을 허무함
// apple 3초 후 banana 3초 후 return
// apple과 banana는 연관성이 없는 코드!
pickFruits().then(console.log);
async function pickFruits() {
const applePromise = getApple();
const bananaPromise = getBanana();
const apple = await applePromise;
const banan = await bananaPromise;
return `${apple} + ${banana}`;
}
// Promise는 선언됨과 동시에 실행이 되기 때문에 병렬로 실행 됨
// apple 3초와 함께 banana 3초 조금의 차이는 있지만 총 약 3초의 시간이 걸림
// 하지만 병렬로 사용할 때는 굳이 이 방법을 사용하지 않고 promise APIs를 사용
pickFruits().then(console.log);
03. useful Promise APIs
function pickAllFruits() {
// Promise 배열을 전달하면 모든 promise가 병렬적으로 받을 때 까지 모와줌
return Promise.all([getApple(), getBanana()])
.then(fruits => fruits.join('+'));
}
pickAllFruits().then(console.log);
// 먼저 성공한 promise만 받아 올때
// Promise.race()
function pickOnlyOne() {
return Promise.race([getApple(), getBanana()]);
}
pickOnlyOne().then(console.log);
04. Callback to Async&Await
class UserStorage {
loginUser(id, password) {
return new Promise((resolve, reject) => {
setTimeout(()=>{
if(
(id === 'ellie' && password === 'dream') ||
(id === 'coder' && password === 'academy')
) {
resolve(id);
} else {
reject(new Error('not found'))
}
},2000);
})
}
getRoles(user) {
return new Promise((resolve,reject)=>{
setTimeout(()=>{
if (user==='ellie') {
resolve({name : 'ellie', role : 'admin'});
} else {
reject(new Error('no access'));
}
},1000);
})
}
async getUserWithRole(user, password) {
const getUser = await this.loginUser(user,password);
const getRole = await this.getRoles(getUser);
return getRole;
}
}
// 백엔드 통신 클래스 생성
const userStorage = new UserStorage();
// 아이디 & 비번 받아오기
const id = prompt('enter your id');
const password = prompt('enter your password');
userStorage//
.getUserWithRole(id, password)
.catch(console.log)
.then(console.log);
4. Promise.all & Promise.race
01. promise.all
- 여러개의 promise를 동시에 처리
- promise 중에서 하나라도 오류가 발생하면 전체 오류로 간주
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const getDog = async () => {
await sleep(1000);
return '멍멍이';
}
const getRabbit = async () => {
await sleep(500);
return '토끼';
}
const getTurtle = async () => {
await sleep(3000);
return '거북이';
}
async function process() {
const dog = awit getDog();
console.log(dog);
const rabbit = awiat getRabbit();
console.log(rabbit)
const turtle = awiat getTurtle();
console.log(turtle);
}
//여러개의 promise를 동시에 처리
async function process() {
const start = Date.now();
// 프로미스는 모든 프로미스를 시작하고 끝나면 반환 값에는 각 각 끝난 결과값이 배열로 반환
const result = await Promise.all([getDog(), getRabbit(), getTurtle()]);
console.log(Date.now() - start);
console.log(result);
}
//만약에 promise.all 안에 있는 각 promise의 결과물을 각 각 다른 변수로 꺼낼 때
async function process() {
// 비구조 할당
const [dog, rabbit, turtle] = await Promise.all([getDog(), getRabbit(), getTurtle()]);
//const dog = result[0];
//const rabbit = result[1];
//const turtle = result[2];
console.log(dog);
console.log(rabbit);
console.log(turtle);
}
//promise 중에서 하나라도 오류가 발생하면 전체 오류로 간주
//그러면 작업이 진행되지 않음
async function process() {
try{
const first = await Promise.all([getDog(), getRabbit(), getTurtle()]);
}catch(e){
console.error(e);
}
console.log(first);
}
process();
02. promise.race
- promise.all과 유사하지만 all과 다르게 race는 실행되는 promise 중 가장 빨리 끝난 promise 값만 반환
- 가장 빨리 끝난 promise가 에러일 때만 에러로 간주
const getDog = async () => {
await sleep(1000);
return '멍멍이';
}
const getRabbit = async () => {
await sleep(500);
return '토끼';
}
const getTurtle = async () => {
await sleep(3000);
return '거북이';
}
async function process() {
//race는 실행되는 promise 중 가장 빨리 끝난 promise 값만 반환
const first = await Promise.race([getDog(), getRabbit(), getTurtle()]);
console.log(first);
}
//가장 빨리 끝난 promise가 에러일 때만 에러로 간주
//그 이후의 에러는 안 잡음
async function process() {
try{
const first = await Promise.race([getDog(), getRabbit(), getTurtle()]);
}catch(e){
console.error(e);
}
console.log(first);
}
process();