[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"content-passing-data-between-promise-callbacks":3,"$f1kbrd54xcft0d":22},{"id":4,"type":5,"slug":6,"title":7,"date":8,"category":9,"tags":10,"body_markdown":14,"permalink":15,"excerpt_src":15,"media_type":15,"media_title":15,"media_author":15,"media_url":15,"rating":15,"layout":15,"pv":16,"admin_only":16,"created_at":17,"updated_at":17,"deleted_at":15,"status":18,"html":19,"excerpt":20,"cover":21},175,"article","passing-data-between-promise-callbacks","【译】在Promise回调之间传值的方法","2017-08-22 13:00:00","web",[11,12,13],"JavaScript","Promise","异步","\n在基于 Promise 编写的代码中，经常会有很多回调函数，它们都有各自的变量作用域。那么如果我们需要在这些回调函数之间共享数据，要怎么办呢？本文总结了一些方法。\n\n## 1. 问题\n\n下面的代码演示了使用 Promise 回调时经常碰到的一类问题：变量`connection`（A）在一个作用域中存在，但是需要被另一个作用域访问（B和C）：\n\n```javascript\ndb.open()\n.then(connection => { \u002F\u002F (A)\n    return connection.select({ name: 'Jane' });\n})\n.then(result => {\n    \u002F\u002F Process result\n    \u002F\u002F Use `connection` to make more queries (B)\n})\n···\n.catch(error => {\n    \u002F\u002F handle errors\n})\n.finally(() => {\n    connection.close(); \u002F\u002F (C)\n});\n```\n\n在这段代码中，我们使用了 ES 规范中的`Promise.prototype.finally()`。它提供了和`try`语句的`finally`分支类似的功能。\n\n\u003C!-- more -->\n\n## 2. 解决方法：副作用\n\n第一种解决方法是将要共享的值`connection`存入这些回调函数的上级作用域（A）：\n\n```javascript\nlet connection; \u002F\u002F (A)\ndb.open()\n.then(conn => {\n    connection = conn;\n    return connection.select({ name: 'Jane' });\n})\n.then(result => {\n    \u002F\u002F Process result\n    \u002F\u002F Use `connection` to make more queries (B)\n})\n···\n.catch(error => {\n    \u002F\u002F handle errors\n})\n.finally(() => {\n    connection.close(); \u002F\u002F (C)\n});\n```\n\n因为`connection`的定义在回调函数的外面，所以 B 和 C 都能访问它。\n\n## 3. 解决方法：嵌套作用域\n\n上面例子的同步版本，看起来是这样的：\n\n```javascript\ntry {\n    const connection = await db.open();\n    const result = await connection.select({ name: 'Jane' });\n    ···\n} catch (error) {\n    \u002F\u002F handle errors\n} finally {\n    connection.close();\n}\n```\n\n同步版本的代码中，使得`connection`在函数内部可用的方法是将声明提前到上级作用域中：\n\n```javascript\nconst connection = await db.open();\ntry {\n    const result = await connection.select({ name: 'Jane' });\n    ···\n} catch (error) {\n    \u002F\u002F handle errors\n} finally {\n    connection.close();\n}\n```\n\n> 译注：`try...catch`中，`catch`和`finally`可以共享`try`中的变量，所以此处将`connection`移到外部定义，对于同步代码来说，是非必需的。\n\n我们可以在 Promise 中做同样的事情——将 Promise 链起来：\n\n```javascript\ndb.open() \u002F\u002F (A)\n.then(connection => { \u002F\u002F (B)\n    return connection.select({ name: 'Jane' }) \u002F\u002F (C)\n    .then(result => {\n        \u002F\u002F Process result\n        \u002F\u002F Use `connection` to make more queries\n    })\n    ···\n    .catch(error => {\n        \u002F\u002F handle errors\n    })\n    .finally(() => {\n        connection.close();\n    });\n})\n```\n\n这段代码有两个 Promise 链：\n\n- 第一个开始于 A ，`connection`是`db.open()`的结果\n- 第二个被包裹在 B 处的`.then()`中，从 C 处开始，注意 C 处的`return`将两个 Promise 连接起来了\n\n你可能已经注意到了，不管是同步版本还是异步版本的代码，如果`db.open()`同步抛出一个错误，这个错误将不能被`catch`处理。有[一篇专门的关于 `Promise.try()`](http:\u002F\u002F2ality.com\u002F2017\u002F08\u002Fpromise-try.html)将演示在异步版本中如何修复这个问题。在同步版本的代码中，你可以将`db.open()`移入`try`中即可。\n\n## 4. 解决方法：返回多值\n\n下面将演示另一种在回调函数之间传值的方法。但是它不是任何时候都能工作，尤其是你不能将它用于前面演示的数据库操作中。我们来看一个它能工作的例子。\n\n我们面临一个相似的问题：在 Promise 链中，需要将`intermediate`的值从 A 处的回调传递到 B 处的回调。\n\n```javascript\nreturn asyncFunc1()\n.then(result1 => { \u002F\u002F (A)\n    const intermediate = ···;\n    return asyncFunc2();\n})\n.then(result2 => { \u002F\u002F (B)\n    console.log(intermediate);\n    ···\n});\n```\n\n我们使用`Promise.all()`从第一个回调函数中传递多个值给第二个回调函数，从而解决这个问题：\n\n```javascript\nreturn asyncFunc1()\n.then(result1 => {\n    const intermediate = ···;\n    return Promise.all([asyncFunc2(), intermediate]); \u002F\u002F (A)\n})\n.then(([result2, intermediate]) => {\n    console.log(intermediate);\n    ···\n});\n```\n\n注意，在 A 处返回一个数组是不行的，因为`.then()`会获得一个Promise，一个值。使用`Promise.all()`时，内部会使用`Promise.resolve()`来保证数组元素都是 Promise ，并且在它们全部被满足（fulfill）时，将它们的值组成一个数组传递给作为下一个回调的参数。\n\n这种方法的局限性在于你不能传值到`.catch()`或者`.finally()`回调中。\n\n最后，这种方法还可以在`Promise.all()`中传入一个对象（不仅仅可以是数组），这样每一个返回的值都有一个标签。\n\n> 译注：`Promise.all()`目前并不支持传入对象，作者应该是希望支持这样一种使用方式。\n\n## 5. 相关链接\n\n- 在 Exploring ES6 的\"[Promises for asynchronous programming](http:\u002F\u002Fexploringjs.com\u002Fes6\u002Fch_promises.html)\"章节，有关于 Promise 链的更多内容\n- [ES提案：`Promise.prototype.finally()`](http:\u002F\u002F2ality.com\u002F2017\u002F07\u002Fpromise-prototype-finally.html)\n- [ES提案：`Promise.try()`](http:\u002F\u002F2ality.com\u002F2017\u002F08\u002Fpromise-try.html)\n\n作者：Dr. Axel Rauschmayer\n\n原文链接：\u003Chttp:\u002F\u002F2ality.com\u002F2017\u002F08\u002Fpromise-callback-data-flow.html>\n",null,0,"2026-08-28 04:37:17","published","\u003Cp>在基于 Promise 编写的代码中，经常会有很多回调函数，它们都有各自的变量作用域。那么如果我们需要在这些回调函数之间共享数据，要怎么办呢？本文总结了一些方法。\u003C\u002Fp>\n\u003Ch2>1. 问题\u003C\u002Fh2>\n\u003Cp>下面的代码演示了使用 Promise 回调时经常碰到的一类问题：变量\u003Ccode>connection\u003C\u002Fcode>（A）在一个作用域中存在，但是需要被另一个作用域访问（B和C）：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>db.open()\n.then(connection =&gt; { \u002F\u002F (A)\n    return connection.select({ name: 'Jane' });\n})\n.then(result =&gt; {\n    \u002F\u002F Process result\n    \u002F\u002F Use `connection` to make more queries (B)\n})\n···\n.catch(error =&gt; {\n    \u002F\u002F handle errors\n})\n.finally(() =&gt; {\n    connection.close(); \u002F\u002F (C)\n});\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>在这段代码中，我们使用了 ES 规范中的\u003Ccode>Promise.prototype.finally()\u003C\u002Fcode>。它提供了和\u003Ccode>try\u003C\u002Fcode>语句的\u003Ccode>finally\u003C\u002Fcode>分支类似的功能。\u003C\u002Fp>\n\u003Ch2>2. 解决方法：副作用\u003C\u002Fh2>\n\u003Cp>第一种解决方法是将要共享的值\u003Ccode>connection\u003C\u002Fcode>存入这些回调函数的上级作用域（A）：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>let connection; \u002F\u002F (A)\ndb.open()\n.then(conn =&gt; {\n    connection = conn;\n    return connection.select({ name: 'Jane' });\n})\n.then(result =&gt; {\n    \u002F\u002F Process result\n    \u002F\u002F Use `connection` to make more queries (B)\n})\n···\n.catch(error =&gt; {\n    \u002F\u002F handle errors\n})\n.finally(() =&gt; {\n    connection.close(); \u002F\u002F (C)\n});\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>因为\u003Ccode>connection\u003C\u002Fcode>的定义在回调函数的外面，所以 B 和 C 都能访问它。\u003C\u002Fp>\n\u003Ch2>3. 解决方法：嵌套作用域\u003C\u002Fh2>\n\u003Cp>上面例子的同步版本，看起来是这样的：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>try {\n    const connection = await db.open();\n    const result = await connection.select({ name: 'Jane' });\n    ···\n} catch (error) {\n    \u002F\u002F handle errors\n} finally {\n    connection.close();\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>同步版本的代码中，使得\u003Ccode>connection\u003C\u002Fcode>在函数内部可用的方法是将声明提前到上级作用域中：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>const connection = await db.open();\ntry {\n    const result = await connection.select({ name: 'Jane' });\n    ···\n} catch (error) {\n    \u002F\u002F handle errors\n} finally {\n    connection.close();\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cblockquote>\n\u003Cp>译注：\u003Ccode>try...catch\u003C\u002Fcode>中，\u003Ccode>catch\u003C\u002Fcode>和\u003Ccode>finally\u003C\u002Fcode>可以共享\u003Ccode>try\u003C\u002Fcode>中的变量，所以此处将\u003Ccode>connection\u003C\u002Fcode>移到外部定义，对于同步代码来说，是非必需的。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>我们可以在 Promise 中做同样的事情——将 Promise 链起来：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>db.open() \u002F\u002F (A)\n.then(connection =&gt; { \u002F\u002F (B)\n    return connection.select({ name: 'Jane' }) \u002F\u002F (C)\n    .then(result =&gt; {\n        \u002F\u002F Process result\n        \u002F\u002F Use `connection` to make more queries\n    })\n    ···\n    .catch(error =&gt; {\n        \u002F\u002F handle errors\n    })\n    .finally(() =&gt; {\n        connection.close();\n    });\n})\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>这段代码有两个 Promise 链：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>第一个开始于 A ，\u003Ccode>connection\u003C\u002Fcode>是\u003Ccode>db.open()\u003C\u002Fcode>的结果\u003C\u002Fli>\n\u003Cli>第二个被包裹在 B 处的\u003Ccode>.then()\u003C\u002Fcode>中，从 C 处开始，注意 C 处的\u003Ccode>return\u003C\u002Fcode>将两个 Promise 连接起来了\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>你可能已经注意到了，不管是同步版本还是异步版本的代码，如果\u003Ccode>db.open()\u003C\u002Fcode>同步抛出一个错误，这个错误将不能被\u003Ccode>catch\u003C\u002Fcode>处理。有\u003Ca href=\"http:\u002F\u002F2ality.com\u002F2017\u002F08\u002Fpromise-try.html\">一篇专门的关于 \u003Ccode>Promise.try()\u003C\u002Fcode>\u003C\u002Fa>将演示在异步版本中如何修复这个问题。在同步版本的代码中，你可以将\u003Ccode>db.open()\u003C\u002Fcode>移入\u003Ccode>try\u003C\u002Fcode>中即可。\u003C\u002Fp>\n\u003Ch2>4. 解决方法：返回多值\u003C\u002Fh2>\n\u003Cp>下面将演示另一种在回调函数之间传值的方法。但是它不是任何时候都能工作，尤其是你不能将它用于前面演示的数据库操作中。我们来看一个它能工作的例子。\u003C\u002Fp>\n\u003Cp>我们面临一个相似的问题：在 Promise 链中，需要将\u003Ccode>intermediate\u003C\u002Fcode>的值从 A 处的回调传递到 B 处的回调。\u003C\u002Fp>\n\u003Cpre>\u003Ccode>return asyncFunc1()\n.then(result1 =&gt; { \u002F\u002F (A)\n    const intermediate = ···;\n    return asyncFunc2();\n})\n.then(result2 =&gt; { \u002F\u002F (B)\n    console.log(intermediate);\n    ···\n});\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>我们使用\u003Ccode>Promise.all()\u003C\u002Fcode>从第一个回调函数中传递多个值给第二个回调函数，从而解决这个问题：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>return asyncFunc1()\n.then(result1 =&gt; {\n    const intermediate = ···;\n    return Promise.all([asyncFunc2(), intermediate]); \u002F\u002F (A)\n})\n.then(([result2, intermediate]) =&gt; {\n    console.log(intermediate);\n    ···\n});\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>注意，在 A 处返回一个数组是不行的，因为\u003Ccode>.then()\u003C\u002Fcode>会获得一个Promise，一个值。使用\u003Ccode>Promise.all()\u003C\u002Fcode>时，内部会使用\u003Ccode>Promise.resolve()\u003C\u002Fcode>来保证数组元素都是 Promise ，并且在它们全部被满足（fulfill）时，将它们的值组成一个数组传递给作为下一个回调的参数。\u003C\u002Fp>\n\u003Cp>这种方法的局限性在于你不能传值到\u003Ccode>.catch()\u003C\u002Fcode>或者\u003Ccode>.finally()\u003C\u002Fcode>回调中。\u003C\u002Fp>\n\u003Cp>最后，这种方法还可以在\u003Ccode>Promise.all()\u003C\u002Fcode>中传入一个对象（不仅仅可以是数组），这样每一个返回的值都有一个标签。\u003C\u002Fp>\n\u003Cblockquote>\n\u003Cp>译注：\u003Ccode>Promise.all()\u003C\u002Fcode>目前并不支持传入对象，作者应该是希望支持这样一种使用方式。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2>5. 相关链接\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>在 Exploring ES6 的&quot;\u003Ca href=\"http:\u002F\u002Fexploringjs.com\u002Fes6\u002Fch_promises.html\">Promises for asynchronous programming\u003C\u002Fa>&quot;章节，有关于 Promise 链的更多内容\u003C\u002Fli>\n\u003Cli>\u003Ca href=\"http:\u002F\u002F2ality.com\u002F2017\u002F07\u002Fpromise-prototype-finally.html\">ES提案：\u003Ccode>Promise.prototype.finally()\u003C\u002Fcode>\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>\u003Ca href=\"http:\u002F\u002F2ality.com\u002F2017\u002F08\u002Fpromise-try.html\">ES提案：\u003Ccode>Promise.try()\u003C\u002Fcode>\u003C\u002Fa>\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>作者：Dr. Axel Rauschmayer\u003C\u002Fp>\n\u003Cp>原文链接：\u003Ca href=\"http:\u002F\u002F2ality.com\u002F2017\u002F08\u002Fpromise-callback-data-flow.html\">http:\u002F\u002F2ality.com\u002F2017\u002F08\u002Fpromise-callback-data-flow.html\u003C\u002Fa>\u003C\u002Fp>\n","\u003Cp>在基于 Promise 编写的代码中，经常会有很多回调函数，它们都有各自的变量作用域。那么如果我们需要在这些回调函数之间共享数据，要怎么办呢？本文总结了一些方法。\u003C\u002Fp>\n\u003Ch2>1. 问题\u003C\u002Fh2>\n\u003Cp>下面的代码演示了使用 Promise 回调时经常碰到的一类问题：变量\u003Ccode>connection\u003C\u002Fcode>（A）在一个作用域中存在，但是需要被另一个作用域访问（B和C）：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>db.open()\n.then(connection =&gt; { \u002F\u002F (A)\n    return connection.select({ name: 'Jane' });\n})\n.then(result =&gt; {\n    \u002F\u002F Process result\n    \u002F\u002F Use `connection` to make more queries (B)\n})\n···\n.catch(error =&gt; {\n    \u002F\u002F handle errors\n})\n.finally(() =&gt; {\n    connection.close(); \u002F\u002F (C)\n});\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>在这段代码中，我们使用了 ES 规范中的\u003Ccode>Promise.prototype.finally()\u003C\u002Fcode>。它提供了和\u003Ccode>try\u003C\u002Fcode>语句的\u003Ccode>finally\u003C\u002Fcode>分支类似的功能。\u003C\u002Fp>\n","",{"total":16,"totalRoots":16,"comments":23,"pv":16},[]]