head 안에 스크립트 태그를 이용해 js 파일을 넣으면, 왜인지는 몰라도 웹페이지에서 JS 를 사용할 수가 없다.
따라서 바디가 다 준비된 다음에 js 를 다운받을 수 있도록 body의 마지막에 script 를 넣는 경우가 많음 근데 ㅣㅇ거도 단점이 있음.
사용자가 기본적인 html을 빨리 볼 수는 있지만 JS 요소가 많은 페이지라면 사용자가 JS 를 다운받기 전까지는 망가져있는 페이지를 볼수밖에 없음
head 안에 script 를 넣으면서 async 옵션을 넣는것. 이렇게 되면 병렬로 js를 다운받으면서 body 를 계속 파싱한다. js 다운이 끝나면 js 를 실행시키는 동안 파싱작업은 잠깐 멈추고 실행 준비가 끝나면 다시 html 을 파싱한다. 이것도 문제가 있는데, 바디 끝에 사용하는 것보다는 병렬적으로 일어나기 때문에 시간은 절약할 수 있지만, html 이 다 파싱되기전에 JS 가 다운로드 및 실행이 되어서 페이지내에서 어떤 요소를 찾아야하는데 그 요소가 아직 파싱이 안되서 에러가 발생할 수 있음.
defer 옵션: head 안에 스크립트를 넣고 defer 옵션을 넣으면 html 을 파싱하면서 js를 발견하면 다운로드 받자 명령만 시켜넣고 나머지 html을 끝까지 파싱한다. 그리고 파싱이 끝나면 그제서야 병렬로 다운로드 받아진 js 를 실행시킨다 이 방법이 가장 좋다!
async로 다수의 js 파일을 포함시키면 정의된 순서에 상관없이 다운로드 된 순서로 실행이 되기 때문에 동기에 있어서 문제가 생긴다.
반대로 defer 을 사용하면 html 파싱이 모두 끝난 후 선언 된 순서로 실행이 되기 때문에 예상 가능하다. 안전하다.
자바스크립트 클래스 알아보기
자바스크립트 배열 제대로 사용하기
map
reduce
some
every
find
등등 멋진 함수가 있다.
vscode 에서 함수 구현까지 이동할 수 있으니깐 구현 되어있는 모습도 살펴보자
자바스크립트의 제이슨
JSON.stringfy // obj 의 스틍링화JSON.parse //json 을 objconstrabbit= { name:'tori', color:'white', size:3, birthDate:newDate(),jump: () => {console.log(`${name} can jump!`); },}json =JSON.stringify(rabbit)console.clear();constobj=JSON.parse(json, (key, value) => {if (key =="birthDate") value =console.log(`key: ${key}, value: ${value}`);return key ==="birthDate"?Date(value) : value;});console.log(json);console.log(obj);
위처럼 Date 데이터를 JSON.stringfy 했다가 parse 하면 문자열형태로 되어있다. 이건 우리가 원하는 형태가 아니므로 타입을 바꿔주도록하자. 어떻게? 콜백함수로 ^_^
프로미스
프로미스 객체는 new 로 생성되자마자 인자로 건네진 executor 함수가 바로 실행된다. 따라서 원치않는 실행으로 부하가 많이 걸릴 수 있으니 주의하자.
인자로 건네주는 resolve와 reject 는 겉보기로만 있는건가?
executor 의 성공 실패 여부에 따라 resolve, reject 함수를 따로 실행시켜주면 된다.
resolve 가 됐을 때는 promise.then 을 이용해서 처리하고 reject 됐을 때는 catch 로 잡아서 처리한다. 그리고 성공 실패 모두에서 실행되어야 할 코드가 있다면 finally 로 잡아서 처리하면 된다.
주의할게 then 으로 return 되는 객체 또한 promise 객체이기 때문에 then 을 몇 번씩 엮어서 사용할 수도 있다.
아래와 같은 콜백 지옥 함수가 있을 때 이를 프로미스로 해결해보자
// 1. 콜백지옥classUserStorage {loginUser(id, password, onSuccess, onError) {setTimeout(() => {if ( (id ==='ellie'&& password ==='dream') || (id ==='coder'&& password ==='academy') ) {console.log("successs");onSuccess(id); } else {console.log("fail");onError(newError('not Found')); } },2000); }getRoles(user, onSuccess, onError) {setTimeout(() => {if (user ==='ellie') {onSuccess({ name:'ellie', role:'admin' }); } else {onError(newError('no Access')); } },1000); }}constuserStorage=newUserStorage();constid=prompt('enter your id');constpassword=prompt('enter your password');userStorage.loginUser( id, password, user => {userStorage.getRoles( user, userWithRole => {alert(`Hello ${userWithRole.name}, you have a ${userWithRole.role} role`) }, erorr => { console.log("error"); } ) }, error => { console.log(error); })
// 프로미스 사용classUserStorage {loginUser(id, password) {returnnewPromise((resolve, reject) => {setTimeout(() => {if (id ==='ellie'&& password ==='dream') {resolve(id); } else {reject(newError("No ID!!")); } },2000); }) }getRoles(user) {returnnewPromise((resolve, reject) => {setTimeout(() => {if (user ==='ellie') {resolve({ name:'ellie', role:'admin' }); } else {reject(newError('No Access')); } },1000); }) }}constuserStorage=newUserStorage();constid=prompt('enter your id');constpassword=prompt('enter your password');userStorage.loginUser(id, password).then(userStorage.getRoles) // resolve 인자 하나를 받아서 getRoles 에 그 인자를 바로 넣을 때는 생략가능.then((userWithRole => {alert(`Hello ${userWithRole.name}, you have a ${userWithRole.role} role`) }))
async 와 await
syntatic sugar. 새롭게 만들어진게 아니라 프로미스를 사용하기 쉽게 문법적으로 만든 APIs
async
함수 앞에 async 를 사용하면 된다. 그러면 함수는 자연스럽게 Promise 를 리턴하게 된다.
asyncfunctionfetchUser() {// 만약 이게 실행시간이 10초라면...return'ellie';}
await
async 함수 내부에서만 사용된다. 이 키워드가 사용된 라인은 실제로 해당 라인이 리턴될 때 까지 기다린다.