ShimSeongbo / study

0 stars 0 forks source link

[Websquare] Promise 기술이 적용된 공통함수들 #9

Open ShimSeongbo opened 1 year ago

ShimSeongbo commented 1 year ago

웹스퀘어에서 Promise로 구현된 공통 함수들

image

Promise란?

비동기 작업의 최종 완료 또는 실패를 나타내는 객체

기본적으로 promise는 함수에 콜백을 전달하는 대신에, 콜백을 첨부하는 방식의 객체이다.

비동기로 음성 파일을 생성해주는 createAudioFileAsync() 라는 함수가 있다고 생각해보면, 해당 함수는 음성 설정에 대한 정보를 받고, 두 가지 콜백 함수를 받는다.

[클래식]

function successCallback(result) {
  console.log("Audio file ready at URL: " + result);
}

function failureCallback(error) {
  console.log("Error generating audio file: " + error);
}

createAudioFileAsync(audioSettings, successCallback, failureCallback);

[모던한 함수]

createAudioFileAsync(audioSettings).then(successCallback, failureCallback);

[더 간단한 함수]

const promise = createAudioFileAsync(audioSettings);
promise.then(successCallback, failureCallback);

위와 같은 것을 비동기 함수 호출이라고 부른다.

Guarantees

콜백 함수를 전달해주는 고전적인 방식과는 달리, Promise 는 아래와 같은 특징을 보장한다.

Chaining

보통 두 개 이상의 비동기 작업을 순차적으로 실행해야 하는 상황을 흔히 보게 된다.

순차적으로 각각의 작업이 이전 단계 비동기 작업이 성공하고 나서, 그 결과값을 이용하여 다음 비동기 작업을 실행해야 하는 경우를 의미하는데, 이런 상황에서 Promise chain을 이용하여 해결하기도 한다.

then() 함수는 새로운 Promise 를 반환한다. 처음 만들었던 Promise 와는 다른 새로운 Promise 임.

const promise = doSomething();
const promise2 = promise.then(successCallback, failureCallback);
const promise2 = doSomething().then(successCallback, failureCallback);

[지옥의 콜백 피라미드]

doSomething(function (result) {
  doSomethingElse(
    result,
    function (newResult) {
      doThirdThing(
        newResult,
        function (finalResult) {
          console.log("Got the final result: " + finalResult);
        },
        failureCallback,
      );
    },
    failureCallback,
  );
}, failureCallback);

[모던한 방식]

doSomething()
  .then(function (result) {
    return doSomethingElse(result);
  })
  .then(function (newResult) {
    return doThirdThing(newResult);
  })
  .then(function (finalResult) {
    console.log("Got the final result: " + finalResult);
  })
  .catch(failureCallback);

then 에 넘겨지는 인자는 선택적(optional)입니다. 그리고 catch(failureCallback)then(null, failureCallback) 의 축약입니다. 이 표현식을 화살표 함수로 나타내면 다음과 같다.

doSomething()
  .then((result) => doSomethingElse(result))
  .then((newResult) => doThirdThing(newResult))
  .then((finalResult) => {
    console.log(`Got the final result: ${finalResult}`);
  })
  .catch(failureCallback);

중요: 반환값이 반드시 있어야 함.

Chaining after a catch

chain에서 작업이 실패한 후에도 새로운 작업을 수행하는 것이 가능

new Promise((resolve, reject) => {
  console.log("Initial");

  resolve();
})
  .then(() => {
    throw new Error("Something failed");

    console.log("Do this");
  })
  .catch(() => {
    console.log("Do that");
  })
  .then(() => {
    console.log("Do this, whatever happened before");
  });
ShimSeongbo commented 1 year ago

[Reference] https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Using_promises#guarantees

ShimSeongbo commented 1 year ago

Promise 는 다음 중 하나의 상태를 가진다.

promises

ShimSeongbo commented 1 year ago

시나리오 [언어 이해도 Javascript < Java]

Promise 함수를 Java언어로 설명해야한다면?

시나리오

웹 서버에서 사용자의 프로필을 가져오는 작업과 그 사용자의 최근 게시글을 가져오는 작업이 있습니다. 두 작업은 독립적이며, 서로에게 의존하지 않습니다. 그렇기 때문에 이 두 작업을 병렬로 실행하여 전체 응답 시간을 줄일 수 있습니다.

이를 비동기적으로 처리하지 않는다면, 사용자 프로필을 먼저 가져온 다음 게시글을 가져와야 하므로 전체 작업 시간은 두 작업의 시간 합이 됩니다. 반면에 두 작업을 병렬로 실행하면 전체 작업 시간은 두 작업 중 더 오래 걸리는 시간이 됩니다.

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

public class AsynchronousExample {

    public static String fetchUserProfile() {
        // 무작위로 1~3초 사이의 지연을 시뮬레이션
        randomDelay();
        return "User Profile";
    }

    public static String fetchUserPosts() {
        // 무작위로 1~3초 사이의 지연을 시뮬레이션
        randomDelay();
        return "User Posts";
    }

    private static void randomDelay() {
        int delay = ThreadLocalRandom.current().nextInt(1, 4);
        try {
            TimeUnit.SECONDS.sleep(delay);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    public static void main(String[] args) {
        long start = System.nanoTime();

        CompletableFuture<String> userProfileFuture = CompletableFuture.supplyAsync(AsynchronousExample::fetchUserProfile);
        CompletableFuture<String> userPostsFuture = CompletableFuture.supplyAsync(AsynchronousExample::fetchUserPosts);

        CompletableFuture<Void> combinedFuture = userProfileFuture.thenAcceptBoth(userPostsFuture, (profile, posts) -> {
            System.out.println(profile);
            System.out.println(posts);
        });

        combinedFuture.join();

        long duration = (System.nanoTime() - start) / 1_000_000_000;
        System.out.println("Completed in " + duration + " seconds");
    }
}

설명

fetchUserProfile 및 fetchUserPosts 메서드는 각각 사용자 프로필 및 게시글을 가져오는 데 시간이 걸리는 작업을 시뮬레이션합니다. randomDelay 메서드는 1~3초 사이의 무작위 지연을 생성하여 실제 네트워크 또는 데이터베이스 호출의 지연을 시뮬레이션합니다. main 메서드에서는 두 작업을 비동기적으로 시작합니다. CompletableFuture.supplyAsync를 사용하여 각 작업을 별도의 스레드에서 실행시킵니다. thenAcceptBoth 메서드는 두 작업이 모두 완료될 때 실행됩니다. 여기에서는 각 작업의 결과를 출력합니다. 마지막으로 전체 작업의 실행 시간을 출력합니다. 이 예제를 실행하면 두 작업이 병렬로 실행되기 때문에 전체 작업 시간이 더 짧아집니다.