콜백함수와 promise, 모나드

boyeonJ·2023년 8월 2일
1

콜백함수와 promise

콜백함수에 비해서 promise가 더 간단하게 구현할 수 있다.

  function add10(a, callback) {
    setTimeout(() => callback(a + 10), 100);
  }

  var a = add10(5, res => {
    add10(res, res => {
      add10(res, res => {
        // log(res);
      });
    });
  });

  // log(a);

  function add20(a) {
    return new Promise(resolve => setTimeout(() => resolve(a + 20), 100));
  }

  var b = add20(5).then(add20).then(add20);

promise의 큰 장점

비동기 상황을 일급 값으로 다룬다는 점이다.

promise라는 클래스로 만들어진 인스턴스를 반환한다. 그 promise는 대기, 성공, 실패를 다루는 일급값으로 이루어져 있다.(code나 context로만 다루지 않는다)

프로미스는 객체로서 비동기 작업의 현재 상태를 나타내며, 성공하거나 실패한 결과 🌟🌟값🌟🌟을 가지고 있습니다. 이렇게 프로미스는 다른 함수에게 인자로 넘기거나, 변수에 할당하거나, 반환 값으로 사용할 수 있습니다. 이를 통해 콜백 헬을 피하고 비동기 코드를 보다 간결하고 가독성 좋게 작성할 수 있습니다.

new Promise(resolve => ...);: Promise 객체를 생성합니다. Promise는 비동기 작업의 결과를 처리하기 위해 사용됩니다. Promise 생성자는 하나의 콜백 함수를 인자로 받으며, 이 콜백 함수는 resolve와 reject를 인자로 가집니다.

promise의 일급값 활용하기

const delay100 = a =>
new Promise(resolve => setTimeout(() => resolve(a), 100));

const go1 = (a, f) => (a instanceof Promise ? a.then(f) : f(a));
const add5 = a => a + 5;

const n1 = 10;
// log(go1(go1(n1, add5), log));

const n2 = delay100(10);
// log(go1(go1(n2, add5), log));

promise로 함수 합성하기

비동기 상황에서 promise는 함수를 합성하는 도구로 활용할 수 있다.

모나드

모나드(Monad)는 함수형 프로그래밍에서 사용되는 개념으로, 순수 함수형 언어에서 부수 효과(side effect)를 다루는 데 유용한 방법을 제공합니다. 모나드는 또한 비동기 작업이나 예외 처리와 같은 복잡한 연산을 간결하게 다룰 수 있도록 도와줍니다.
모나드는 아래의 세 가지 기본적인 구성 요소로 이루어져 있습니다:
1. 값을 감싸는(래핑하는) 컨테이너: 모나드는 값을 감싸는 컨테이너로서 동작합니다. 이 컨테이너에는 값 자체 또는 값을 가공하는 함수가 들어있습니다.
2. 값을 변환하는 연산자: 모나드는 값을 컨테이너에서 추출하여 변환하는 연산자를 제공합니다. 이로 인해 함수형 코드에서 값의 변환과 가공이 간편해집니다.
3. 연속된 계산(순서 조합): 모나드는 연속된 계산을 표현하는데 도움이 됩니다. 여러 개의 모나드를 연결하고 값을 가공하면서 순서대로 작업을 수행할 수 있습니다.
예를 들어, 자바스크립트에서 Promise는 모나드의 한 예입니다. Promise는 비동기 작업의 결과를 다루는데 사용되며, 값을 감싸는 컨테이너로 작동하며 then 메서드를 사용하여 값을 변환하고 연속된 비동기 작업을 순서대로 수행할 수 있게 해줍니다.
모나드는 함수형 프로그래밍에서 중요한 개념이지만 처음에는 다소 낯설게 느껴질 수 있습니다. 하지만 모나드를 이해하면 함수형 코드를 더욱 간결하고 명확하게 작성할 수 있으며, 부수 효과를 피하고 안정성을 높이는 데 도움이 됩니다.

// f . g
// f(g(x))

const g = a => a + 1;
const f = a => a * a;

// log(f(g(1)));
// log(f(g()));


[1,2].map(g).map(f).forEach(r => log(r));
//이게 실행안되는게 중요...?
[].map(g).map(f).forEach(r => log(r));


Array.of(1).map(g).map(f) /*.forEach(r => log(r))*/;
[].map(g).map(f) /*.forEach(r => log(r))*/;

Promise.resolve(2).then(g).then(f) /*.then(r => log(r))*/;
new Promise(resolve => setTimeout(() => resolve(2), 100))
  .then(g)
  .then(f) /*.then(r => log(r))*/;

Kleisli

// f . g
// f(g(x)) = f(g(x))
// f(g(x)) = g(x)

var users = [
  { id: 1, name: 'aa' },
  { id: 2, name: 'bb' },
  { id: 3, name: 'cc' },
];

const getUserById = id =>
find(u => u.id == id, users) || Promise.reject('없어요!');

const f = ({ name }) => name;
const g = getUserById;

// const fg = id => f(g(id));

const fg = id =>
Promise.resolve(id)
.then(g)
.then(f)
.catch(a => a);

fg(2).then(log);

setTimeout(function () {
  users.pop();
  users.pop();
  fg(2).then(log);
}, 10);

문제풀이

1/2번. CallBack vs Promise
CallBack은 비동기 작업을 코드로서 다루고, Promise는 값으로서 다룹니다. 따라서 Promise는 작업의 현재상태를 인자로 넘기거나 변수에 할당하거나 반환값으로 사용하여 간결하고 가독성 좋게 작성할 수 있습니다.

  function add10(a, callback) {
    setTimeout(() => callback(a + 10), 100);
  }

  add10(5, res => {
    add10(res, res => {
      add10(res, res => {
        log(res);
      });
    });
  });

  function add20(a) {
    return new Promise(resolve => setTimeout(() => resolve(a + 20), 100));
  }

  add20(5).then(add20).then(add20).then(log);

3번. go

const delay100 = a =>
new Promise(resolve => setTimeout(() => resolve(a), 100));

const go1 = (a, f) => (a instanceof Promise ? a.then(f) : f(a));
const add5 = a => a + 5;

const n1 = 10;
go1(go1(n1, add5), log)
// log(go1(go1(n1, add5), log)); 값

const n2 = delay100(10);
go1(go1(n2, add5), log)
// log(go1(go1(n2, add5), log)); Promise

4번. 모나드는 무엇인가?
함수 합성을 안전하게 해준다.(어떤 특정 상황에서)
-배열은 안에 값이 있는지를 확인하는것
-promise는 비동기 상황을 확실하게

5번. 모나드를 배열로 구현하기

[1,2].map(g).map(f).forEach(r => log(r));
//이게 실행안되는게 중요...?
[].map(g).map(f).forEach(r => log(r));

6번. 모나드를 Promise로 구현하기

Array.of(1).map(g).map(f) /*.forEach(r => log(r))*/;
[].map(g).map(f) /*.forEach(r => log(r))*/;

Promise.resolve(2).then(g).then(f) /*.then(r => log(r))*/;
new Promise(resolve => setTimeout(() => resolve(2), 100))
  .then(g)
  .then(f) /*.then(r => log(r))*/;

7번. Kleisli Composition는 무엇인가?
8번. Kleisli arrow로 합성해보는 구현하기

// f . g
// f(g(x)) = f(g(x))
// f(g(x)) = g(x)

var users = [
  { id: 1, name: 'aa' },
  { id: 2, name: 'bb' },
  { id: 3, name: 'cc' },
];

const getUserById = id =>
find(u => u.id == id, users) || Promise.reject('없어요!');

const f = ({ name }) => name;
const g = getUserById;

// const fg = id => f(g(id));

const fg = id =>
Promise.resolve(id)
.then(g)
.then(f)
.catch(a => a);

fg(2).then(log);

setTimeout(function () {
  users.pop();
  users.pop();
  fg(2).then(log);
}, 10);

1개의 댓글

comment-user-thumbnail
2023년 8월 2일

좋은 정보 감사합니다

답글 달기