[TIL 2021.10.13] js.callback hell

45147 ワード

Today I Learned
youtube dream coding - js 10,11,12
Callback function, Promise
JSON to String and String to JSON in JS

地獄体験

'use strict';

// 자바스크립트는 동기적이다.
// 호이스팅 이후에 코드가 순서에 맞춰서 하나씩 동기적으로 실행된다.
// 호이스팅이란 var 변수와 함수 선언이 자동으로 제일 위로가는것을 호이스팅이라 한다.
console.log('1');
setTimeout(() => console.log('2'), 1000);
// 비동기적인 실행 나중에 실행하니까 콜백

console.log('3');

// 콜백은 항상 비동기일때만?
// ㄴㄴ
// 즉각적으로 실행하는 Synchronous callback
function printImmediately(print) {
    print();
} // 이 함수는 호이스팅되어서 실제로는 제일 위에 선언 되었을 것이다.

printImmediately(() => console.log("hello"));

// 나중에 언제실행할지 예측되지않는 Asynchronous callback
function printWithDelay(print, timeout) {
    setTimeout(print, timeout);
}

printWithDelay(() => console.log('async callback'), 2000);

/**
 * 자바스크립트는 콜백형태로 다른 인자에 전달할수도있고 변수에 할당할수도있다.
 * 언어마다 다르다. 서브루틴, 람다식, 함수포인터를 활용하는 방법도 있다.
 * 자바스크립트에서는 콜백함수를 위와 같이 활용한다.
 */

// -----------------------콜백 지옥 체험------------------------- //

class UserStorage { // 백엔드에서 데이터 불러오는 척하는 예제 클래스
    loginUser(id, password, onSuccess, onError) {
        setTimeout(() => {
            if (
                (id === 'kyu' && password === '1234') ||
                (id === 'code' && password === 'squad')
            ) {
                onSuccess(id);
            } else {
                onError(new Error('not found'));
            }
        }, 2000); // 로그인 하는데 2초 걸린다고 생각해보자
    }

    getRoles(user, onSuccess, onError) {
        setTimeout(() => {
            if (user === 'kyu') {
                onSuccess({name: 'kyu', 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 => {

            })
    },
    (error) => {
    }
);

/**
 * 콜백 지옥의 문제점
 * - 비즈니스 로직을 한번에 파악하기가 힘듦
 * - 에러가 발생하거나 디버깅할때 해결하기가 힘듦
 * - 내가 했던게 콜백지옥이었다는 것
 */

/**
 * 해결법 - Promise
 */

Promise

'user strict';

// Promise는 Asynchronous operation을 위한 자바스크립트 객체이다
// State: pending -> fulfilled or rejected
// Producer vs Consumer

// 1. Producer
// Promise가 만들어지면 excutor가 자동으로 실해된다.
const promise = new Promise((resolve, reject) => {
// doing some heavy work
// 시간이 걸리는건 비동기적으로 해주는게 좋다. (네트워크, 파일 읽기)
    console.log('doing something...');
    setTimeout(() => {
        // resolve('kyu');
        reject(new Error('no network')); // Error는 자바스크립트 제공
        /**
         * 어떤 작업 이후에 성공하면 resolve 함수,
         * 실패하면 reject(new Error...)를 반환한다.
         * 반환한 것은 아래 Consumers 에서 받아 처리한다.
         */

    }, 2000); 
});

// 2. Consumers: then, catch, finally
promise
    .then((value) => {
        console.log(value);
    })
    .catch(error => {
        console.log(error);
    })
    .finally(() => console.log('finally')
);
    

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

fetchNumber
    .then(num => num * 2)
    .then(num => num * 3)
    .then(num => { // then에서는 값을 전달하거나 또다른 비동기인 Promise를 전달할수있다.
        return new Promise((resolve, reject) => {
            setTimeout(() => resolve(num - 1), 1000);
        });
    })
    .then(num => console.log(num));

    // 4. 에러 핸들링

const getHen = () =>
    new Promise((resolve, reject) => {
        setTimeout(() => resolve('🐓'), 1000);
    });
const getEgg = hen =>
    new Promise((resolve, reject) => {
        setTimeout(() => resolve(`${hen}=> 🥚`), 1000);
    }); 
const cook = egg =>
    new Promise((resolve, reject) => {
        setTimeout(() => resolve(`${egg} => 🍳`, 1000));
    });

getHen()
    .then(hen => getEgg(hen)) // -> .then(getEgg) 받아서 바로 함수애 넣는 경우
    // .catch(error => {
    //     return '🥖';
    // }) reject() 된 것을 바로 처리할수 있도록 catch 할 수 있음
    .then(egg => cook(egg)) // -> .then(cook)
    .then(meal => console.log(meal)) // -> .then(console.log)
    .catch(console.log);

Promiseでコールバック地獄を解決

'use strict';

class UserStorage { // 백엔드에서 데이터 불러오는 척하는 예제 클래스
    loginUser(id, password) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                if (
                    (id === 'kyu' && password === '1234') ||
                    (id === 'code' && password === 'squad')
                ) {
                    resolve(id);
                } else {
                    reject(new Error('not found'));
                }
            }, 2000); // 로그인 하는데 2초 걸린다고 생각해보자
        });
    }

    getRoles(user) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                if (user === 'kyu') {
                    resolve({ name: 'kyu', 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(userStorage.getRoles)
    .then(user => alert(`Hello ${user.name}, you have a ${user.role} role`))
    .catch(console.log);

/**
 * 아까 callback 안 callback 에서는 가독성이 매우 떨어졌지만
 * Promise 를 사용하면서 간결하고 가독성이 좋아졌다.
 */

JSONについて

//JSON

// 1. Object to JSON
// stringify(obj)
let json = JSON.stringify(true); // boolean 타입도 변환 가능
console.log(json);

json = JSON.stringify(['apple', 'banana']);
console.log(json);

const rabbit = {
    name: "less",
    color: 'white',
    size: null,
    birthDate: new Date(),
    jump: () => {
        console.log(`can jump!`);
    } // 함수는 JSON에 포함되지 않는것을 밑에 로그함수에서 확인 가능
}

json = JSON.stringify(rabbit);
console.log(json);

json = JSON.stringify(rabbit, ["name", 'color']);
console.log(json);

console.clear();

json = JSON.stringify(rabbit, (key, value) => {
    console.log(`key ${key}, value: ${value}`);
    return key === 'name' ? 'kyu' : value;
})
console.log(json);

// 2. JSON to Object
// parse(json)

console.clear();
json = JSON.stringify(rabbit);
const obj = JSON.parse(json);
console.log(obj);
rabbit.jump();
// obj.jump();

console.log(rabbit.birthDate.getDate());