From 86c48a1f41215a3bc13fa15f57c63eb0a9f2db20 Mon Sep 17 00:00:00 2001 From: LycheeEng Date: Wed, 17 Jul 2019 17:05:00 +0800 Subject: [PATCH 1/5] rename folder --- .../01-promise-errors-as-results/solution.md | 0 .../01-promise-errors-as-results/solution.view/index.html | 0 .../01-promise-errors-as-results/source.view/index.html | 0 .../01-promise-errors-as-results/task.md | 0 .../02-promise-errors-as-results-2/solution.md | 0 .../02-promise-errors-as-results-2/solution.view/index.html | 0 .../02-promise-errors-as-results-2/source.view/index.html | 0 .../02-promise-errors-as-results-2/task.md | 0 1-js/11-async/{04-promise-api => 05-promise-api}/article.md | 0 1-js/11-async/{04-promise-api => 05-promise-api}/head.html | 0 1-js/11-async/{04-promise-api => 05-promise-api}/iliakan.json | 0 1-js/11-async/{04-promise-api => 05-promise-api}/one.js | 0 1-js/11-async/{04-promise-api => 05-promise-api}/two.js | 0 13 files changed, 0 insertions(+), 0 deletions(-) rename 1-js/11-async/{04-promise-api => 05-promise-api}/01-promise-errors-as-results/solution.md (100%) rename 1-js/11-async/{04-promise-api => 05-promise-api}/01-promise-errors-as-results/solution.view/index.html (100%) rename 1-js/11-async/{04-promise-api => 05-promise-api}/01-promise-errors-as-results/source.view/index.html (100%) rename 1-js/11-async/{04-promise-api => 05-promise-api}/01-promise-errors-as-results/task.md (100%) rename 1-js/11-async/{04-promise-api => 05-promise-api}/02-promise-errors-as-results-2/solution.md (100%) rename 1-js/11-async/{04-promise-api => 05-promise-api}/02-promise-errors-as-results-2/solution.view/index.html (100%) rename 1-js/11-async/{04-promise-api => 05-promise-api}/02-promise-errors-as-results-2/source.view/index.html (100%) rename 1-js/11-async/{04-promise-api => 05-promise-api}/02-promise-errors-as-results-2/task.md (100%) rename 1-js/11-async/{04-promise-api => 05-promise-api}/article.md (100%) rename 1-js/11-async/{04-promise-api => 05-promise-api}/head.html (100%) rename 1-js/11-async/{04-promise-api => 05-promise-api}/iliakan.json (100%) rename 1-js/11-async/{04-promise-api => 05-promise-api}/one.js (100%) rename 1-js/11-async/{04-promise-api => 05-promise-api}/two.js (100%) diff --git a/1-js/11-async/04-promise-api/01-promise-errors-as-results/solution.md b/1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.md similarity index 100% rename from 1-js/11-async/04-promise-api/01-promise-errors-as-results/solution.md rename to 1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.md diff --git a/1-js/11-async/04-promise-api/01-promise-errors-as-results/solution.view/index.html b/1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.view/index.html similarity index 100% rename from 1-js/11-async/04-promise-api/01-promise-errors-as-results/solution.view/index.html rename to 1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.view/index.html diff --git a/1-js/11-async/04-promise-api/01-promise-errors-as-results/source.view/index.html b/1-js/11-async/05-promise-api/01-promise-errors-as-results/source.view/index.html similarity index 100% rename from 1-js/11-async/04-promise-api/01-promise-errors-as-results/source.view/index.html rename to 1-js/11-async/05-promise-api/01-promise-errors-as-results/source.view/index.html diff --git a/1-js/11-async/04-promise-api/01-promise-errors-as-results/task.md b/1-js/11-async/05-promise-api/01-promise-errors-as-results/task.md similarity index 100% rename from 1-js/11-async/04-promise-api/01-promise-errors-as-results/task.md rename to 1-js/11-async/05-promise-api/01-promise-errors-as-results/task.md diff --git a/1-js/11-async/04-promise-api/02-promise-errors-as-results-2/solution.md b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.md similarity index 100% rename from 1-js/11-async/04-promise-api/02-promise-errors-as-results-2/solution.md rename to 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.md diff --git a/1-js/11-async/04-promise-api/02-promise-errors-as-results-2/solution.view/index.html b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.view/index.html similarity index 100% rename from 1-js/11-async/04-promise-api/02-promise-errors-as-results-2/solution.view/index.html rename to 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.view/index.html diff --git a/1-js/11-async/04-promise-api/02-promise-errors-as-results-2/source.view/index.html b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/source.view/index.html similarity index 100% rename from 1-js/11-async/04-promise-api/02-promise-errors-as-results-2/source.view/index.html rename to 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/source.view/index.html diff --git a/1-js/11-async/04-promise-api/02-promise-errors-as-results-2/task.md b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/task.md similarity index 100% rename from 1-js/11-async/04-promise-api/02-promise-errors-as-results-2/task.md rename to 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/task.md diff --git a/1-js/11-async/04-promise-api/article.md b/1-js/11-async/05-promise-api/article.md similarity index 100% rename from 1-js/11-async/04-promise-api/article.md rename to 1-js/11-async/05-promise-api/article.md diff --git a/1-js/11-async/04-promise-api/head.html b/1-js/11-async/05-promise-api/head.html similarity index 100% rename from 1-js/11-async/04-promise-api/head.html rename to 1-js/11-async/05-promise-api/head.html diff --git a/1-js/11-async/04-promise-api/iliakan.json b/1-js/11-async/05-promise-api/iliakan.json similarity index 100% rename from 1-js/11-async/04-promise-api/iliakan.json rename to 1-js/11-async/05-promise-api/iliakan.json diff --git a/1-js/11-async/04-promise-api/one.js b/1-js/11-async/05-promise-api/one.js similarity index 100% rename from 1-js/11-async/04-promise-api/one.js rename to 1-js/11-async/05-promise-api/one.js diff --git a/1-js/11-async/04-promise-api/two.js b/1-js/11-async/05-promise-api/two.js similarity index 100% rename from 1-js/11-async/04-promise-api/two.js rename to 1-js/11-async/05-promise-api/two.js From a12ce78b992e2a8fc133dde282c680c1d69cd0c0 Mon Sep 17 00:00:00 2001 From: LycheeEng Date: Wed, 17 Jul 2019 18:35:24 +0800 Subject: [PATCH 2/5] delete 01 and 02 folder --- .../01-promise-errors-as-results/solution.md | 44 ----------------- .../solution.view/index.html | 24 ---------- .../source.view/index.html | 28 ----------- .../01-promise-errors-as-results/task.md | 48 ------------------- .../solution.md | 0 .../solution.view/index.html | 29 ----------- .../source.view/index.html | 33 ------------- .../02-promise-errors-as-results-2/task.md | 34 ------------- 8 files changed, 240 deletions(-) delete mode 100644 1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.md delete mode 100644 1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.view/index.html delete mode 100644 1-js/11-async/05-promise-api/01-promise-errors-as-results/source.view/index.html delete mode 100644 1-js/11-async/05-promise-api/01-promise-errors-as-results/task.md delete mode 100644 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.md delete mode 100644 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.view/index.html delete mode 100644 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/source.view/index.html delete mode 100644 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/task.md diff --git a/1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.md b/1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.md deleted file mode 100644 index 63e0bf1b70..0000000000 --- a/1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.md +++ /dev/null @@ -1,44 +0,0 @@ -实际上解决方案非常简单。 - -就像这样: - -```js -Promise.all( - fetch('https://api.github.com/users/iliakan'), - fetch('https://api.github.com/users/remy'), - fetch('http://no-such-url') -) -``` - -这里我们有一个指向 `Promise.all` 的 `fetch(...)` promise 数组。 - -我们不能改变 `Promise.all` 的工作方式:如果它检测到 error,就会 reject 它。因此我们需要避免任何 error 发生。相反,如果 `fetch` 发生 error,我们需要将其视为“正常”结果。 - -就像这样: - -```js -Promise.all( - fetch('https://api.github.com/users/iliakan').catch(err => err), - fetch('https://api.github.com/users/remy').catch(err => err), - fetch('http://no-such-url').catch(err => err) -) -``` - -换句话说,`.catch` 会对所有的 promise 产生 error,然后正常返回。根据 promise 的工作原理,只要 `.then/catch` 处理器返回值(无论是 error 对象或其他内容),执行流程就会“正常”进行。 - -因此 `.catch` 会将 error 作为“正常”结果返回给外部的 `Promise.all`。 - -代码如下: -```js -Promise.all( - urls.map(url => fetch(url)) -) -``` - -可重写为: - -```js -Promise.all( - urls.map(url => fetch(url).catch(err => err)) -) -``` diff --git a/1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.view/index.html b/1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.view/index.html deleted file mode 100644 index 209e60640a..0000000000 --- a/1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.view/index.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - diff --git a/1-js/11-async/05-promise-api/01-promise-errors-as-results/source.view/index.html b/1-js/11-async/05-promise-api/01-promise-errors-as-results/source.view/index.html deleted file mode 100644 index 241b09ad4a..0000000000 --- a/1-js/11-async/05-promise-api/01-promise-errors-as-results/source.view/index.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - diff --git a/1-js/11-async/05-promise-api/01-promise-errors-as-results/task.md b/1-js/11-async/05-promise-api/01-promise-errors-as-results/task.md deleted file mode 100644 index 086154c14f..0000000000 --- a/1-js/11-async/05-promise-api/01-promise-errors-as-results/task.md +++ /dev/null @@ -1,48 +0,0 @@ -# 容错机制 Promise.all - -我们想要并行获取多个 URL。 - -执行此操作代码如下: - -```js run -let urls = [ - 'https://api.github.com/users/iliakan', - 'https://api.github.com/users/remy', - 'https://api.github.com/users/jeresig' -]; - -Promise.all(urls.map(url => fetch(url))) - // for each response show its status - .then(responses => { // (*) - for(let response of responses) { - alert(`${response.url}: ${response.status}`); - } - )); -``` - -问题是如果任何请求都失败了,那么 `Promise.all` 就会 reject error,而且所有的其他请求结果都会丢失。 - -这并不好。 - -修改代码会导致 `(*)` 行的 `responses` 数组包含成功响应的对象和失败时获取的 error 对象。 - -例如,如果其中一个 URL 失效,那么就会变成这样: - -```js -let urls = [ - 'https://api.github.com/users/iliakan', - 'https://api.github.com/users/remy', - 'http://no-such-url' -]; - -Promise.all(...) // your code to fetch URLs... - // ...and pass fetch errors as members of the resulting array... - .then(responses => { - // 3 urls => 3 array members - alert(responses[0].status); // 200 - alert(responses[1].status); // 200 - alert(responses[2]); // TypeError: failed to fetch (text may vary) - }); -``` - -P.S. 在这个任务中,你无需使用 `response.text()` 或 `response.json()` 来加载完整的请求。只要正确处理 fetch 的 error 即可。 diff --git a/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.md b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.view/index.html b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.view/index.html deleted file mode 100644 index 744efd2b76..0000000000 --- a/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.view/index.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - diff --git a/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/source.view/index.html b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/source.view/index.html deleted file mode 100644 index adb86d41fe..0000000000 --- a/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/source.view/index.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - diff --git a/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/task.md b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/task.md deleted file mode 100644 index 0382e5dda7..0000000000 --- a/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/task.md +++ /dev/null @@ -1,34 +0,0 @@ -# 用 JSON fetch 的容错处理 - -改进之前 任务的解决方案。我们现在只需要调用 `fetch`,但要从给定的 URL 中加载 JSON。 - -这是改进后的代码: - -```js run -let urls = [ - 'https://api.github.com/users/iliakan', - 'https://api.github.com/users/remy', - 'https://api.github.com/users/jeresig' -]; - -// make fetch requests -Promise.all(urls.map(url => fetch(url))) - // map each response to response.json() - .then(responses => Promise.all( - responses.map(r => r.json()) - )) - // show name of each user - .then(users => { // (*) - for(let user of users) { - alert(user.name); - } - }); -``` - -问题是如果任意请求都失败了,那么 `Promise.all` 就会 reject error,而且会丢失其他所有请求的结果。因此上述代码不易于容错,与上一个任务相同。 - -请修改代码后,保证 `(*)` 的数组中包含请求成功解析后的 JSON 和错误的 JSON。 - -请注意,错误可能同时发生在 `fetch`(如果请求失败)和 `response.json()`(如果响应的是无效 JSON)中。在这两种情况下,错误都会成为结果对象的成员。 - -这两种情况沙箱都有。 From bc6537bdff0702ac0f6a328db08f092b1fe027d2 Mon Sep 17 00:00:00 2001 From: LycheeEng Date: Wed, 17 Jul 2019 18:41:22 +0800 Subject: [PATCH 3/5] Sync articel.md with English version --- 1-js/11-async/05-promise-api/article.md | 185 ++++++++++++++++++------ 1 file changed, 137 insertions(+), 48 deletions(-) diff --git a/1-js/11-async/05-promise-api/article.md b/1-js/11-async/05-promise-api/article.md index 448d78cc97..2ab52acd77 100644 --- a/1-js/11-async/05-promise-api/article.md +++ b/1-js/11-async/05-promise-api/article.md @@ -1,26 +1,26 @@ # Promise API -在 `Promise` 类中,有 4 中静态方法。我们在这里做下简单介绍。 +There are 5 static methods in the `Promise` class. We'll quickly cover their use cases here. ## Promise.resolve -语法: +The syntax: ```js let promise = Promise.resolve(value); ``` -根据给定的 `value` 值返回 resolved promise。 +Returns a resolved promise with the given `value`. -等价于: +Same as: ```js let promise = new Promise(resolve => resolve(value)); ``` -当我们已经有一个 value 的时候,就会使用该方法,但希望将它“封装”进 promise。 +The method is used when we already have a value, but would like to have it "wrapped" into a promise. -例如,下面的 `loadCached` 函数会获取 `url` 并记住结果,以便以后对同一 URL 进行调用时可以立即返回: +For instance, the `loadCached` function below fetches the `url` and remembers the result, so that future calls on the same URL return it immediately: ```js function loadCached(url) { @@ -35,59 +35,65 @@ function loadCached(url) { return fetch(url) .then(response => response.text()) .then(text => { - cache[url] = text; + cache.set(url,text); return text; }); } ``` -我们可以使用 `loadCached(url).then(…)`,因为函数保证会返回一个 promise。这是 `Promise.resolve` 在 `(*)` 行的目的:它确保了接口的统一性。我们可以在 `loadCached` 之后使用 `.then`。 +We can use `loadCached(url).then(…)`, because the function is guaranteed to return a promise. That's the purpose `Promise.resolve` serves in the line `(*)`: it makes sure the interface is unified. We can always use `.then` after `loadCached`. ## Promise.reject -语法: +The syntax: ```js let promise = Promise.reject(error); ``` -创建一个带有 `error` 的 rejected promise。 +Create a rejected promise with the `error`. -就像这样: +Same as: ```js let promise = new Promise((resolve, reject) => reject(error)); ``` -我们会在此讨论它的完整性,但在实际工作中,我们很少这样使用。 +We cover it here for completeness, rarely used in real code. ## Promise.all -该方法并行运行多个 promise,并等待所有 promise 准备就绪。 +Let's say we want to run many promises to execute in parallel, and wait till all of them are ready. -语法: +For instance, download several URLs in parallel and process the content when all are done. + +That's what `Promise.all` is for. + +The syntax is: ```js -let promise = Promise.all(iterable); +let promise = Promise.all([...promises...]); ``` -它需要一个带有 promise 的 `iterable` 对象,技术上来说,它是可以迭代的,但通常情况下,它只是一个数组,而且会返回一个新的 promise。新的 promise 是在所有 promise 都被解决并拥有一个存放结果的数组之后才出现的。 +It takes an array of promises (technically can be any iterable, but usually an array) and returns a new promise. + +The new promise resolves when all listed promises are settled and has an array of their results. -例如,下面的 `Promise.all` 在 3 秒之后被处理,然后它的结果就是一个 `[1, 2, 3]` 数组: +For instance, the `Promise.all` below settles after 3 seconds, and then its result is an array `[1, 2, 3]`: ```js run Promise.all([ - new Promise((resolve, reject) => setTimeout(() => resolve(1), 3000)), // 1 - new Promise((resolve, reject) => setTimeout(() => resolve(2), 2000)), // 2 - new Promise((resolve, reject) => setTimeout(() => resolve(3), 1000)) // 3 + new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1 + new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2 + new Promise(resolve => setTimeout(() => resolve(3), 1000)) // 3 ]).then(alert); // 1,2,3 when promises are ready: each promise contributes an array member ``` -注意,它们的相对顺序是相同的。尽管第一个 promise 需要很长的时间来解决,但它仍然是结果数组中的第一个。 +Please note that the relative order is the same. Even though the first promise takes the longest time to resolve, it is still first in the array of results. -常见技巧是将一组作业数据映射到一个 promise 数组,然后再将它们封装进 `Promise.all`。 +A common trick is to map an array of job data into an array of promises, and then wrap that into `Promise.all`. -例如,我们有一个存储 URL 的数组,我们就可以像这样来获取它们: +For instance, if we have an array of URLs, we can fetch them all like this: ```js run let urls = [ @@ -96,7 +102,7 @@ let urls = [ 'https://api.github.com/users/jeresig' ]; -// map every url to the promise fetch(github url) +// map every url to the promise of the fetch let requests = urls.map(url => fetch(url)); // Promise.all waits until all jobs are resolved @@ -106,7 +112,7 @@ Promise.all(requests) )); ``` -一个更真实的示例是通过用户名来为 GitHub 用户数组获取用户信息(或者我们可以通过他们的 id 来获取一系列商品,逻辑都是一样的): +A bigger example with fetching user information for an array of GitHub users by their names (we could fetch an array of goods by their ids, the logic is same): ```js run let names = ['iliakan', 'remy', 'jeresig']; @@ -128,10 +134,9 @@ Promise.all(requests) .then(users => users.forEach(user => alert(user.name))); ``` -如果任何 promise 为 rejected,`Promise.all` 就会立即以 error reject。 - -例如: +**If any of the promises is rejected, the promise returned by `Promise.all` immediately rejects with that error.** +For instance: ```js run Promise.all([ @@ -140,19 +145,23 @@ Promise.all([ new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)), */!* new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)) -]).catch(alert); // 错误:喔! +]).catch(alert); // Error: Whoops! ``` -这里的第二个 promise 在两秒内为 reject。这立即导致了对 `Promise.all` 的 reject。因此 `.catch` 被执行:避免 error 成为整个 `Promise.all` 的结果。 +Here the second promise rejects in two seconds. That leads to immediate rejection of `Promise.all`, so `.catch` executes: the rejection error becomes the outcome of the whole `Promise.all`. + +```warn header="In case of an error, other promises are ignored" +If one promise rejects, `Promise.all` immediately rejects, completely forgetting about the other ones in the list. Their results are ignored. -重要的细节是 promise 没有提供 "cancel" 或 "abort" 执行方法。因此,其他 promise 会继续执行,并最终为 settle,但它们的结果会被忽略。 +For example, if there are multiple `fetch` calls, like in the example above, and one fails, other ones will still continue to execute, but `Promise.all` don't watch them any more. They will probably settle, but the result will be ignored. -有避免这种情况的方法:我们可以编写额外的代码到 `clearTimeout`(或在出现 error 时取消)promise,或者我们可以将 error 作为结果数组中的成员显示出来(参阅本章下的 task)。 +`Promise.all` does nothing to cancel them, as there's no concept of "cancellation" in promises. In [another chapter](info:fetch-abort) we'll cover `AbortController` that can help with that, but it's not a part of the Promise API. +``` -````smart header="`Promise.all(iterable)` 允许在 `iterable` 中无 promise" -通常 `Promise.all(iterable)` 接受可迭代的 promise(大多数情况是数组)。但如果这些对象中的任何一个不是 promise,它就会被封装进 `Promise.resolve`。 +````smart header="`Promise.all(iterable)` allows non-promise \"regular\" values in `iterable`" +Normally, `Promise.all(...)` accepts an iterable (in most cases an array) of promises. But if any of those objects is not a promise, it's wrapped in `Promise.resolve`. -例如。这里的结果是 `[1, 2, 3]`: +For instance, here the results are `[1, 2, 3]`: ```js run Promise.all([ @@ -164,21 +173,98 @@ Promise.all([ ]).then(alert); // 1, 2, 3 ``` -因此我们可以在方便的时候将非 promise 值传递给 `Promise.all`。 - +So we are able to pass ready values to `Promise.all` where convenient. ```` +## Promise.allSettled + +[recent browser="new"] + +`Promise.all` rejects as a whole if any promise rejects. That's good in cases, when we need *all* results to go on: + +```js +Promise.all([ + fetch('/template.html'), + fetch('/style.css'), + fetch('/data.json') +]).then(render); // render method needs them all +``` + +`Promise.allSettled` waits for all promises to settle: even if one rejects, it waits for the others. The resulting array has: + +- `{status:"fulfilled", value:result}` for successful responses, +- `{status:"rejected", reason:error}` for errors. + +For example, we'd like to fetch the information about multiple users. Even if one request fails, we're interested in the others. + +Let's use `Promise.allSettled`: + +```js run +let urls = [ + 'https://api.github.com/users/iliakan', + 'https://api.github.com/users/remy', + 'https://no-such-url' +]; + +Promise.allSettled(urls.map(url => fetch(url))) + .then(results => { // (*) + results.forEach((result, num) => { + if (result.status == "fulfilled") { + alert(`${urls[num]}: ${result.value.status}`); + } + if (result.status == "rejected") { + alert(`${urls[num]}: ${result.reason}`); + } + }); + }); +``` + +The `results` in the line `(*)` above will be: +```js +[ + {status: 'fulfilled', value: ...response...}, + {status: 'fulfilled', value: ...response...}, + {status: 'rejected', reason: ...error object...} +] +``` + +So, for each promise we get its status and `value/reason`. + +### Polyfill + +If the browser doesn't support `Promise.allSettled`, it's easy to polyfill: + +```js +if(!Promise.allSettled) { + Promise.allSettled = function(promises) { + return Promise.all(promises.map(p => Promise.resolve(p).then(v => ({ + state: 'fulfilled', + value: v, + }), r => ({ + state: 'rejected', + reason: r, + })))); + }; +} +``` + +In this code, `promises.map` takes input values, turns into promises (just in case a non-promise was passed) with `p => Promise.resolve(p)`, and then adds `.then` handler to it. + +That handler turns a successful result `v` into `{state:'fulfilled', value:v}`, and an error `r` into `{state:'rejected', reason:r}`. That's exactly the format of `Promise.allSettled`. + +Then we can use `Promise.allSettled` to get the results or *all* given promises, even if some of them reject. + ## Promise.race -与 `Promise.all` 类似,所有的 promise 都是可迭代的,但不会等待所有都完成 —— 只等待第一个完成(或者有 error),然后继续执行。 +Similar to `Promise.all`, it takes an iterable of promises, but instead of waiting for all of them to finish, it waits for the first result (or error), and goes on with it. -语法是: +The syntax is: ```js let promise = Promise.race(iterable); ``` -例如,这里的结果回事 `1`: +For instance, here the result will be `1`: ```js run Promise.race([ @@ -188,15 +274,18 @@ Promise.race([ ]).then(alert); // 1 ``` -因此,第一个结果/错误会成为整个 `Promise.race` 的结果。在第一个 promise "wins the race" 被解决后,所有的深层的结果/错误都会被忽略。 +So, the first result/error becomes the result of the whole `Promise.race`. After the first settled promise "wins the race", all further results/errors are ignored. -## 总结 +## Summary -`Promise` 类有 4 中静态方法: +There are 5 static methods of `Promise` class: -1. `Promise.resolve(value)` —— 根据给定值返回 resolved promise, -2. `Promise.reject(error)` —— 根据给定错误返回 rejected promise, -3. `Promise.all(promises)` —— 等待所有的 promise 为 resolve 时返回存放它们结果的数组。如果任意给定的 promise 为 reject,那么它就会变成 `Promise.all` 的错误结果,所以所有的其他结果都会被忽略。 -4. `Promise.race(promises)` —— 等待第一个 promise 被解决,其结果/错误即为结果。 +1. `Promise.resolve(value)` -- makes a resolved promise with the given value. +2. `Promise.reject(error)` -- makes a rejected promise with the given error. +3. `Promise.all(promises)` -- waits for all promises to resolve and returns an array of their results. If any of the given promises rejects, then it becomes the error of `Promise.all`, and all other results are ignored. +4. `Promise.allSettled(promises)` (a new method) -- waits for all promises to resolve or reject and returns an array of their results as object with: + - `state`: `'fulfilled'` or `'rejected'` + - `value` (if fulfilled) or `reason` (if rejected). +5. `Promise.race(promises)` -- waits for the first promise to settle, and its result/error becomes the outcome. -这四个方法中,`Promise.all` 在实战中使用的最多。 +Of these five, `Promise.all` is probably the most common in practice. From 1b2e81b1731e215d0662922ede8d629707ca009e Mon Sep 17 00:00:00 2001 From: LycheeEng Date: Wed, 17 Jul 2019 22:07:35 +0800 Subject: [PATCH 4/5] Translate article.md --- 1-js/11-async/05-promise-api/article.md | 140 ++++++++++++------------ 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/1-js/11-async/05-promise-api/article.md b/1-js/11-async/05-promise-api/article.md index 2ab52acd77..8f1a9e5f0b 100644 --- a/1-js/11-async/05-promise-api/article.md +++ b/1-js/11-async/05-promise-api/article.md @@ -1,26 +1,26 @@ # Promise API -There are 5 static methods in the `Promise` class. We'll quickly cover their use cases here. +在 `Promise` 类中,有 5 种静态方法。我们在这里简单介绍下它们的使用场景。 ## Promise.resolve -The syntax: +语法: ```js let promise = Promise.resolve(value); ``` -Returns a resolved promise with the given `value`. +根据给定的 `value` 值返回 resolved promise。 -Same as: +等价于: ```js let promise = new Promise(resolve => resolve(value)); ``` -The method is used when we already have a value, but would like to have it "wrapped" into a promise. +当我们已经有一个 value 的时候,就会使用该方法,但希望将它“封装”进 promise。 -For instance, the `loadCached` function below fetches the `url` and remembers the result, so that future calls on the same URL return it immediately: +例如,下面的 `loadCached` 函数会获取 `url` 并记住结果,以便以后对同一 URL 进行调用时可以立即返回: ```js function loadCached(url) { @@ -41,59 +41,59 @@ function loadCached(url) { } ``` -We can use `loadCached(url).then(…)`, because the function is guaranteed to return a promise. That's the purpose `Promise.resolve` serves in the line `(*)`: it makes sure the interface is unified. We can always use `.then` after `loadCached`. +我们可以使用 `loadCached(url).then(…)`,因为该函数必定返回一个 promise。这是 `Promise.resolve` 在 `(*)` 行的目的:它确保了接口的统一性。我们可以在 `loadCached` 之后使用 `.then`。 ## Promise.reject -The syntax: +语法: ```js let promise = Promise.reject(error); ``` -Create a rejected promise with the `error`. +创建一个带有 `error` 的 rejected promise。 -Same as: +就像这样: ```js let promise = new Promise((resolve, reject) => reject(error)); ``` -We cover it here for completeness, rarely used in real code. +我们会在此讨论它的完整性,但在实际工作中,我们很少这样使用。 ## Promise.all -Let's say we want to run many promises to execute in parallel, and wait till all of them are ready. +假设我想要并行执行多个 promise,并等待所有 promise 准备就绪。 -For instance, download several URLs in parallel and process the content when all are done. +例如,并行下载几个 URL 并等到所有内容都下载完毕后才开始处理它们。 -That's what `Promise.all` is for. +这就是 `Promise.all` 的用途: -The syntax is: +语法: ```js let promise = Promise.all([...promises...]); ``` -It takes an array of promises (technically can be any iterable, but usually an array) and returns a new promise. +它需要一个 promise 的数组作为其参数(严格来说可以是任何可迭代对象,但通常都是数组)并返回一个新的 promise。 -The new promise resolves when all listed promises are settled and has an array of their results. +当所有给定的 promise 都被处理并以数组的形式呈现其结果时,新的 promise 也就被 resolve。 -For instance, the `Promise.all` below settles after 3 seconds, and then its result is an array `[1, 2, 3]`: +例如,下面的 `Promise.all` 在 3 秒之后被处理,然后它的结果就是一个 `[1, 2, 3]` 数组: ```js run Promise.all([ new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1 new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2 new Promise(resolve => setTimeout(() => resolve(3), 1000)) // 3 -]).then(alert); // 1,2,3 when promises are ready: each promise contributes an array member +]).then(alert); // 1,2,3 当 promise 就绪:每一个 promise 即成为数组中的一员 ``` -Please note that the relative order is the same. Even though the first promise takes the longest time to resolve, it is still first in the array of results. +请注意,它们的相对顺序是相同的。即使第一个 promise 需要很长的时间来 resolve,但它仍然是结果数组中的第一个。 -A common trick is to map an array of job data into an array of promises, and then wrap that into `Promise.all`. +常见技巧是将一组作业数据映射到一个 promise 数组,然后再将它们封装进 `Promise.all`。 -For instance, if we have an array of URLs, we can fetch them all like this: +例如,假设我们有一个存储 URL 的数组,我们就可以像这样来获取它们: ```js run let urls = [ @@ -102,17 +102,17 @@ let urls = [ 'https://api.github.com/users/jeresig' ]; -// map every url to the promise of the fetch +// 将每个 url 映射到 fetch 的 promise 中 let requests = urls.map(url => fetch(url)); -// Promise.all waits until all jobs are resolved +// Promise.all 等待所有作业都被 resolve Promise.all(requests) .then(responses => responses.forEach( response => alert(`${response.url}: ${response.status}`) )); ``` -A bigger example with fetching user information for an array of GitHub users by their names (we could fetch an array of goods by their ids, the logic is same): +一个更真实的示例是通过用户名来为一组 GitHub 用户获取他们的信息(或者我们可以通过他们的 id 来获取一系列商品,逻辑都是一样的): ```js run let names = ['iliakan', 'remy', 'jeresig']; @@ -121,22 +121,22 @@ let requests = names.map(name => fetch(`https://api.github.com/users/${name}`)); Promise.all(requests) .then(responses => { - // all responses are ready, we can show HTTP status codes + // 所有响应都就绪时,我们可以显示 HTTP 状态码 for(let response of responses) { - alert(`${response.url}: ${response.status}`); // shows 200 for every url + alert(`${response.url}: ${response.status}`); // 每个 url 都显示 200 } return responses; }) - // map array of responses into array of response.json() to read their content + // 映射 response 数组到 response.json() 中以读取它们的内容 .then(responses => Promise.all(responses.map(r => r.json()))) - // all JSON answers are parsed: "users" is the array of them + // 所有 JSON 结果都被解析:“users” 是它们的数组 .then(users => users.forEach(user => alert(user.name))); ``` -**If any of the promises is rejected, the promise returned by `Promise.all` immediately rejects with that error.** +**如果任意一个 promise 为 reject,`Promise.all` 返回的 promise 就会立即 reject 这个错误。** -For instance: +例如: ```js run Promise.all([ @@ -145,59 +145,59 @@ Promise.all([ new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)), */!* new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)) -]).catch(alert); // Error: Whoops! +]).catch(alert); // 错误:Whoops! ``` -Here the second promise rejects in two seconds. That leads to immediate rejection of `Promise.all`, so `.catch` executes: the rejection error becomes the outcome of the whole `Promise.all`. +这里的第二个 promise 在两秒内被 reject。这立即导致了对 `Promise.all` 的 reject。因此 `.catch` 被执行:reject 的错误成为整个 `Promise.all` 的结果。 -```warn header="In case of an error, other promises are ignored" -If one promise rejects, `Promise.all` immediately rejects, completely forgetting about the other ones in the list. Their results are ignored. +```warn header="如果出现错误,其他 promise 就会被忽略" +如果其中一个 promise 被 reject,`Promise.all` 就会立即被 reject 并忽略所有列表中其他的 promise。它们的结果也被忽略。 -For example, if there are multiple `fetch` calls, like in the example above, and one fails, other ones will still continue to execute, but `Promise.all` don't watch them any more. They will probably settle, but the result will be ignored. +例如,像上面例子中提到的那样,如果同时进行多个 `fetch` 操作,其中一个失败,其他的 `fetch` 操作仍然会继续执行,但是 `Promise.all` 会忽略它们。它们可能已经解决了某些问题,但是结果将会被忽略。 -`Promise.all` does nothing to cancel them, as there's no concept of "cancellation" in promises. In [another chapter](info:fetch-abort) we'll cover `AbortController` that can help with that, but it's not a part of the Promise API. +没有什么方法能取消 `Promise.all`,因为 promise 中没有 “cancellation” 这类概念。在 [其他章节](info:fetch-abort) 我们将会讨论可以“取消” promise 的 `AbortController`,但它不是 Promise API 的一部分。 ``` -````smart header="`Promise.all(iterable)` allows non-promise \"regular\" values in `iterable`" -Normally, `Promise.all(...)` accepts an iterable (in most cases an array) of promises. But if any of those objects is not a promise, it's wrapped in `Promise.resolve`. +````smart header="`Promise.all(iterable)` 允许“迭代”中的非 promise(non-promise)的 \“常规\” 值" +通常,`Promise.all(...)` 接受可迭代的 promise(大部分情况下是数组)。但是如果这些对象中的任意一个不是 promise,它将会被直接包装进 `Promise.resolve`。 -For instance, here the results are `[1, 2, 3]`: +例如,这里的结果是 `[1, 2, 3]`: ```js run Promise.all([ new Promise((resolve, reject) => { setTimeout(() => resolve(1), 1000) }), - 2, // treated as Promise.resolve(2) - 3 // treated as Promise.resolve(3) + 2, // 视为 Promise.resolve(2) + 3 // 视为 Promise.resolve(3) ]).then(alert); // 1, 2, 3 ``` -So we are able to pass ready values to `Promise.all` where convenient. +所以我们可以很方便的将准备好的值传递给 `Promise.all`。 ```` ## Promise.allSettled [recent browser="new"] -`Promise.all` rejects as a whole if any promise rejects. That's good in cases, when we need *all* results to go on: +如果任意 promise reject,`Promise.all` 整个将会 reject。当我们需要*所有*结果来做些什么的时候,这样的情况就很好: ```js Promise.all([ fetch('/template.html'), fetch('/style.css'), fetch('/data.json') -]).then(render); // render method needs them all +]).then(render); // render 方法需要上面所有数据 ``` -`Promise.allSettled` waits for all promises to settle: even if one rejects, it waits for the others. The resulting array has: +`Promise.allSettled` 等待所有的 promise 都被处理:即使其中一个 reject,它仍然会等待其他的 promise。处理完成后的数组有: -- `{status:"fulfilled", value:result}` for successful responses, -- `{status:"rejected", reason:error}` for errors. +- `{status:"fulfilled", value:result}` 对于成功的响应, +- `{status:"rejected", reason:error}` 对于错误的响应。 -For example, we'd like to fetch the information about multiple users. Even if one request fails, we're interested in the others. +例如,我们想要获取多个用户的信息。即使其中一个请求失败,我们仍然对其他的感兴趣。 -Let's use `Promise.allSettled`: +让我们使用 `Promise.allSettled`: ```js run let urls = [ @@ -219,7 +219,7 @@ Promise.allSettled(urls.map(url => fetch(url))) }); ``` -The `results` in the line `(*)` above will be: +上面的 `(*)` 行,`results` 将会是: ```js [ {status: 'fulfilled', value: ...response...}, @@ -228,11 +228,11 @@ The `results` in the line `(*)` above will be: ] ``` -So, for each promise we get its status and `value/reason`. +因此,对于每个 promise,我们都能获取其状态(status)和 `value/reason`。 ### Polyfill -If the browser doesn't support `Promise.allSettled`, it's easy to polyfill: +如果浏览器不支持 `Promise.allSettled`,使用 polyfill 很容易让其支持: ```js if(!Promise.allSettled) { @@ -248,23 +248,23 @@ if(!Promise.allSettled) { } ``` -In this code, `promises.map` takes input values, turns into promises (just in case a non-promise was passed) with `p => Promise.resolve(p)`, and then adds `.then` handler to it. +在这段代码中,`promises.map` 获取输入值,并使用 `p => Promise.resolve(p)` 将该值转换为 promise(以防传递了非 promise),然后向其添加 `.then` 处理器。 -That handler turns a successful result `v` into `{state:'fulfilled', value:v}`, and an error `r` into `{state:'rejected', reason:r}`. That's exactly the format of `Promise.allSettled`. +这个处理器将成功的结果 `v` 转换为 `{state:'fulfilled', value:v}`,将错误的结果 `r` 转换为 `{state:'rejected', reason:r}`。这正是 `Promise.allSettled` 的格式。 -Then we can use `Promise.allSettled` to get the results or *all* given promises, even if some of them reject. +然后我们就可以使用 `Promise.allSettled` 来获取结果或*所有*给出的 promise,即使其中一些被 reject。 ## Promise.race -Similar to `Promise.all`, it takes an iterable of promises, but instead of waiting for all of them to finish, it waits for the first result (or error), and goes on with it. +与 `Promise.all` 类似,它获取可迭代的 promise,但是它只等待第一个完成(或者 error)而不会等待所有都完成,然后继续执行。 -The syntax is: +语法: ```js let promise = Promise.race(iterable); ``` -For instance, here the result will be `1`: +例如,这里的结果会是 `1`: ```js run Promise.race([ @@ -274,18 +274,18 @@ Promise.race([ ]).then(alert); // 1 ``` -So, the first result/error becomes the result of the whole `Promise.race`. After the first settled promise "wins the race", all further results/errors are ignored. +因此,第一个结果/错误会成为整个 `Promise.race` 的结果。在第一个 promise 被解决(“赢得比赛[wins the race]”)后,所有后面的结果/错误都会被忽略。 -## Summary +## 总结 -There are 5 static methods of `Promise` class: +`Promise` 类有 5 种静态方法: -1. `Promise.resolve(value)` -- makes a resolved promise with the given value. -2. `Promise.reject(error)` -- makes a rejected promise with the given error. -3. `Promise.all(promises)` -- waits for all promises to resolve and returns an array of their results. If any of the given promises rejects, then it becomes the error of `Promise.all`, and all other results are ignored. -4. `Promise.allSettled(promises)` (a new method) -- waits for all promises to resolve or reject and returns an array of their results as object with: - - `state`: `'fulfilled'` or `'rejected'` - - `value` (if fulfilled) or `reason` (if rejected). -5. `Promise.race(promises)` -- waits for the first promise to settle, and its result/error becomes the outcome. +1. `Promise.resolve(value)` - 根据给定值返回 resolved promise。 +2. `Promise.reject(error)` - 根据给定错误返回 rejected promise。 +3. `Promise.all(promises)` - 等待所有的 promise 为 resolve 时返回存放它们结果的数组。如果任意给定的 promise 为 reject,那么它就会变成 `Promise.all` 的错误结果,所有的其他结果都会被忽略。 +4. `Promise.allSettled(promises)` (新方法) - 等待所有 promise resolve 或者 reject,并以对象形式返回它们结果数组: + - `state`:`‘fulfilled’` 或 `‘rejected’` + - `value`(如果 fulfilled)或 `reason`(如果 rejected) +5. `Promise.race(promises)` - 等待第一个 promise 被解决,其结果/错误即为结果。 -Of these five, `Promise.all` is probably the most common in practice. +这五个方法中,`Promise.all` 在实战中使用的最多。 From c61b197b6f244e51d01c81bb47f7f7f3a5cf871c Mon Sep 17 00:00:00 2001 From: LycheeEng Date: Thu, 18 Jul 2019 09:43:08 +0800 Subject: [PATCH 5/5] Update article with review suggestions --- 1-js/11-async/05-promise-api/article.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/1-js/11-async/05-promise-api/article.md b/1-js/11-async/05-promise-api/article.md index 8f1a9e5f0b..bcfa4a1a7f 100644 --- a/1-js/11-async/05-promise-api/article.md +++ b/1-js/11-async/05-promise-api/article.md @@ -145,7 +145,7 @@ Promise.all([ new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)), */!* new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)) -]).catch(alert); // 错误:Whoops! +]).catch(alert); // Error: Whoops! ``` 这里的第二个 promise 在两秒内被 reject。这立即导致了对 `Promise.all` 的 reject。因此 `.catch` 被执行:reject 的错误成为整个 `Promise.all` 的结果。 @@ -159,7 +159,7 @@ Promise.all([ ``` ````smart header="`Promise.all(iterable)` 允许“迭代”中的非 promise(non-promise)的 \“常规\” 值" -通常,`Promise.all(...)` 接受可迭代的 promise(大部分情况下是数组)。但是如果这些对象中的任意一个不是 promise,它将会被直接包装进 `Promise.resolve`。 +通常,`Promise.all(...)` 接受可迭代的 promise 集合(大部分情况下是数组)。但是如果这些对象中的任意一个不是 promise,它将会被直接包装进 `Promise.resolve`。 例如,这里的结果是 `[1, 2, 3]`: @@ -256,7 +256,7 @@ if(!Promise.allSettled) { ## Promise.race -与 `Promise.all` 类似,它获取可迭代的 promise,但是它只等待第一个完成(或者 error)而不会等待所有都完成,然后继续执行。 +与 `Promise.all` 类似,它接受一个可迭代的 promise 集合,但是它只等待第一个完成(或者 error)而不会等待所有都完成,然后继续执行。 语法: