From 1b8842dbc1659e931d84fcf043ace29cdfaad4d7 Mon Sep 17 00:00:00 2001 From: hyemimi Date: Sat, 16 May 2026 14:45:43 +0900 Subject: [PATCH 1/8] =?UTF-8?q?docs:=2006-promisify=20=EC=B6=A9=EB=8F=8C?= =?UTF-8?q?=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 1-js/11-async/06-promisify/article.md | 61 +-------------------------- 1 file changed, 1 insertion(+), 60 deletions(-) diff --git a/1-js/11-async/06-promisify/article.md b/1-js/11-async/06-promisify/article.md index 395614637..252351df3 100644 --- a/1-js/11-async/06-promisify/article.md +++ b/1-js/11-async/06-promisify/article.md @@ -4,13 +4,7 @@ 기능을 구현 하다 보면 콜백보다는 프라미스가 더 편리하기 때문에 콜백 기반 함수와 라이브러리를 프라미스를 반환하는 함수로 바꾸는 게 좋은 경우가 종종 생길 겁니다. -<<<<<<< HEAD 챕터에서 사용했던 `loadScript(src, callback)` 예시를 사용해 프라미스화에 대해 좀 더 자세히 알아봅시다. -======= -For better understanding, let's see an example. - -For instance, we have `loadScript(src, callback)` from the chapter . ->>>>>>> upstream/master ```js run function loadScript(src, callback) { @@ -27,19 +21,7 @@ function loadScript(src, callback) { // loadScript('path/script.js', (err, script) => {...}) ``` -<<<<<<< HEAD `loadScript(src, callback)`를 이제 프라미스화해봅시다. 새로운 함수 `loadScriptPromise(src)`는 `loadScript`와 동일하게 동작하지만 `callback`을 제외한 `src`만 인수로 받아야 하고, 프라미스를 반환해야 합니다. -======= -The function loads a script with the given `src`, and then calls `callback(err)` in case of an error, or `callback(null, script)` in case of successful loading. That's a widespread agreement for using callbacks, we saw it before. ->>>>>>> upstream/master - -Let's promisify it. - -We'll make a new function `loadScriptPromise(src)`, that does the same (loads the script), but returns a promise instead of using callbacks. - -In other words, we pass it only `src` (no `callback`) and get a promise in return, that resolves with `script` when the load is successful, and rejects with the error otherwise. - -Here it is: ```js let loadScriptPromise = function(src) { return new Promise((resolve, reject) => { @@ -54,10 +36,9 @@ let loadScriptPromise = function(src) { // loadScriptPromise('path/script.js').then(...) ``` -<<<<<<< HEAD 새롭게 구현한 `loadScriptPromise`는 프라미스 기반 코드와 잘 융화됩니다. -예시에서 볼 수 있듯이, `loadScriptPromise`는 기존 함수 `loadScript`에 모든 일을 위임합니다. `loadScript`의 콜백은 스크립트 로딩 상태에 따라 `이행` 혹은 `거부`상태의 프라미스를 반환합니다. +예시에서 볼 수 있듯이, `loadScriptPromise`는 기존 함수 `loadScript`에 모든 일을 위임합니다. `loadScript`의 콜백은 스크립트 로딩 상태에 따라 `이행` 혹은 `거부`상태의 프라미스를 반환합니다. 그런데 실무에선 함수 하나가 아닌 여러 개의 함수를 프라미스화 해야 할 겁니다. 헬퍼 함수를 만드는 게 좋을 것 같네요. 프라미스화를 적용 할 함수 `f`를 받고 래퍼 함수를 반환하는 함수 `promisify(f)`를 만들어봅시다. @@ -68,21 +49,6 @@ function promisify(f) { return function (...args) { // 래퍼 함수를 반환함 return new Promise((resolve, reject) => { function callback(err, result) { // f에 사용할 커스텀 콜백 -======= -As we can see, the new function is a wrapper around the original `loadScript` function. It calls it providing its own callback that translates to promise `resolve/reject`. - -Now `loadScriptPromise` fits well in promise-based code. If we like promises more than callbacks (and soon we'll see more reasons for that), then we will use it instead. - -In practice we may need to promisify more than one function, so it makes sense to use a helper. - -We'll call it `promisify(f)`: it accepts a to-promisify function `f` and returns a wrapper function. - -```js -function promisify(f) { - return function (...args) { // return a wrapper-function (*) - return new Promise((resolve, reject) => { - function callback(err, result) { // our custom callback for f (**) ->>>>>>> upstream/master if (err) { reject(err); } else { @@ -102,26 +68,11 @@ let loadScriptPromise = promisify(loadScript); loadScriptPromise(...).then(...); ``` -<<<<<<< HEAD 위 예시는 프라미스화 할 함수가 인수가 두 개(`(err, result)`)인 콜백을 받을 것이라 가정하고 작성되었습니다. 십중팔구 이런 상황일 것이고, 커스텀 콜백은 이 상황에 딱 들어맞습니다. `promisify`가 잘 동작하는 것은 말할 것도 없겠죠. -======= -The code may look a bit complex, but it's essentially the same that we wrote above, while promisifying `loadScript` function. - -A call to `promisify(f)` returns a wrapper around `f` `(*)`. That wrapper returns a promise and forwards the call to the original `f`, tracking the result in the custom callback `(**)`. - -Here, `promisify` assumes that the original function expects a callback with exactly two arguments `(err, result)`. That's what we encounter most often. Then our custom callback is in exactly the right format, and `promisify` works great for such a case. ->>>>>>> upstream/master 그런데 함수 `f`가 두 개를 초과하는 인수를 가진 콜백, `callback(err, res1, res2, ...)`을 받는다면 어떤 일이 발생할까요? -<<<<<<< HEAD 이런 경우를 대비하여 좀 더 진화한 헬퍼 함수, `promisify`를 만들어 봅시다. 새롭게 만든 함수를 `promisify(f, true)`형태로 호출하면, 프라미스 결과는 콜백의 성공 케이스(`results`)를 담은 배열, `[res1, res2, ...]`이 됩니다. -======= -We can improve our helper. Let's make a more advanced version of `promisify`. - -- When called as `promisify(f)` it should work similar to the version above. -- When called as `promisify(f, true)`, it should return the promise that resolves with the array of callback results. That's exactly for callbacks with many arguments. ->>>>>>> upstream/master ```js // 콜백의 성공 결과를 담은 배열을 얻게 해주는 promisify(f, true) @@ -149,22 +100,12 @@ f = promisify(f, true); f(...).then(arrayOfResults => ..., err => ...); ``` -<<<<<<< HEAD `callback(result)`같이 `err`이 없는 형태나 지금까지 언급하지 않은 형태의 이색적인 콜백도 있을 수 있는데, 이런 경우엔 헬퍼 함수를 사용하지 않고 직접 프라미스화 하면 됩니다. -======= -As you can see it's essentially the same as above, but `resolve` is called with only one or all arguments depending on whether `manyArgs` is truthy. - -For more exotic callback formats, like those without `err` at all: `callback(result)`, we can promisify such functions manually without using the helper. ->>>>>>> upstream/master 본 챕터에서 설명한 헬퍼 함수보다 더 유용한 형태의 프라미스화를 도와주는 함수를 제공하는 모듈도 많습니다. [es6-promisify](https://github.com/digitaldesignlabs/es6-promisify)가 대표적인 예입니다. Node.js에선 내장 함수 `util.promisify`를 사용해 프라미스화를 할 수 있습니다. ```smart -<<<<<<< HEAD 프라미스화는 곧 배우게 될 `async/await`와 함께 사용하면 더 좋습니다. 다만, 콜백을 완전히 대체하지는 못한다는 사실을 기억해 두시기 바랍니다. -======= -Promisification is a great approach, especially when you use `async/await` (covered later in the chapter ), but not a total replacement for callbacks. ->>>>>>> upstream/master 프라미스는 하나의 결과만 가질 수 있지만, 콜백은 여러 번 호출할 수 있기 때문입니다. From a7105070d90bf8a3395b23cc05c9969f7c52d098 Mon Sep 17 00:00:00 2001 From: hyemimi Date: Sat, 16 May 2026 14:46:43 +0900 Subject: [PATCH 2/8] =?UTF-8?q?docs:=2007-microtask-queue=20=EC=B6=A9?= =?UTF-8?q?=EB=8F=8C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 1-js/11-async/07-microtask-queue/article.md | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/1-js/11-async/07-microtask-queue/article.md b/1-js/11-async/07-microtask-queue/article.md index fbcaeff75..81f089bcc 100644 --- a/1-js/11-async/07-microtask-queue/article.md +++ b/1-js/11-async/07-microtask-queue/article.md @@ -23,22 +23,14 @@ alert("코드 종료"); // 얼럿 창이 가장 먼저 뜹니다. ## 마이크로태스크 큐 -<<<<<<< HEAD 비동기 작업을 처리하려면 적절한 관리가 필요합니다. 이를 위해 ECMA에선 `PromiseJobs`라는 내부 큐(internal queue)를 명시합니다. V8 엔진에선 이를 '마이크로태스크 큐(microtask queue)'라고 부르기 때문에 이 용어가 좀 더 선호됩니다. -======= -Asynchronous tasks need proper management. For that, the ECMA standard specifies an internal queue `PromiseJobs`, more often referred to as the "microtask queue" (V8 term). ->>>>>>> upstream/master [명세서](https://tc39.github.io/ecma262/#sec-jobs-and-job-queues)의 설명을 살펴봅시다. - 마이크로태스크 큐는 먼저 들어온 작업을 먼저 실행합니다(FIFO, first-in-first-out). - 실행할 것이 아무것도 남아있지 않을 때만 마이크로태스크 큐에 있는 작업이 실행되기 시작합니다. -<<<<<<< HEAD 요약하자면, 어떤 프라미스가 준비되었을 때 이 프라미스의 `.then/catch/finally` 핸들러가 큐에 들어간다고 생각하시면 됩니다. 이때 핸들러들은 여전히 실행되지 않습니다. 현재 코드에서 자유로운 상태가 되었을 때에서야 자바스크립트 엔진은 큐에서 작업을 꺼내 실행합니다. -======= -Or, to put it more simply, when a promise is ready, its `.then/catch/finally` handlers are put into the queue; they are not executed yet. When the JavaScript engine becomes free from the current code, it takes a task from the queue and executes it. ->>>>>>> upstream/master 위 예시에서 '코드 종료'가 먼저 출력되는 이유가 여기에 있습니다. @@ -48,11 +40,7 @@ Or, to put it more simply, when a promise is ready, its `.then/catch/finally` ha 여러 개의 `.then/catch/finally`를 사용해 만든 체인의 경우, 각 핸들러는 비동기적으로 실행됩니다. 큐에 들어간 핸들러 각각은 현재 코드가 완료되고, 큐에 적체된 이전 핸들러의 실행이 완료되었을 때 실행됩니다. -<<<<<<< HEAD **그렇다면 '프라미스 성공!'을 먼저, '코드 종료'를 나중에 출력되게 하려면 어떻게 해야 할까요?** 실행 순서가 중요한 경우엔 이런 요구사항이 충족되도록 코드를 작성해야 합니다. -======= -**What if the order matters for us? How can we make `code finished` appear after `promise done`?** ->>>>>>> upstream/master 방법은 아주 쉽습니다. `.then`을 사용해 큐에 넣으면 됩니다. @@ -115,11 +103,7 @@ window.addEventListener('unhandledrejection', event => alert(event.reason)); ## 요약 -<<<<<<< HEAD 모든 프라미스 동작은 '마이크로태스크 큐'(ES8 용어)라 불리는 내부 '프라미스 잡(promise job)' 큐에 들어가서 처리되기 때문에 프라미스 핸들링은 항상 비동기로 처리됩니다. -======= -Promise handling is always asynchronous, as all promise actions pass through the internal "promise jobs" queue, also called "microtask queue" (V8 term). ->>>>>>> upstream/master 따라서 `.then/catch/finally` 핸들러는 항상 현재 코드가 종료되고 난 후에 호출됩니다. From b890ebc17b1e32176104178c6bfc52920dd84f02 Mon Sep 17 00:00:00 2001 From: hyemimi Date: Sat, 16 May 2026 14:47:49 +0900 Subject: [PATCH 3/8] =?UTF-8?q?docs:=2008-async-await=20article=20?= =?UTF-8?q?=EC=B6=A9=EB=8F=8C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 1-js/11-async/08-async-await/article.md | 32 ------------------------- 1 file changed, 32 deletions(-) diff --git a/1-js/11-async/08-async-await/article.md b/1-js/11-async/08-async-await/article.md index c13a77bd2..ac1f0ec7d 100644 --- a/1-js/11-async/08-async-await/article.md +++ b/1-js/11-async/08-async-await/article.md @@ -69,17 +69,10 @@ f(); `await`는 말 그대로 프라미스가 처리될 때까지 함수 실행을 기다리게 만듭니다. 프라미스가 처리되면 그 결과와 함께 실행이 재개되죠. 프라미스가 처리되길 기다리는 동안엔 엔진이 다른 일(다른 스크립트를 실행, 이벤트 처리 등)을 할 수 있기 때문에, CPU 리소스가 낭비되지 않습니다. -<<<<<<< HEAD `await`는 `promise.then`보다 좀 더 세련되게 프라미스의 `result` 값을 얻을 수 있도록 해주는 문법입니다. `promise.then`보다 가독성 좋고 쓰기도 쉽습니다. ````warn header="일반 함수엔 `await`을 사용할 수 없습니다." `async` 함수가 아닌데 `await`을 사용하면 문법 에러가 발생합니다. -======= -It's just a more elegant syntax of getting the promise result than `promise.then`. And, it's easier to read and write. - -````warn header="Can't use `await` in regular functions" -If we try to use `await` in a non-async function, there would be a syntax error: ->>>>>>> upstream/master ```js run function f() { @@ -90,11 +83,7 @@ function f() { } ``` -<<<<<<< HEAD function 앞에 `async`를 붙이지 않으면 이런 에러가 발생할 수 있습니다. 앞서 설명해 드린 바와 같이 `await`는 `async` 함수 안에서만 동작합니다. -======= -We may get this error if we forget to put `async` before a function. As stated earlier, `await` only works inside an `async` function. ->>>>>>> upstream/master ```` 챕터의 `showAvatar()` 예시를 `async/await`를 사용해 다시 작성해봅시다. @@ -132,34 +121,18 @@ showAvatar(); 코드가 깔끔해지고 읽기도 쉬워졌습니다. 프라미스를 사용한 것보다 훨씬 낫네요. -<<<<<<< HEAD ````smart header="`await`는 최상위 레벨 코드에서 작동하지 않습니다." `await`을 이제 막 사용하기 시작한 분들은 최상위 레벨 코드(top-level code)에 `await`을 사용할 수 없다는 사실을 잊곤 합니다. 아래와 같은 코드는 동작하지 않습니다. ```js run // 최상위 레벨 코드에선 문법 에러가 발생함 -======= -````smart header="Modern browsers allow top-level `await` in modules" -In modern browsers, `await` on top level works just fine, when we're inside a module. We'll cover modules in article . - -For instance: - -```js run module -// we assume this code runs at top level, inside a module ->>>>>>> upstream/master let response = await fetch('/article/promise-chaining/user.json'); let user = await response.json(); console.log(user); ``` -<<<<<<< HEAD 하지만 익명 async 함수로 코드를 감싸면 최상위 레벨 코드에도 `await`를 사용할 수 있습니다. -======= -If we're not using modules, or [older browsers](https://caniuse.com/mdn-javascript_operators_await_top_level) must be supported, there's a universal recipe: wrapping into an anonymous async function. - -Like this: ->>>>>>> upstream/master ```js (async () => { @@ -325,13 +298,8 @@ function 앞에 `async` 키워드를 추가하면 두 가지 효과가 있습니 프라미스 앞에 `await` 키워드를 붙이면 자바스크립트는 프라미스가 처리될 때까지 대기합니다. 처리가 완료되면 조건에 따라 아래와 같은 동작이 이어집니다. -<<<<<<< HEAD 1. 에러 발생 -- 예외가 생성됨(에러가 발생한 장소에서 `throw error`를 호출한 것과 동일함) 2. 에러 미발생 -- 프라미스 객체의 result 값을 반환 -======= -1. If it's an error, an exception is generated — same as if `throw error` were called at that very place. -2. Otherwise, it returns the result. ->>>>>>> upstream/master `async/await`를 함께 사용하면 읽고, 쓰기 쉬운 비동기 코드를 작성할 수 있습니다. From 333a74ed2013c7a9bc8248e88d7f3e4856c7bbf8 Mon Sep 17 00:00:00 2001 From: hyemimi Date: Sat, 16 May 2026 14:49:03 +0900 Subject: [PATCH 4/8] =?UTF-8?q?docs:=2008-async-await/02-rewrite-async-2?= =?UTF-8?q?=20=EC=B6=A9=EB=8F=8C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 1-js/11-async/08-async-await/02-rewrite-async-2/solution.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/1-js/11-async/08-async-await/02-rewrite-async-2/solution.md b/1-js/11-async/08-async-await/02-rewrite-async-2/solution.md index 597d651ee..534c6593f 100644 --- a/1-js/11-async/08-async-await/02-rewrite-async-2/solution.md +++ b/1-js/11-async/08-async-await/02-rewrite-async-2/solution.md @@ -1,9 +1,5 @@ -<<<<<<< HEAD -속임수랄게 없는 문제입니다. `demoGithubUser`안의 `.catch`를 `try...catch`로 교체하고 필요한 곳에 `async/await`를 추가하면 됩니다. -======= -There are no tricks here. Just replace `.catch` with `try..catch` inside `demoGithubUser` and add `async/await` where needed: ->>>>>>> upstream/master +속임수랄게 없는 문제입니다. `demoGithubUser`안의 `.catch`를 `try..catch`로 교체하고 필요한 곳에 `async/await`를 추가하면 됩니다. ```js run class HttpError extends Error { From 473d6144936e08b42a832b24b1655cd1e433bbee Mon Sep 17 00:00:00 2001 From: hyemimi Date: Sat, 16 May 2026 14:49:46 +0900 Subject: [PATCH 5/8] =?UTF-8?q?docs:=2008-rewrite-async-await/02-rewrite-a?= =?UTF-8?q?sync-2=20task=20=EC=B6=A9=EB=8F=8C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 1-js/11-async/08-async-await/02-rewrite-async-2/task.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/1-js/11-async/08-async-await/02-rewrite-async-2/task.md b/1-js/11-async/08-async-await/02-rewrite-async-2/task.md index cbdd5b79b..3f57c71fa 100644 --- a/1-js/11-async/08-async-await/02-rewrite-async-2/task.md +++ b/1-js/11-async/08-async-await/02-rewrite-async-2/task.md @@ -1,11 +1,7 @@ # async와 await를 사용해서 '다시 던지기' 예시 재작성하기 -<<<<<<< HEAD 챕터에서 다뤘던 '다시 던지기(rethrow)' 관련 예시를 기억하실 겁니다. 이 예시를 `.then/catch` 대신 `async/await`를 사용해 다시 작성해 봅시다. -======= -Below you can find the "rethrow" example. Rewrite it using `async/await` instead of `.then/catch`. ->>>>>>> upstream/master 그리고 `demoGithubUser` 안의 반복(recursion)은 반복문(loop)을 사용해 작성하도록 합시다. `async/await`를 사용하면 쉽게 작성할 수 있습니다. From f56c747c6b7b97d79f6dbfc20278d921e895a434 Mon Sep 17 00:00:00 2001 From: hyemimi Date: Sat, 16 May 2026 14:50:20 +0900 Subject: [PATCH 6/8] =?UTF-8?q?docs:=2003-async-from-regular=20=EC=B6=A9?= =?UTF-8?q?=EB=8F=8C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../08-async-await/03-async-from-regular/task.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/1-js/11-async/08-async-await/03-async-from-regular/task.md b/1-js/11-async/08-async-await/03-async-from-regular/task.md index e38ed7c9e..7c1ed56e1 100644 --- a/1-js/11-async/08-async-await/03-async-from-regular/task.md +++ b/1-js/11-async/08-async-await/03-async-from-regular/task.md @@ -1,11 +1,7 @@ # async가 아닌 함수에서 async 함수 호출하기 -<<<<<<< HEAD '일반' 함수가 하나 있는데, 여기서 `async` 함수를 어떻게 하면 호출하고, 그 결과를 사용할 수 있을까요? -======= -We have a "regular" function called `f`. How can you call the `async` function `wait()` and use its result inside of `f`? ->>>>>>> upstream/master ```js async function wait() { @@ -15,15 +11,9 @@ async function wait() { } function f() { -<<<<<<< HEAD // ...코드... // async wait()를 호출하고 그 결과인 10을 얻을 때까지 기다리려면 어떻게 해야 할까요? // f는 일반 함수이기 때문에 여기선 'await'를 사용할 수 없다는 점에 주의하세요! -======= - // ...what should you write here? - // we need to call async wait() and wait to get 10 - // remember, we can't use "await" ->>>>>>> upstream/master } ``` From fb909e8e786b4e9264c85f6d80c3b8ce5c1dddd9 Mon Sep 17 00:00:00 2001 From: hyemimi Date: Sat, 16 May 2026 15:21:28 +0900 Subject: [PATCH 7/8] =?UTF-8?q?docs:=20promise.all=20task=20&=20solution?= =?UTF-8?q?=20=EC=8B=A0=EA=B7=9C=20=EB=B2=88=EC=97=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../02-rewrite-async-2/solution.md | 4 +- .../04-promise-all-failure/solution.md | 46 +++++++++---------- .../04-promise-all-failure/task.md | 38 +++++++-------- 3 files changed, 44 insertions(+), 44 deletions(-) diff --git a/1-js/11-async/08-async-await/02-rewrite-async-2/solution.md b/1-js/11-async/08-async-await/02-rewrite-async-2/solution.md index 534c6593f..3a4adec9a 100644 --- a/1-js/11-async/08-async-await/02-rewrite-async-2/solution.md +++ b/1-js/11-async/08-async-await/02-rewrite-async-2/solution.md @@ -1,5 +1,5 @@ -속임수랄게 없는 문제입니다. `demoGithubUser`안의 `.catch`를 `try..catch`로 교체하고 필요한 곳에 `async/await`를 추가하면 됩니다. +속임수랄게 없는 문제입니다. `demoGithubUser`안의 `.catch`를 `try..catch`로 교체하고 필요한 곳에 `async/await`를 추가하면 됩니다. ```js run class HttpError extends Error { @@ -46,4 +46,4 @@ async function demoGithubUser() { } demoGithubUser(); -``` \ No newline at end of file +``` diff --git a/1-js/11-async/08-async-await/04-promise-all-failure/solution.md b/1-js/11-async/08-async-await/04-promise-all-failure/solution.md index 9fda8e000..847aa2add 100644 --- a/1-js/11-async/08-async-await/04-promise-all-failure/solution.md +++ b/1-js/11-async/08-async-await/04-promise-all-failure/solution.md @@ -1,52 +1,52 @@ -The root of the problem is that `Promise.all` immediately rejects when one of its promises rejects, but it do nothing to cancel the other promises. +문제의 원인은 `Promise.all`이 프라미스 중 하나라도 거부되면 즉시 거부되지만, 나머지 프라미스를 취소하지는 않는다는 데 있습니다. -In our case, the second query fails, so `Promise.all` rejects, and the `try...catch` block catches this error.Meanwhile, other promises are *not affected* - they independently continue their execution. In our case, the third query throws an error of its own after a bit of time. And that error is never caught, we can see it in the console. +위 예시에서는 두 번째 쿼리가 실패하므로 `Promise.all`이 거부되고, `try...catch` 블록이 이 에러를 잡습니다. 한편 다른 프라미스는 *아무 영향도 받지 않습니다*. 각자 독립적으로 계속 실행됩니다. 예시에서는 잠시 후 세 번째 쿼리가 자체적으로 에러를 던집니다. 이 에러는 어디에서도 잡히지 않으므로 콘솔에서 확인할 수 있습니다. -The problem is especially dangerous in server-side environments, such as Node.js, when an uncaught error may cause the process to crash. +이 문제는 Node.js 같은 서버 측 환경에서 특히 위험합니다. 잡히지 않은 에러로 인해 프로세스가 중단될 수 있기 때문입니다. -How to fix it? +어떻게 고칠 수 있을까요? -An ideal solution would be to cancel all unfinished queries when one of them fails. This way we avoid any potential errors. +가장 이상적인 해결책은 쿼리 중 하나가 실패했을 때 아직 끝나지 않은 쿼리를 모두 취소하는 것입니다. 이렇게 하면 잠재적인 에러를 피할 수 있습니다. -However, the bad news is that service calls (such as `database.query`) are often implemented by a 3rd-party library which doesn't support cancellation. Then there's no way to cancel a call. +하지만 안타깝게도 `database.query` 같은 서비스 호출은 취소 기능을 지원하지 않는 서드파티 라이브러리로 구현된 경우가 많습니다. 이런 경우 호출을 취소할 방법이 없습니다. -As an alternative, we can write our own wrapper function around `Promise.all` which adds a custom `then/catch` handler to each promise to track them: results are gathered and, if an error occurs, all subsequent promises are ignored. +대안으로 `Promise.all`을 감싸는 래퍼 함수를 직접 작성할 수 있습니다. 이 함수는 각 프라미스에 커스텀 `then/catch` 핸들러를 붙여 상태를 추적합니다. 결과를 모으다가 에러가 발생하면 그 이후 프라미스는 모두 무시합니다. ```js function customPromiseAll(promises) { return new Promise((resolve, reject) => { const results = []; let resultsCount = 0; - let hasError = false; // we'll set it to true upon first error + let hasError = false; // 첫 번째 에러가 발생하면 true로 바꿉니다. promises.forEach((promise, index) => { promise .then(result => { - if (hasError) return; // ignore the promise if already errored + if (hasError) return; // 이미 에러가 발생했다면 이 프라미스를 무시합니다. results[index] = result; resultsCount++; if (resultsCount === promises.length) { - resolve(results); // when all results are ready - successs + resolve(results); // 모든 결과가 준비되면 성공입니다. } }) .catch(error => { - if (hasError) return; // ignore the promise if already errored - hasError = true; // wops, error! - reject(error); // fail with rejection + if (hasError) return; // 이미 에러가 발생했다면 이 프라미스를 무시합니다. + hasError = true; // 에러가 발생했습니다. + reject(error); // 거부 상태로 실패 처리합니다. }); }); }); } ``` -This approach has an issue of its own - it's often undesirable to `disconnect()` when queries are still in the process. +이 방식에도 문제가 있습니다. 쿼리가 아직 처리 중일 때 `disconnect()`를 호출하는 것은 대개 바람직하지 않습니다. -It may be important that all queries complete, especially if some of them make important updates. +특히 일부 쿼리가 중요한 업데이트를 수행한다면 모든 쿼리가 완료되는 것이 중요할 수 있습니다. -So we should wait until all promises are settled before going further with the execution and eventually disconnecting. +따라서 실행을 계속 진행하고 마지막에 연결을 끊기 전에 모든 프라미스가 처리될 때까지 기다려야 합니다. -Here's another implementation. It behaves similar to `Promise.all` - also resolves with the first error, but waits until all promises are settled. +다른 구현을 살펴봅시다. 이 구현은 `Promise.all`과 비슷하게 동작합니다. 첫 번째 에러와 함께 거부되지만, 모든 프라미스가 처리될 때까지 기다립니다. ```js function customPromiseAllWait(promises) { @@ -80,16 +80,16 @@ function customPromiseAllWait(promises) { } ``` -Now `await customPromiseAllWait(...)` will stall the execution until all queries are processed. +이제 `await customPromiseAllWait(...)`는 모든 쿼리가 처리될 때까지 실행을 멈춥니다. -This is a more reliable approach, as it guarantees a predictable execution flow. +실행 흐름을 예측할 수 있게 보장하므로 더 안정적인 방식입니다. -Lastly, if we'd like to process all errors, we can use either use `Promise.allSettled` or write a wrapper around it to gathers all errors in a single [AggregateError](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError) object and rejects with it. +마지막으로 모든 에러를 처리하고 싶다면 `Promise.allSettled`를 사용하면 됩니다. 또는 `Promise.allSettled`를 감싸는 래퍼를 작성해 모든 에러를 하나의 [AggregateError](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/AggregateError) 객체에 모아 거부할 수도 있습니다. ```js -// wait for all promises to settle -// return results if no errors -// throw AggregateError with all errors if any +// 모든 프라미스가 처리될 때까지 기다립니다. +// 에러가 없으면 결과를 반환합니다. +// 에러가 하나라도 있으면 모든 에러가 담긴 AggregateError를 던집니다. function allOrAggregateError(promises) { return Promise.allSettled(promises).then(results => { const errors = []; diff --git a/1-js/11-async/08-async-await/04-promise-all-failure/task.md b/1-js/11-async/08-async-await/04-promise-all-failure/task.md index 74571c43e..fc83c50af 100644 --- a/1-js/11-async/08-async-await/04-promise-all-failure/task.md +++ b/1-js/11-async/08-async-await/04-promise-all-failure/task.md @@ -1,17 +1,17 @@ -# Dangerous Promise.all +# 위험한 Promise.all -`Promise.all` is a great way to parallelize multiple operations. It's especially useful when we need to make parallel requests to multiple services. +`Promise.all`은 여러 작업을 병렬로 처리할 때 아주 유용합니다. 여러 서비스에 병렬로 요청을 보내야 할 때 특히 빛을 발합니다. -However, there's a hidden danger. We'll see an example in this task and explore how to avoid it. +하지만 숨어 있는 위험이 있습니다. 이번 과제에서는 예시를 통해 어떤 위험이 있는지 살펴보고 이를 피하는 방법을 알아봅시다. -Let's say we have a connection to a remote service, such as a database. +데이터베이스 같은 원격 서비스에 연결한다고 가정해 봅시다. -There're two functions: `connect()` and `disconnect()`. +`connect()`와 `disconnect()`라는 두 함수가 있습니다. -When connected, we can send requests using `database.query(...)` - an async function which usually returns the result but also may throw an error. +연결된 상태에서는 `database.query(...)`를 사용해 요청을 보낼 수 있습니다. `database.query(...)`는 보통 결과를 반환하지만 에러를 던질 수도 있는 비동기 함수입니다. -Here's a simple implementation: +간단히 구현하면 다음과 같습니다. ```js let database; @@ -28,21 +28,21 @@ function disconnect() { database = null; } -// intended usage: +// 사용법: // connect() // ... -// database.query(true) to emulate a successful call -// database.query(false) to emulate a failed call +// database.query(true)는 성공한 호출을 흉내 냅니다. +// database.query(false)는 실패한 호출을 흉내 냅니다. // ... // disconnect() ``` -Now here's the problem. +이제 문제가 되는 부분을 살펴봅시다. -We wrote the code to connect and send 3 queries in parallel (all of them take different time, e.g. 100, 200 and 300ms), then disconnect: +연결한 뒤 쿼리 세 개를 병렬로 보내고, 이후 연결을 끊는 코드를 작성했습니다. 각 쿼리는 100, 200, 300ms처럼 서로 다른 시간이 걸립니다. ```js -// Helper function to call async function `fn` after `ms` milliseconds +// `ms` 밀리초 뒤에 비동기 함수 `fn`을 호출하는 헬퍼 함수 function delay(fn, ms) { return new Promise((resolve, reject) => { setTimeout(() => fn().then(resolve, reject), ms); @@ -54,8 +54,8 @@ async function run() { try { await Promise.all([ - // these 3 parallel jobs take different time: 100, 200 and 300 ms - // we use the `delay` helper to achieve this effect + // 병렬 작업 세 개는 각각 100, 200, 300ms로 서로 다른 시간이 걸립니다. + // 이 효과를 내기 위해 `delay` 헬퍼를 사용합니다. *!* delay(() => database.query(true), 100), delay(() => database.query(false), 200), @@ -63,7 +63,7 @@ async function run() { */!* ]); } catch(error) { - console.log('Error handled (or was it?)'); + console.log('에러를 처리했습니다. 정말 그럴까요?'); } disconnect(); @@ -72,8 +72,8 @@ async function run() { run(); ``` -Two of these queries happen to be unsuccessful, but we're smart enough to wrap the `Promise.all` call into a `try..catch` block. +세 쿼리 중 두 개는 실패합니다. 그래도 `Promise.all` 호출을 `try..catch` 블록으로 감싸 두었으니 충분해 보입니다. -However, this doesn't help! This script actually leads to an uncaught error in console! +하지만 이 방법으로는 부족합니다! 실제로 이 스크립트는 콘솔에 잡히지 않은 에러를 남깁니다. -Why? How to avoid it? \ No newline at end of file +왜 이런 일이 생길까요? 어떻게 피할 수 있을까요? \ No newline at end of file From 3c69192206f77ae7b5ec8f67be5d3620710b2781 Mon Sep 17 00:00:00 2001 From: hyemimi Date: Sat, 16 May 2026 15:55:53 +0900 Subject: [PATCH 8/8] =?UTF-8?q?fix:=20=EB=A7=9E=EC=B6=A4=EB=B2=95=20?= =?UTF-8?q?=EA=B2=80=EC=82=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 1-js/11-async/08-async-await/04-promise-all-failure/solution.md | 2 +- 1-js/11-async/08-async-await/04-promise-all-failure/task.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/1-js/11-async/08-async-await/04-promise-all-failure/solution.md b/1-js/11-async/08-async-await/04-promise-all-failure/solution.md index 847aa2add..e03853b3f 100644 --- a/1-js/11-async/08-async-await/04-promise-all-failure/solution.md +++ b/1-js/11-async/08-async-await/04-promise-all-failure/solution.md @@ -1,7 +1,7 @@ 문제의 원인은 `Promise.all`이 프라미스 중 하나라도 거부되면 즉시 거부되지만, 나머지 프라미스를 취소하지는 않는다는 데 있습니다. -위 예시에서는 두 번째 쿼리가 실패하므로 `Promise.all`이 거부되고, `try...catch` 블록이 이 에러를 잡습니다. 한편 다른 프라미스는 *아무 영향도 받지 않습니다*. 각자 독립적으로 계속 실행됩니다. 예시에서는 잠시 후 세 번째 쿼리가 자체적으로 에러를 던집니다. 이 에러는 어디에서도 잡히지 않으므로 콘솔에서 확인할 수 있습니다. +위 예시에서는 두 번째 쿼리가 실패하므로 `Promise.all`이 거부되고, `try...catch` 블록이 이 에러를 잡습니다. 한편, 다른 프라미스는 *아무 영향도 받지 않습니다*. 각자 독립적으로 계속 실행됩니다. 예시에서는 잠시 후 세 번째 쿼리가 자체적으로 에러를 던집니다. 이 에러는 어디에서도 잡히지 않으므로 콘솔에서 확인할 수 있습니다. 이 문제는 Node.js 같은 서버 측 환경에서 특히 위험합니다. 잡히지 않은 에러로 인해 프로세스가 중단될 수 있기 때문입니다. diff --git a/1-js/11-async/08-async-await/04-promise-all-failure/task.md b/1-js/11-async/08-async-await/04-promise-all-failure/task.md index fc83c50af..5df4c3852 100644 --- a/1-js/11-async/08-async-await/04-promise-all-failure/task.md +++ b/1-js/11-async/08-async-await/04-promise-all-failure/task.md @@ -9,7 +9,7 @@ `connect()`와 `disconnect()`라는 두 함수가 있습니다. -연결된 상태에서는 `database.query(...)`를 사용해 요청을 보낼 수 있습니다. `database.query(...)`는 보통 결과를 반환하지만 에러를 던질 수도 있는 비동기 함수입니다. +연결된 상태에서는 `database.query(...)`를 사용해 요청을 보낼 수 있습니다. `database.query(...)`는 보통 결과를 반환하지만, 에러를 던질 수도 있는 비동기 함수입니다. 간단히 구현하면 다음과 같습니다.