logo image
Published on

JS 에서 async 함수 다루기 #2 : async.series & async-series

Authors
  • avatar
    Name
    Cheesepaninim
    Twitter

async.series

'async 라이브러리 사이트 이미지'

오늘은 async.series 라이브러리를 소개하려고 한다.

원래는 저번 포스팅에 이이서 개인적으로 만든 라이브러리1를 포스팅할까 했지만 npm 배포까지 진행 후에 하려고 아껴두기로 했다.

async-series 는 지난 포스팅 - JS에서 async 함수 다루기 #1 에서 소개한 async-waterfall 과 마찬가지로 비동기 함수를 편하게 다룰 수 있도록 도와주는 라이브러리이다.

사실은 아래에서 소개할 async-series 를 얘기할 예정이었는데, 내가 착각을 한건지 기억하고 있던 사용법이 async.series 였다.

다만, https://caolan.github.io/async/v3/ 이 곳을 둘러보니 async 관련해서 쓸 수 있는 함수가 다양하게 구성되어 있는 것 같아 한 번 살펴보면 좋을 것 같다.🎁

async.series 사용 예제

$ npm i async
// or
$ yarn add async

우선 npm 또는 yarn 을 이용해 설치를 진행한다.

이번엔 기업의 채용 프로세스를 예시로 들어보자.

A 기업의 채용 과정은 아래 순서로 진행된다.

  1. 서류
  2. 기술 면접
  3. 입원 면접
  4. 채용 - 계약서 작성

1 ~ 3까지의 결과는 불합격만 되지 않는다면 다음 과정에서 영향이 없다. 어떤 단계에서 불합격 될 경우 그 다음 과정이 진행되지 않는다. 1 ~ 3번의 평가는 4 에서 계약서 작성 시 연봉의 판단 기준이 된다.

위와 같이 진행된다고 할 때 1,2,3 의 과정은 tasks 가 될 것이고 4 는 callback 이 될 것이다.

예제

import series from 'async/series'

const time_1 = 100 * 3
const time_2 = 100 * 2
const time_3 = 100 * 5
const docTest = (callback) => {
    setTimeout(() => {
        const score = 21 // 점수
        const isPass = score >= 15 // 합격여부
        console.log(`pass docTest!`)
        callback(isPass ? null : 'fail from docTest!', score)
    }, time_1)
}

const techTest = (callback) => {
    setTimeout(() => {
        const score = 30 // 점수
        const isPass = score >= 30 // 합격여부
        console.log(`pass techTest!`)
        callback(isPass ? null : 'fail from techTest!', score)
    }, time_2)
}

const executiveTest = (callback) => {
    setTimeout(() => {
        const score = 33 // 점수
        const isPass = score >= 25 // 합격여부
        console.log(`pass executiveTest!`)
        callback(isPass ? null : 'fail from executiveTest!', score)
    }, time_3)
}

const recruit = () => {
    series([docTest, techTest, executiveTest], (err, result) => {
        err ? console.log(`err : ${err}`) : console.log(result) // [21, 30, 33]
    })
}

recruit()

지난 포스팅에서 소개했던 async-waterfall 과 매우 유사하지만 두 가지가 다르다.

  1. task 에서 받는 인자가 callback 하나로 고정되어 있다. 순차적으로만 실행될 뿐 의존성이 없고 다음 task 에 값을 전달하지 못한다. 그래서 async-waterfall 보다 더 단순해보이고 사용하기 편한 것 같다.

  2. 에러 없이 정상 동작되어 callback 함수가 실행 될 때 result 에서 모든 task 가 전달한 값을 배열 형태로 받는다. waterfall 에서는 callback 에서 전달한 값들이(에러 제외) 다음 task 로 전달되었다면, series 에서는 callback 함수의 result 배열에 하나하나 쌓인다는 점이다.

waterfall 과 series 는 사용해야되는 케이스가 다르니 각각 상황에 맞추어 쓰면 될 것 같다. 2



async-series

async-series 라이브러리 사이트 이미지


Run a series of callbacks in sequence, as simply as possible.

async-series 는 소개에도 설명되었듯이 async 함수의 순차적 실행만을 목적으로 가능한 간단히 만들었다고 한다.3

원래 소개하려했던 series 라이브러리의 이름이 async-series 였는데 기억하고 있던 사용법이 달라서 아래로 내렸다.

series([...tasks], callback)

async-series 역시 done 이라는 argument 이름으로 예시를 들었지만 waterfall 과 마찬가지로 callback 을 각각의 task 에서 받아 원하는 시점에 호출하도록 설계되어 있다.

간단히 설명하면, 아래와 같이 진행된다.

  1. tasks 의 함수를 순차적으로 실행
  2. done 함수 호출 시 다음 task 함수 실행
  3. done 함수에 값 전달 시 다음 task 로 넘어가지 않고 callback 함수 실행 (Error 객체를 전달하지 않아도 에러로 인식)
  4. done 함수에 값이 전달되지 않으면 마지막 task 까지 실행 후 callback 함수 실행
series(
    [
        function (done) {
            console.log('first thing')
            done()
        },
        function (done) {
            console.log('second thing')
            done(new Error('another thing'))
        },
        function (done) {
            // never happens, because "second thing"
            // passed an error to the done() callback
        },
    ],
    function (err) {
        console.log(err.message) // "another thing"
    }
)

각각의 task 에서 값을 마지막 callback 으로 값을 전달하지 못하는 부분이 아쉬울 순 있지만 task 구조나 작성이 매우 단순하고 편리해 보인다.

특정 스코프 내에서 지역변수를 공유해도 될 것 같고 아니면 정말 단순히 순차적 실행을 보장하는 정도로만 사용한다면 간단히 편리하게 쓰기 좋은 것 같다.

Footnotes

  1. 라이브러리라고 부르기엔 아직 보잘 것 없지만..

  2. 이 외에도 async parallel 도 자주 썼는데 요즘엔 개인적으로 Promise.all 을 자주 사용했다.

  3. 이 라이브러리도 무려 9년 전에 개발되었으나 여전히 많이 쓰이고 있다.