Javascript7(비동기처리) feat.Ellie&velopert

min seung moon·2021년 3월 6일
0

Javascript

목록 보기
8/23

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
      1. state, 상태, 프로세스가 무거운 오퍼레이션을 수행하고 있는 중인지 아니면 기능을 수행을 성공했는지 실패했는지 catch
      1. 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

  • Promise.all([...])
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();
profile
아직까지는 코린이!

0개의 댓글