Async: 콜백이 μ—†λŠ” ν•¨μˆ˜λ₯Ό μ•½μ†μœΌλ‘œ 처리

에 λ§Œλ“  2019λ…„ 07μ›” 09일  Β·  12μ½”λ©˜νŠΈ  Β·  좜처: caolan/async

ν˜„μž¬ λ™μž‘μ€ 맀우 λ†€λžμŠ΅λ‹ˆλ‹€.

await async.parallel({foo: async()=>Promise.all([1,2])})
// { foo: [ 1, 2 ] }
await async.parallel({foo: ()=>Promise.all([1,2])})
// waits forever because non-async function expects a ballback

짧은 ν•¨μˆ˜μ˜ 경우 foo: () => doSomething() μž‘μ„±ν•˜κ³  싢을 수 μžˆμŠ΅λ‹ˆλ‹€. async ν‚€μ›Œλ“œλ₯Ό μžŠμ–΄λ²„λ¦¬κΈ°κ°€ 맀우 μ‰½μŠ΅λ‹ˆλ‹€.

κ·ΈλŸ¬λ‚˜ ν˜„μž¬ 이것은 μž‘λ™ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. foo: async () => doSomething() 둜 μž‘μ„±ν•΄μ•Ό ν•©λ‹ˆλ‹€.

ν•¨μˆ˜κ°€ AsyncFunction인지 ν™•μΈν•˜λŠ” λŒ€μ‹  콜백이 μ „λ‹¬λ˜μ—ˆλŠ”μ§€ 확인할 수 μžˆμŠ΅λ‹ˆκΉŒ?

wont fix

κ°€μž₯ μœ μš©ν•œ λŒ“κΈ€

이 λ¬Έμ œκ°€ μ’…λ£Œλœ μƒνƒœμ—μ„œλ„ μœ„μ˜ 링크된 λ¬Έμ œμ™€ 같이 μ—¬κΈ°μ„œ @caub 문을 λ‹€μ‹œ κ°•μ‘°ν•˜κ³  μ‹ΆμŠ΅λ‹ˆλ‹€.
ν˜„μž¬μ˜ 행동은 λ‚˜λ₯Ό 많이 ν˜Όλž€μŠ€λŸ½κ²Œ ν–ˆκ³  μ—¬κΈ°μ„œ λ…Όμ˜λœ 기술적 ν•œκ³„λ₯Ό (μΌμ’…μ˜) μ΄ν•΄ν•˜μ§€λ§Œ λ¬Έμ„œκ°€ λ‹¨μˆœνžˆ ν˜„μž¬μ˜ 행동과 μΌμΉ˜ν•˜μ§€ μ•ŠλŠ”λ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€.
예λ₯Ό λ“€μ–΄ mapLimit의 경우 "λ°˜ν™˜: 콜백이 μ „λ‹¬λ˜μ§€ μ•Šμ€ 경우 약속"이라고 ν‘œμ‹œλ©λ‹ˆλ‹€. - μ €λŠ” 콜백 λ²„μ „μœΌλ‘œ μ‹œμž‘ν•œ λ‹€μŒ 기본적으둜 μ½œλ°±μ„ μƒλž΅ν–ˆκ³  약속을 얻지 λͺ»ν•œ 것에 λ†€λžμŠ΅λ‹ˆλ‹€. '비동기' ν‚€μ›Œλ“œ).
λ˜ν•œ iteratee ν•¨μˆ˜μ˜ 'callback' 문은 ν•„μš”ν•˜μ§€ μ•ŠμœΌλ©° λŒ€μ‹  값을 'λ°˜ν™˜'ν•΄μ•Ό ν•©λ‹ˆλ‹€.
λ”°λΌμ„œ ν˜Όλ™μ„ ν”Όν•˜κΈ° μœ„ν•΄ λ™μž‘μ„ λ³€κ²½(μ„ ν˜Έ)ν•˜κ±°λ‚˜ μ΅œμ†Œν•œ λ¬Έμ„œμ™€ 예제λ₯Ό λ‚΄ κ΄€μ μ—μ„œ μ •λ ¬ν•΄μ•Ό ν•©λ‹ˆλ‹€.

λͺ¨λ“  12 λŒ“κΈ€

ν•¨μˆ˜κ°€ 약속을 λ°˜ν™˜ν•˜λŠ”μ§€ 여뢀와 μ½œλ°±μ„ 미리 전달해야 ν•˜λŠ”μ§€ μ—¬λΆ€λ₯Ό μ•ˆμ •μ μœΌλ‘œ 감지할 수 μžˆλŠ” 방법이 μ—†μŠ΅λ‹ˆλ‹€. ( Function.length λ˜λŠ” Function.toString() ꡬ문 λΆ„μ„μœΌλ‘œλŠ” μΆ©λΆ„ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.) 이것이 asyncify 에 κ΄€ν•œ κ²ƒμž…λ‹ˆλ‹€. 약속을 λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜λ₯Ό μ½œλ°±μ„ λ°›λŠ” ν•¨μˆ˜λ‘œ λ³€ν™˜ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. async ν•¨μˆ˜λŠ” μœ ν˜•μ΄ λ‹€λ₯΄κΈ° λ•Œλ¬Έμ— 감지할 수 μžˆμœΌλ―€λ‘œ μžλ™μœΌλ‘œ asyncify ν•  수 μžˆμŠ΅λ‹ˆλ‹€(이것이 λ‚΄λΆ€μ μœΌλ‘œ μž‘λ™ν•˜λŠ” λ°©μ‹μž…λ‹ˆλ‹€). κ·ΈλŸ¬λ‚˜ 약속을 λ°˜ν™˜ν•˜λŠ” 일반 ν•¨μˆ˜μ˜ 경우 Asyncκ°€ ν˜ΈμΆœν•˜κΈ° 전에 μ•Œ 수 μžˆλŠ” 방법이 μ—†μŠ΅λ‹ˆλ‹€.

@aearly 흠, κ·Έλž˜μ„œ μ•„λ§ˆλ„ μ΅œμƒμœ„ μˆ˜μ€€μ—μ„œ μ½œλ°±μ„ κ°μ§€ν•˜λŠ” 것과 같은 더 κ°„λ‹¨ν•œ 것이 μžˆμ„κΉŒμš”?

async function parallel(o, cb) {
  if (!cb) { // promise behavior
    return Object.fromEntries(
      await Promise.all(Object.entries(o).map(async ([k, v]) => [k, await v()]))
    )
  }
  // else do the callback behavior
}

μš°λ¦¬λŠ” 이미 그런 μ‹μž…λ‹ˆλ‹€. async.parallel λŒ€ν•œ μ½œλ°±μ„ μƒλž΅ν•˜λ©΄ 약속을 λ°˜ν™˜ν•©λ‹ˆλ‹€. μ‚¬μš©μž μ˜λ„λ₯Ό κ°€μ •ν•  방법이 μ—†κΈ° λ•Œλ¬Έμ— μ½œλ°±μ„ μƒλž΅ν•˜λ©΄ μ „λ‹¬ν•œ ν•¨μˆ˜κ°€ 약속을 λ°˜ν™˜ν•œλ‹€κ³  κ°€μ •ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λ‚΄κ°€ 무슨 말인지 μ˜€ν•΄ν•˜κ³  μžˆλŠ” 건 μ•„λ‹κΉŒ?

μ’‹μ•„, ꡬ체적으둜 말해 보자: https://repl.it/@caub/async -async

// npm.im/async expects all inner functions to be async,
// even when using no outer callback (as 2nd arg of async.parallel)
console.log(
  await async.parallel({
    foo: async () => delay(1000).then(() => 1),
    bar: async (rs) => {
      return 2
    },
  })
);

// Doesn't work without those async,
// but I'd like this to resolve just like above instead of freezing
console.log(
  await async.parallel({
    foo: () => delay(1000).then(() => 1),
    bar: () => {
      return 2
    },
  })
);

λ‹€μŒ μ£Όμš” λ²„μ „μ—μ„œλŠ” λ‹€μŒκ³Ό μœ μ‚¬ν•œ λ°©μ‹μœΌλ‘œλ§Œ μ½œλ°±μ„ μ§€μ›ν•˜λŠ” 것이 ν₯미둜울 수 μžˆλ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€.

await Promise.all([
  { then(resolve){ setTimeout(resolve, 50, 1) } }, // callback way
  2,
  delay(45).then(())=>3)
])
// [1, 2, 3]

λ‚΄λΆ€ ν•¨μˆ˜μ—μ„œ λΆˆν•„μš”ν•œ async ν‚€μ›Œλ“œμ˜ ν•„μš”μ„± 제거

μž₯점은 더 λͺ…ν™•ν•©λ‹ˆλ‹€. μ‚¬μš©μž μ˜λ„λ₯Ό κ°€μ •ν•˜μ§€ μ•ŠλŠ” 것에 λŒ€ν•œ κ·€ν•˜μ˜ μš”μ μ„ μ΄ν•΄ν•˜μ§€λ§Œ μ—¬μ „νžˆ μ‚¬λžŒλ“€μ΄ Promise 해결을 μ‚¬μš©ν•˜λ €κ³  ν•  λ•Œ async ν‚€μ›Œλ“œλ₯Ό μžŠμ§€ μ•Šμ„ 것이라고 κ°€μ •ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

그것은 κ½€ 큰 λ°©μ‹μœΌλ‘œ 이전 λ²„μ „κ³Όμ˜ ν˜Έν™˜μ„±μ„ 깨뜨릴 κ²ƒμž…λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄ Async.map(fileNames, fs.readFile) 와 같은 일을 더 이상 ν•  수 μ—†μŠ΅λ‹ˆλ‹€.

μ–΄λ””μ„œ λΆ€λŸ¬μ§€λŠ”μ§€ μ„€λͺ…ν•΄μ£Όμ‹€ 수 μžˆλ‚˜μš”?

ν˜„μž¬ μ€‘λ‹¨λœ λ‚΄μš©μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

await async.map(['server.js', 'package.json'], fs.readFile) // works
await async.map(['server.js', 'package.json'], fs.promises.readFile) // works
await async.map(['server.js', 'package.json'], s => fs.promises.readFile(s)) // doesn't work
await async.map(['server.js', 'package.json'], async s => fs.promises.readFile(s)) // works

μ•„μ΄λ””μ–΄λŠ” μ„Έ 번째 문제λ₯Ό ν•΄κ²°ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

예λ₯Ό λ“€μ–΄ λ‹€μŒκ³Ό 같이 μΌμŠ΅λ‹ˆλ‹€.

  // ...
  doc = await async.map(items, item => item.toDoc(currentUser, options));

μ–΄λ”˜κ°€μ—, 그리고 async item => item.toDoc.. μžƒμ–΄λ²„λ ΈκΈ° λ•Œλ¬Έμ— μ‹œκ°„μ΄ μ΄ˆκ³Όλ˜μ—ˆμŠ΅λ‹ˆλ‹€.

κ·Έ κ²½μš°μ— λ‚˜ 혼자라면 κ°€μΉ˜κ°€ μ—†μ§€λ§Œ 더 λ§Žμ€ μ‚¬λžŒλ“€μ΄μ΄ 상황에 μžˆλ‹€λ©΄ μ œμ•ˆ 된 λ³€κ²½ 사항을 κ³ λ €ν•  κ°€μΉ˜κ°€ μžˆλ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€

async.map κ°€ μžˆλŠ” 이 μ˜ˆλ„ 맀우 λŒ€ν‘œμ μΈ μ˜ˆμž…λ‹ˆλ‹€.

  doc = await Promise.all(items.map(item => item.toDoc(currentUser, options)));

async item => item.toDoc.. ν•„μš” 없이 μž‘λ™

μ„Έ 번째 문제λ₯Ό μ•ˆμ •μ μœΌλ‘œ κ³ μΉ  수 μžˆλŠ” 방법은 μ—†μŠ΅λ‹ˆλ‹€. 이것은 과거에 많이 λ…Όμ˜λ˜μ—ˆμŠ΅λ‹ˆλ‹€. ν•¨μˆ˜μ˜ λ°˜ν™˜ 값을 ν™•μΈν•œ λ‹€μŒ μ†ŒκΈ‰ν•˜μ—¬ ν•¨μˆ˜μ— μ½œλ°±μ„ μ „λ‹¬ν•˜μ§€ μ•Šμ„ 수 μ—†μŠ΅λ‹ˆλ‹€. async ν‚€μ›Œλ“œλŠ” 그만큼 μ’‹μŠ΅λ‹ˆλ‹€.

@caub λ»”λ»”ν•œ ν”ŒλŸ¬κ·Έκ°€ 여기에 μžˆμ§€λ§Œ asyncp μ—λŠ” μ—¬κΈ°μ—μ„œ μ°Ύκ³  μžˆλŠ” 것을 μ²˜λ¦¬ν•  수 μžˆλŠ” λŠ₯λ ₯이 μžˆλ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€.

이 λ¬Έμ œκ°€ μ’…λ£Œλœ μƒνƒœμ—μ„œλ„ μœ„μ˜ 링크된 λ¬Έμ œμ™€ 같이 μ—¬κΈ°μ„œ @caub 문을 λ‹€μ‹œ κ°•μ‘°ν•˜κ³  μ‹ΆμŠ΅λ‹ˆλ‹€.
ν˜„μž¬μ˜ 행동은 λ‚˜λ₯Ό 많이 ν˜Όλž€μŠ€λŸ½κ²Œ ν–ˆκ³  μ—¬κΈ°μ„œ λ…Όμ˜λœ 기술적 ν•œκ³„λ₯Ό (μΌμ’…μ˜) μ΄ν•΄ν•˜μ§€λ§Œ λ¬Έμ„œκ°€ λ‹¨μˆœνžˆ ν˜„μž¬μ˜ 행동과 μΌμΉ˜ν•˜μ§€ μ•ŠλŠ”λ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€.
예λ₯Ό λ“€μ–΄ mapLimit의 경우 "λ°˜ν™˜: 콜백이 μ „λ‹¬λ˜μ§€ μ•Šμ€ 경우 약속"이라고 ν‘œμ‹œλ©λ‹ˆλ‹€. - μ €λŠ” 콜백 λ²„μ „μœΌλ‘œ μ‹œμž‘ν•œ λ‹€μŒ 기본적으둜 μ½œλ°±μ„ μƒλž΅ν–ˆκ³  약속을 얻지 λͺ»ν•œ 것에 λ†€λžμŠ΅λ‹ˆλ‹€. '비동기' ν‚€μ›Œλ“œ).
λ˜ν•œ iteratee ν•¨μˆ˜μ˜ 'callback' 문은 ν•„μš”ν•˜μ§€ μ•ŠμœΌλ©° λŒ€μ‹  값을 'λ°˜ν™˜'ν•΄μ•Ό ν•©λ‹ˆλ‹€.
λ”°λΌμ„œ ν˜Όλ™μ„ ν”Όν•˜κΈ° μœ„ν•΄ λ™μž‘μ„ λ³€κ²½(μ„ ν˜Έ)ν•˜κ±°λ‚˜ μ΅œμ†Œν•œ λ¬Έμ„œμ™€ 예제λ₯Ό λ‚΄ κ΄€μ μ—μ„œ μ •λ ¬ν•΄μ•Ό ν•©λ‹ˆλ‹€.

@caub 이것이 μ΄μœ μž…λ‹ˆλ‹€
https://github.com/caolan/async/blob/8aecf108b3922bc5211036706a0f6f75e02bd42b/lib/internal/wrapAsync.js#L3 :L5

function isAsync(fn) {
    return fn[Symbol.toStringTag] === 'AsyncFunction';
}

μ΄λ ‡κ²Œ ν•˜λ©΄ μ‹€μ œλ‘œ ν˜ΈμΆœν•˜μ§€ μ•Šκ³  ν•¨μˆ˜κ°€ 약속을 λ°˜ν™˜ν•˜λŠ”μ§€ ν™•μΈν•˜λŠ” 것이 더 μ‰½μŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ 이것이 async/await κ·œμΉ™μ— μœ„λ°°λœλ‹€λŠ” μ μ—λŠ” μ—¬μ „νžˆ λ™μ˜ν•©λ‹ˆλ‹€. λ‚΄λΆ€ await 없이 async ν‚€μ›Œλ“œλ₯Ό μΆ”κ°€ν•˜λŠ” 것은 μ‹€μ œλ‘œ λŒ€λΆ€λΆ„μ˜ 경우 λ‚˜μœ μŠ΅κ΄€μœΌλ‘œ κ°„μ£Όλ©λ‹ˆλ‹€.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_functionμ—μ„œ

async ν•¨μˆ˜μ˜ 본문은 0개 μ΄μƒμ˜ await ν‘œν˜„μ‹μœΌλ‘œ λΆ„ν• λ˜λŠ” κ²ƒμœΌλ‘œ 생각할 수 μžˆμŠ΅λ‹ˆλ‹€. 첫 번째 await ν‘œν˜„μ‹(μžˆλŠ” 경우)κΉŒμ§€ ν¬ν•¨ν•˜λŠ” μ΅œμƒμœ„ μ½”λ“œλŠ” λ™κΈ°μ‹μœΌλ‘œ μ‹€ν–‰λ©λ‹ˆλ‹€.

... κ·Έλž˜μ„œ μ“Έλͺ¨μ—†κ³  μ΅œμ•…μ˜ 경우 μ½”λ“œκ°€ babel에 μ˜ν•΄ νŠΈλžœμŠ€νŒŒμΌλ˜λŠ” 경우 μ•½κ°„μ˜ μ„±λŠ₯ μ˜€λ²„ν—€λ“œκ°€ μΆ”κ°€λ©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄ async ν‚€μ›Œλ“œκ°€ 있기 λ•Œλ¬Έμ— 더 λ§Žμ€ μ½”λ“œμ™€ 단계가 μΆ”κ°€λ˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.

예λ₯Ό λ“€λ©΄ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

async function foo() {
   return 1
}

... λ‹€μŒκ³Ό λ™μΌν•©λ‹ˆλ‹€.

function foo() {
   return Promise.resolve(1)
}

ν•˜μ§€λ§Œ 이 라이브러리λ₯Ό μ‚¬μš©ν•˜λ©΄

async.mapLimit([1], 1, async v => v)

그리고

async.mapLimit([1], 1, v => Promise.resolve(v)))

λ™λ“±ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€!

이 νŽ˜μ΄μ§€κ°€ 도움이 λ˜μ—ˆλ‚˜μš”?
0 / 5 - 0 λ“±κΈ‰