A place to cache linked articles (think custom and personal wayback machine)
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

4 роки тому
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang=fr>
  4. <!-- Has to be within the first 1024 bytes, hence before the <title>
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset=utf-8>
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name=viewport content="width=device-width,minimum-scale=1,initial-scale=1,shrink-to-fit=no">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>Why I’m Not Waiting On ‘await’ (part 1) (archive) — David Larlet</title>
  13. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  14. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons/apple-touch-icon.png">
  15. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons/favicon-32x32.png">
  16. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons/favicon-16x16.png">
  17. <link rel="manifest" href="/manifest.json">
  18. <link rel="mask-icon" href="/static/david/icons/safari-pinned-tab.svg" color="#5bbad5">
  19. <link rel="shortcut icon" href="/static/david/icons/favicon.ico">
  20. <meta name="apple-mobile-web-app-title" content="David Larlet">
  21. <meta name="application-name" content="David Larlet">
  22. <meta name="msapplication-TileColor" content="#da532c">
  23. <meta name="msapplication-config" content="/static/david/icons/browserconfig.xml">
  24. <meta name="theme-color" content="#f0f0ea">
  25. <!-- That good ol' feed, subscribe :p. -->
  26. <link rel=alternate type="application/atom+xml" title=Feed href="/david/log/">
  27. <meta name="robots" content="noindex, nofollow">
  28. <meta content="origin-when-cross-origin" name="referrer">
  29. <!-- Canonical URL for SEO purposes -->
  30. <link rel="canonical" href="https://blog.getify.com/not-awaiting-1/">
  31. <style>
  32. /* http://meyerweb.com/eric/tools/css/reset/ */
  33. html, body, div, span,
  34. h1, h2, h3, h4, h5, h6, p, blockquote, pre,
  35. a, abbr, address, big, cite, code,
  36. del, dfn, em, img, ins,
  37. small, strike, strong, tt, var,
  38. dl, dt, dd, ol, ul, li,
  39. fieldset, form, label, legend,
  40. table, caption, tbody, tfoot, thead, tr, th, td,
  41. article, aside, canvas, details, embed,
  42. figure, figcaption, footer, header, hgroup,
  43. menu, nav, output, ruby, section, summary,
  44. time, mark, audio, video {
  45. margin: 0;
  46. padding: 0;
  47. border: 0;
  48. font-size: 100%;
  49. font: inherit;
  50. vertical-align: baseline;
  51. }
  52. /* HTML5 display-role reset for older browsers */
  53. article, aside, details, figcaption, figure,
  54. footer, header, hgroup, menu, nav, section { display: block; }
  55. body { line-height: 1; }
  56. blockquote, q { quotes: none; }
  57. blockquote:before, blockquote:after,
  58. q:before, q:after {
  59. content: '';
  60. content: none;
  61. }
  62. table {
  63. border-collapse: collapse;
  64. border-spacing: 0;
  65. }
  66. /* http://practicaltypography.com/equity.html */
  67. /* https://calendar.perfplanet.com/2016/no-font-face-bulletproof-syntax/ */
  68. /* https://www.filamentgroup.com/lab/js-web-fonts.html */
  69. @font-face {
  70. font-family: 'EquityTextB';
  71. src: url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff2') format('woff2'),
  72. url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff') format('woff');
  73. font-weight: 300;
  74. font-style: normal;
  75. font-display: swap;
  76. }
  77. @font-face {
  78. font-family: 'EquityTextB';
  79. src: url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff2') format('woff2'),
  80. url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff') format('woff');
  81. font-weight: 300;
  82. font-style: italic;
  83. font-display: swap;
  84. }
  85. @font-face {
  86. font-family: 'EquityTextB';
  87. src: url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff2') format('woff2'),
  88. url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff') format('woff');
  89. font-weight: 700;
  90. font-style: normal;
  91. font-display: swap;
  92. }
  93. @font-face {
  94. font-family: 'ConcourseT3';
  95. src: url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff2') format('woff2'),
  96. url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff') format('woff');
  97. font-weight: 300;
  98. font-style: normal;
  99. font-display: swap;
  100. }
  101. /* http://practice.typekit.com/lesson/caring-about-opentype-features/ */
  102. body {
  103. /* http://www.cssfontstack.com/ Palatino 99% Win 86% Mac */
  104. font-family: "EquityTextB", Palatino, serif;
  105. background-color: #f0f0ea;
  106. color: #07486c;
  107. font-kerning: normal;
  108. -moz-osx-font-smoothing: grayscale;
  109. -webkit-font-smoothing: subpixel-antialiased;
  110. text-rendering: optimizeLegibility;
  111. font-variant-ligatures: common-ligatures contextual;
  112. font-feature-settings: "kern", "liga", "clig", "calt";
  113. }
  114. pre, code, kbd, samp, var, tt {
  115. font-family: 'TriplicateT4c', monospace;
  116. }
  117. em {
  118. font-style: italic;
  119. color: #323a45;
  120. }
  121. strong {
  122. font-weight: bold;
  123. color: black;
  124. }
  125. nav {
  126. background-color: #323a45;
  127. color: #f0f0ea;
  128. display: flex;
  129. justify-content: space-around;
  130. padding: 1rem .5rem;
  131. }
  132. nav:last-child {
  133. border-bottom: 1vh solid #2d7474;
  134. }
  135. nav a {
  136. color: #f0f0ea;
  137. }
  138. nav abbr {
  139. border-bottom: 1px dotted white;
  140. }
  141. h1 {
  142. border-top: 1vh solid #2d7474;
  143. border-bottom: .2vh dotted #2d7474;
  144. background-color: #e3e1e1;
  145. color: #323a45;
  146. text-align: center;
  147. padding: 5rem 0 4rem 0;
  148. width: 100%;
  149. font-family: 'ConcourseT3';
  150. display: flex;
  151. flex-direction: column;
  152. }
  153. h1.single {
  154. padding-bottom: 10rem;
  155. }
  156. h1 span {
  157. position: absolute;
  158. top: 1vh;
  159. left: 20%;
  160. line-height: 0;
  161. }
  162. h1 span a {
  163. line-height: 1.7;
  164. padding: 1rem 1.2rem .6rem 1.2rem;
  165. border-radius: 0 0 6% 6%;
  166. background: #2d7474;
  167. font-size: 1.3rem;
  168. color: white;
  169. text-decoration: none;
  170. }
  171. h2 {
  172. margin: 4rem 0 1rem;
  173. border-top: .2vh solid #2d7474;
  174. padding-top: 1vh;
  175. }
  176. h3 {
  177. text-align: center;
  178. margin: 3rem 0 .75em;
  179. }
  180. hr {
  181. height: .4rem;
  182. width: .4rem;
  183. border-radius: .4rem;
  184. background: #07486c;
  185. margin: 2.5rem auto;
  186. }
  187. time {
  188. display: bloc;
  189. margin-left: 0 !important;
  190. }
  191. ul, ol {
  192. margin: 2rem;
  193. }
  194. ul {
  195. list-style-type: square;
  196. }
  197. a {
  198. text-decoration-skip-ink: auto;
  199. text-decoration-thickness: 0.05em;
  200. text-underline-offset: 0.09em;
  201. }
  202. article {
  203. max-width: 50rem;
  204. display: flex;
  205. flex-direction: column;
  206. margin: 2rem auto;
  207. }
  208. article.single {
  209. border-top: .2vh dotted #2d7474;
  210. margin: -6rem auto 1rem auto;
  211. background: #f0f0ea;
  212. padding: 2rem;
  213. }
  214. article p:last-child {
  215. margin-bottom: 1rem;
  216. }
  217. p {
  218. padding: 0 .5rem;
  219. margin-left: 3rem;
  220. }
  221. p + p,
  222. figure + p {
  223. margin-top: 2rem;
  224. }
  225. blockquote {
  226. background-color: #e3e1e1;
  227. border-left: .5vw solid #2d7474;
  228. display: flex;
  229. flex-direction: column;
  230. align-items: center;
  231. padding: 1rem;
  232. margin: 1.5rem;
  233. }
  234. blockquote cite {
  235. font-style: italic;
  236. }
  237. blockquote p {
  238. margin-left: 0;
  239. }
  240. figure {
  241. border-top: .2vh solid #2d7474;
  242. background-color: #e3e1e1;
  243. text-align: center;
  244. padding: 1.5rem 0;
  245. margin: 1rem 0 0;
  246. font-size: 1.5rem;
  247. width: 100%;
  248. }
  249. figure img {
  250. max-width: 250px;
  251. max-height: 250px;
  252. border: .5vw solid #323a45;
  253. padding: 1px;
  254. }
  255. figcaption {
  256. padding: 1rem;
  257. line-height: 1.4;
  258. }
  259. aside {
  260. display: flex;
  261. flex-direction: column;
  262. background-color: #e3e1e1;
  263. padding: 1rem 0;
  264. border-bottom: .2vh solid #07486c;
  265. }
  266. aside p {
  267. max-width: 50rem;
  268. margin: 0 auto;
  269. }
  270. /* https://fvsch.com/code/css-locks/ */
  271. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  272. font-size: 1rem;
  273. line-height: calc( 1.5em + 0.2 * 1rem );
  274. }
  275. h1 {
  276. font-size: 1.9rem;
  277. line-height: calc( 1.2em + 0.2 * 1rem );
  278. }
  279. h2 {
  280. font-size: 1.6rem;
  281. line-height: calc( 1.3em + 0.2 * 1rem );
  282. }
  283. h3 {
  284. font-size: 1.35rem;
  285. line-height: calc( 1.4em + 0.2 * 1rem );
  286. }
  287. @media (min-width: 20em) {
  288. /* The (100vw - 20rem) / (50 - 20) part
  289. resolves to 0-1rem, depending on the
  290. viewport width (between 20em and 50em). */
  291. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  292. font-size: calc( 1rem + .6 * (100vw - 20rem) / (50 - 20) );
  293. line-height: calc( 1.5em + 0.2 * (100vw - 50rem) / (20 - 50) );
  294. margin-left: 0;
  295. }
  296. h1 {
  297. font-size: calc( 1.9rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  298. line-height: calc( 1.2em + 0.2 * (100vw - 50rem) / (20 - 50) );
  299. }
  300. h2 {
  301. font-size: calc( 1.5rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  302. line-height: calc( 1.3em + 0.2 * (100vw - 50rem) / (20 - 50) );
  303. }
  304. h3 {
  305. font-size: calc( 1.35rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  306. line-height: calc( 1.4em + 0.2 * (100vw - 50rem) / (20 - 50) );
  307. }
  308. }
  309. @media (min-width: 50em) {
  310. /* The right part of the addition *must* be a
  311. rem value. In this example we *could* change
  312. the whole declaration to font-size:2.5rem,
  313. but if our baseline value was not expressed
  314. in rem we would have to use calc. */
  315. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  316. font-size: calc( 1rem + .6 * 1rem );
  317. line-height: 1.5em;
  318. }
  319. p, li, pre, details {
  320. margin-left: 3rem;
  321. }
  322. h1 {
  323. font-size: calc( 1.9rem + 1.5 * 1rem );
  324. line-height: 1.2em;
  325. }
  326. h2 {
  327. font-size: calc( 1.5rem + 1.5 * 1rem );
  328. line-height: 1.3em;
  329. }
  330. h3 {
  331. font-size: calc( 1.35rem + 1.5 * 1rem );
  332. line-height: 1.4em;
  333. }
  334. figure img {
  335. max-width: 500px;
  336. max-height: 500px;
  337. }
  338. }
  339. figure.unsquared {
  340. margin-bottom: 1.5rem;
  341. }
  342. figure.unsquared img {
  343. height: inherit;
  344. }
  345. @media print {
  346. body { font-size: 100%; }
  347. a:after { content: " (" attr(href) ")"; }
  348. a, a:link, a:visited, a:after {
  349. text-decoration: underline;
  350. text-shadow: none !important;
  351. background-image: none !important;
  352. background: white;
  353. color: black;
  354. }
  355. abbr[title] { border-bottom: 0; }
  356. abbr[title]:after { content: " (" attr(title) ")"; }
  357. img { page-break-inside: avoid; }
  358. @page { margin: 2cm .5cm; }
  359. h1, h2, h3 { page-break-after: avoid; }
  360. p3 { orphans: 3; widows: 3; }
  361. img {
  362. max-width: 250px !important;
  363. max-height: 250px !important;
  364. }
  365. nav, aside { display: none; }
  366. }
  367. ul.with_columns {
  368. column-count: 1;
  369. }
  370. @media (min-width: 20em) {
  371. ul.with_columns {
  372. column-count: 2;
  373. }
  374. }
  375. @media (min-width: 50em) {
  376. ul.with_columns {
  377. column-count: 3;
  378. }
  379. }
  380. ul.with_two_columns {
  381. column-count: 1;
  382. }
  383. @media (min-width: 20em) {
  384. ul.with_two_columns {
  385. column-count: 1;
  386. }
  387. }
  388. @media (min-width: 50em) {
  389. ul.with_two_columns {
  390. column-count: 2;
  391. }
  392. }
  393. .gallery {
  394. display: flex;
  395. flex-wrap: wrap;
  396. justify-content: space-around;
  397. }
  398. .gallery figure img {
  399. margin-left: 1rem;
  400. margin-right: 1rem;
  401. }
  402. .gallery figure figcaption {
  403. font-family: 'ConcourseT3'
  404. }
  405. footer {
  406. font-family: 'ConcourseT3';
  407. display: flex;
  408. flex-direction: column;
  409. border-top: 3px solid white;
  410. padding: 4rem 0;
  411. background-color: #07486c;
  412. color: white;
  413. }
  414. footer > * {
  415. max-width: 50rem;
  416. margin: 0 auto;
  417. }
  418. footer a {
  419. color: #f1c40f;
  420. }
  421. footer .avatar {
  422. width: 200px;
  423. height: 200px;
  424. border-radius: 50%;
  425. float: left;
  426. -webkit-shape-outside: circle();
  427. shape-outside: circle();
  428. margin-right: 2rem;
  429. padding: 2px 5px 5px 2px;
  430. background: white;
  431. border-left: 1px solid #f1c40f;
  432. border-top: 1px solid #f1c40f;
  433. border-right: 5px solid #f1c40f;
  434. border-bottom: 5px solid #f1c40f;
  435. }
  436. </style>
  437. <h1>
  438. <span><a id="jumper" href="#jumpto" title="Un peu perdu ?">?</a></span>
  439. Why I’m Not Waiting On ‘await’ (part 1) (archive)
  440. <time>Pour la pérennité des contenus liés. Non-indexé, retrait sur simple email.</time>
  441. </h1>
  442. <section>
  443. <article>
  444. <h3><a href="https://blog.getify.com/not-awaiting-1/">Source originale du contenu</a></h3>
  445. <div>
  446. <p>Two-part series on the highs and lows of the coming <code>async..await</code> in JS:</p>
  447. <ul>
  448. <li><a href="https://blog.getify.com/not-awaiting-1">Part 1</a>: what <code>async..await</code> is and why it’s awesome</li>
  449. <li><a href="https://blog.getify.com/not-awaiting-2">Part 2</a>: why <code>async..await</code> falls short compared to generators+promises</li>
  450. </ul>
  451. </div>
  452. <p><hr/>
  453. <hr/>
  454. <p>The hype is strong for the <code>async..await</code> syntax that’s likely getting official spec approval for ES2017, but has already <a href="https://chromium.googlesource.com/v8/v8.git/+/d08c0304c5779223d6c468373af4815ec3ccdb84">landed in v8</a> and <a href="https://blogs.windows.com/msedgedev/2015/09/30/asynchronous-code-gets-easier-with-es2016-async-function-support-in-chakra-and-microsoft-edge/">Microsoft Edge</a>, at least. And in some ways, <code>async await</code> is very promising and the hype is well deserved.</p>
  455. <p>In fact, it seems even before we got Promises in ES6, most developers were already desperately pining for <code>async function</code> so they could escape some the vagaries of the Promise API and promise chain hell. There’s no doubt that a lot of code will be improved as we move callbacks to promises, and then from promises to promise-driven <code>async function</code>s.</p>
  456. <p>But is <code>async..await</code> our long-awaited white knight? Is it really the only <em>future</em> of async flow control programming in JavaScript?</p>
  457. <p>I like the pattern that <code>async..await</code> signals. Really, I do. It’s a pattern I’ve been advocating for several years now, since even before ES6 landed. But the substance of <code>async..await</code> leaves me hanging in a few places.</p>
  458. <h3 id="why-the-hype">Why The Hype?</h3>
  459. <p>Put simply, an <code>async function</code> promotes inside it a synchronous-looking style of flow control (value resolution, error handling).</p>
  460. <p>Each asynchronous step — usually the retrieval of a value or waiting for an action to finish — is modeled with a Promise. But instead of having to <code>.then(..)</code> the promise and provide a function to receive this value/completion, the <code>await</code> keyword is able to locally block while waiting to unwrap the promise.</p>
  461. <p>It’s better to illustrate with code. Here’s three versions of the same (fictional) code, one with callbacks, one with promise chains, and the final with <code>async..await</code>.</p>
  462. <p>First, callbacks:</p>
  463. <pre class="code">
  464. function getOrderDetails(orderID,cb) {
  465. db.find( "orders", orderID, function(err,order){
  466. if (!err) {
  467. db.find(
  468. "customers",
  469. order.customerID,
  470. function(err,customer){
  471. if (!err) {
  472. order.customer = customer;
  473. cb( null, order );
  474. }
  475. else cb(err);
  476. }
  477. );
  478. }
  479. else cb(err);
  480. });
  481. }</p>
  482. <p>getOrderDetails( 1234, function(err,order){
  483. if (!err) {
  484. displayOrder(order);
  485. }
  486. else showError(err);
  487. });
  488. </pre>
  489. <hr/>
  490. <hr/>
  491. <div id="inline-vs-declaration">
  492. <p><strong>Update:</strong> Some readers expressed disdain at the usage of nested function expressions here, since it’s putting the worst light on the visual aesthetic of callbacks. That’s not actually <em>my</em> argument in this post — that they’re ugly — but I can see why it would be distracting to my overall points for those who prefer more readable callback style code.</p>
  493. <p><a href="https://gist.github.com/getify/28c586cff83516cbd2dca573eb4cb194">Here’s a comparison</a> between this version and using separate function declarations for each. I think the readability is definitely better. But, that’s just an aside; <a href="https://github.com/getify/You-Dont-Know-JS/blob/master/async%20&amp;%20performance/ch2.md#trust-issues">the virtues of promises are about far more than just nesting/indentation/readability</a>. That discussion is for <a href="https://blog.getify.com/promises-part-2/">another post</a>.</p>
  494. </div>
  495. <hr/>
  496. <hr/>
  497. <p>No sense in belaboring explanation of this code too much. That’s not really the point of the post. But do notice that error handling is intertwined with and cluttering up the flow control of data, and that we have to nest functions to express their temporal dependence (one has to start after the other finishes, etc).</p>
  498. <p>OK, now the promises version:</p>
  499. <pre class="code">
  500. function getOrderDetails(orderID) {
  501. return db.find( "orders", orderID )
  502. .then(function(order){
  503. return db.find( "customers", order.customerID )
  504. .then(function(customer){
  505. order.customer = customer;
  506. return order;
  507. });
  508. });
  509. }</p>
  510. <p>getOrderDetails( 1234 )
  511. .then( displayOrder, showError );
  512. </pre>
  513. <p>Phew! That’s a lot better.</p>
  514. <p>Crucially, we see here that the error handling part of the flow control becomes implicit (and thus hidden), which declutters the code. If any error happens at any depth of this promise chain, it’ll propagate out and get handled by <code>showError(..)</code> — we don’t need to manually push it out.</p>
  515. <p>We still have to use functions to unwrap our promises, though. And indeed, we also still have some unfortunate nesting going on. The reason the <code>then(function(customer)..</code> has to be nested instead of chained off the top-level promise chain in <code>getOrderDetails(..)</code> is because we need lexical scope access to <code>order</code> so we can combine <code>order</code> and <code>customer</code> into a single value to pass along.</p>
  516. <p>OK, so what’s <code>async..await</code> going to do for us?</p>
  517. <pre class="code">
  518. async function getOrderDetails(orderID) {
  519. var order = await db.find( "orders", orderID );</p>
  520. <p>var customer = await db.find( "customers", order.customerID );</p>
  521. <p>order.customer = customer;
  522. return order;
  523. }</p>
  524. <p>getOrderDetails( 1234 )
  525. .then( displayOrder, showError );
  526. </pre>
  527. <p>Wow. Just wow.</p>
  528. <p>The <code>await</code> keyword locally blocks to <em>wait</em> on the promise that <code>db.find(..)</code> gives us, meaning once it resolves, we’ll get the order we can then synchronously assign to <code>order</code>; same for <code>customer</code>.</p>
  529. <p>If either of those results in a rejected promise (generally, an error), a synchronous exception is thrown at that point in the flow of <code>getOrderDetails(..)</code>, and the rest of that function body won’t run.</p>
  530. <p>And, since <code>getOrderDetails(..)</code> is an <code>async function</code>, it makes its promise. Any uncaught exception inside it becomes a rejection of <em>that</em> promise, so we route either to <code>displayOrder(..)</code> or <code>showError(..)</code> implicitly.</p>
  531. <p>If the <code>getOrderDetails( 1234 )</code> call was itself inside an <code>async function</code>, it would use the same <code>await</code> unwrapping trick along with the ol’ synchronous, familiar <code>try..catch</code>:</p>
  532. <pre class="code">
  533. async function main() {
  534. try {
  535. showOrder( await getOrderDetails( 1234 ) );
  536. }
  537. catch (err) {
  538. showError( err );
  539. }
  540. }
  541. </pre>
  542. <p>The <code>await</code> unwrapping magic is nice to facilitate asynchronous function composition.</p>
  543. <p>Is the implicit error routing of the former snippet better than the explicit form here in the latter? I prefer the explicit handling at the end of the path. I like that promises implicitly pass along rejections, but at the end of the line when it’s time to handle the error, I think that explicit <code>try..catch</code> is much clearer.</p>
  544. <h3 id="have-we-seen-this-before">Have We Seen This Before?</h3>
  545. <p>Are you sold on <code>async..await</code> yet? I think they’re pretty cool.</p>
  546. <p>But as I’m a true blooded hipster, I liked this pattern before the rest of you thought it was cool.</p>
  547. <p><em>Way back in ES6</em>, we could actually do this same pattern, just with slightly different vocabulary: <strong>Generators</strong> and <strong><code>yield</code></strong> (with promises):</p>
  548. <pre class="code">
  549. function *getOrderDetails(orderID) {
  550. var order = yield db.find( "orders", orderID );</p>
  551. <p>var customer = yield db.find( "customers", order.customerID );</p>
  552. <p>order.customer = customer;
  553. return order;
  554. }</p>
  555. <p>run( getOrderDetails( 1234 ) )
  556. .then( displayOrder, showError );
  557. </pre>
  558. <p>What’s different from <code>async..await</code>? Well, we see that ugly <code>*</code> there instead of the prettier <code>async</code> word. We also see <code>yield</code> instead of <code>await</code>. And lastly, we see usage of <code>run(..)</code> — where the heck does that come from?</p>
  559. <p>I’ve <a href="https://github.com/getify/You-Dont-Know-JS/blob/master/es6%20&amp;%20beyond/ch4.md#generators--promises">written</a> extensively <a href="https://davidwalsh.name/async-generators">before</a> about using generators and promises to facilitate this synchronous-async pattern before, and about why that’s so important. Read those if you’re not sure what is going on in that above snippet.</p>
  560. <p>The short version is that generators don’t come out-of-the-box with knowing how to receive a <code>yield</code>ed promise and how to resume when it finishes. So we conjure a <code>run(..)</code> function that is smart enough. I won’t show the implementation of it here, but it’s listed in the first of those two links, and it’s only about 25 LoC even with comments — not too difficult, but not a detail to get tripped up on here.</p>
  561. <p>The point is, <code>run(..)</code> is a known-quantity, and can be (and is!) easily provided by various async-helper libraries so you don’t have to worry about it, but just use it.</p>
  562. <p>Of course, I think you <em>should</em> eventually learn how it works, and that’s what those other readings will teach you. But for now, let’s just use it as-is.</p>
  563. <h3 id="a-rose-by-any-other-name">A Rose By Any Other Name…</h3>
  564. <p>Is an <code>async function</code> just nicer syntactic sugar for a generator with a <code>run(..)</code> utility driving it?</p>
  565. <p><strong>No.</strong> But I can see why most people find that natural/comforting to assume.</p>
  566. <p>Let’s start with <code>yield</code> and <code>await</code>. I think it’s fair to claim that <code>yield</code> is imperative while <code>await</code> is declarative, but that may take a bit more explanation to convince you.</p>
  567. <p><code>yield</code> is inexorably connected to the process of locally pausing to <em>yield</em> control to some external facility, and, if appropriate, eventually resume the generator. The sending of a value, and the receiving of a value back, are entirely optional; the return of control is all that <em>really</em> matters. We’re imperatively saying “yield control to somewhere else and wait until its given back”. <code>yield</code> is primarily a control-transfer mechanism.</p>
  568. <p>With <code>await</code>, we’ve covered up all the mechanics and semantics of any external control involved; the engine itself does that <code>run(..)</code> work for us. As far as we’re concerned, semantically, control never leaves our local function, we just sort of sit and a<em>wait</em> for processing to finish so we can move on to the next line.</p>
  569. <p>Also, the usage of <code>await</code> seems to be a bit more focused on value semantics than <code>yield</code>. “What is this line waiting on?”” It’s waiting on a value. Would you use <code>await</code> just for pausing and not capturing any return value? I don’t think that’d be as natural, because the whole notion of transfer of control is hidden, conceptually and syntactically.</p>
  570. <p>Taking promises out of the equation for a moment, you could use <code>yield;</code> in a generator to just simply transfer control elsewhere for awhile, and neither have a value you’re sending out nor have a value you’re expecting back.</p>
  571. <p>But you can’t <code>await;</code>.</p>
  572. <p>The <code>await</code> keyword has to always explicitly be awaiting on a promise. Even if you did <code>await undefined</code> or <code>await 3</code>, those immediate values are implicitly wrapped in an already-resolved promise, which is then <code>await</code>ed.</p>
  573. <p>In other words, <code>await</code> is declarative in that it’s saying “await on this value until it’s ready”. The <em>how</em> of that is entirely hidden so we aren’t even thinking about it. Even if under the covers, the browser did exactly the same steps with <code>await</code> that would be done with a <code>yield</code>, the mental models and the syntax surface area are declarative for <code>await</code> and imperative for <code>yield</code>.</p>
  574. <p>We all like declarative forms, though. So that seems like a clear win, right?</p>
  575. <p>The downside is when a declarative form removes important functional characteristics.</p>
  576. <h3 id="just-use-both">Just Use Both?</h3>
  577. <p>In <a href="https://blog.getify.com/not-awaiting-2">part two</a> of this blog post, I’m going to explain my specific reservations of <code>async function</code>s.</p>
  578. <p>But to conclude this post, let me address the most obvious question floating in the air right now: if <code>async function</code> is nice for syntactic sugar, and generators+promises can do more powerful things when needed, why not just use both?</p>
  579. <p>We wouldn’t need a <a href="https://blog.getify.com/not-awaiting-2">part two</a> blog post if I felt the world was that easy!</p>
  580. <p>The concern I have with mixing <code>async function</code> and generator is that the composition of the two is more awkward. It’s easy from a generator to <code>yield foo()</code> if <code>foo()</code> is an <code>async function</code>, but it’s weirder from an <code>async function</code> to <code>await run( foo() )</code> if <code>foo()</code> is a generator.</p>
  581. <p>By “weirder”, I mean that it pushes that extra <code>run(..)</code> tax out to every call-site of <code>foo()</code>, but if most of your code is based around <code>async function</code>, you’re pretty likely to forget that you need to put <code>run(..)</code> in there.</p>
  582. <p>Unfortunately, you won’t necessarily detect this error like you’d expect. Since <code>await</code> automatically promise-wraps any non-promise values, it’ll just promise-wrap the iterator that comes back from your generator invocation, never having run any of the code in the generator at all! And since the value is a non-promise, the wrapped promise will immediately resolve, meaning <code>await</code> unwraps it right away.</p>
  583. <p>The take-away is that mixing the two in the same code base is going to be error-prone. I’m not just predicting… I’ve tried it, and made these mistakes. Many times.</p>
  584. <p>Check out <a href="https://blog.getify.com/not-awaiting-2">part two</a> where I explain the reasons I plan to stick with generators+promises instead of jumping on the <code>async function</code> bandwagon.</p></p>
  585. </article>
  586. </section>
  587. <nav id="jumpto">
  588. <p>
  589. <a href="/david/blog/">Accueil du blog</a> |
  590. <a href="https://blog.getify.com/not-awaiting-1/">Source originale</a> |
  591. <a href="/david/stream/2019/">Accueil du flux</a>
  592. </p>
  593. </nav>
  594. <footer>
  595. <div>
  596. <img src="/static/david/david-larlet-avatar.jpg" loading="lazy" class="avatar" width="200" height="200">
  597. <p>
  598. Bonjour/Hi!
  599. Je suis <a href="/david/" title="Profil public">David&nbsp;Larlet</a>, je vis actuellement à Montréal et j’alimente cet espace depuis 15 ans. <br>
  600. Si tu as apprécié cette lecture, n’hésite pas à poursuivre ton exploration. Par exemple via les <a href="/david/blog/" title="Expériences bienveillantes">réflexions bimestrielles</a>, la <a href="/david/stream/2019/" title="Pensées (dés)articulées">veille hebdomadaire</a> ou en t’abonnant au <a href="/david/log/" title="S’abonner aux publications via RSS">flux RSS</a> (<a href="/david/blog/2019/flux-rss/" title="Tiens c’est quoi un flux RSS ?">so 2005</a>).
  601. </p>
  602. <p>
  603. Je m’intéresse à la place que je peux avoir dans ce monde. En tant qu’humain, en tant que membre d’une famille et en tant qu’associé d’une coopérative. De temps en temps, je fais aussi des <a href="https://github.com/davidbgk" title="Principalement sur Github mais aussi ailleurs">trucs techniques</a>. Et encore plus rarement, <a href="/david/talks/" title="En ce moment je laisse plutôt la place aux autres">j’en parle</a>.
  604. </p>
  605. <p>
  606. Voici quelques articles choisis :
  607. <a href="/david/blog/2019/faire-equipe/" title="Accéder à l’article complet">Faire équipe</a>,
  608. <a href="/david/blog/2018/bivouac-automnal/" title="Accéder à l’article complet">Bivouac automnal</a>,
  609. <a href="/david/blog/2018/commodite-effondrement/" title="Accéder à l’article complet">Commodité et effondrement</a>,
  610. <a href="/david/blog/2017/donnees-communs/" title="Accéder à l’article complet">Des données aux communs</a>,
  611. <a href="/david/blog/2016/accompagner-enfant/" title="Accéder à l’article complet">Accompagner un enfant</a>,
  612. <a href="/david/blog/2016/senior-developer/" title="Accéder à l’article complet">Senior developer</a>,
  613. <a href="/david/blog/2016/illusion-sociale/" title="Accéder à l’article complet">L’illusion sociale</a>,
  614. <a href="/david/blog/2016/instantane-scopyleft/" title="Accéder à l’article complet">Instantané Scopyleft</a>,
  615. <a href="/david/blog/2016/enseigner-web/" title="Accéder à l’article complet">Enseigner le Web</a>,
  616. <a href="/david/blog/2016/simplicite-defaut/" title="Accéder à l’article complet">Simplicité par défaut</a>,
  617. <a href="/david/blog/2016/minimalisme-esthetique/" title="Accéder à l’article complet">Minimalisme et esthétique</a>,
  618. <a href="/david/blog/2014/un-web-omni-present/" title="Accéder à l’article complet">Un web omni-présent</a>,
  619. <a href="/david/blog/2014/manifeste-developpeur/" title="Accéder à l’article complet">Manifeste de développeur</a>,
  620. <a href="/david/blog/2013/confort-convivialite/" title="Accéder à l’article complet">Confort et convivialité</a>,
  621. <a href="/david/blog/2013/testament-numerique/" title="Accéder à l’article complet">Testament numérique</a>,
  622. et <a href="/david/blog/" title="Accéder aux archives">bien d’autres…</a>
  623. </p>
  624. <p>
  625. On peut <a href="mailto:david%40larlet.fr" title="Envoyer un courriel">échanger par courriel</a>. Si éventuellement tu souhaites que l’on travaille ensemble, tu devrais commencer par consulter le <a href="http://larlet.com">profil dédié à mon activité professionnelle</a> et/ou contacter directement <a href="http://scopyleft.fr/">scopyleft</a>, la <abbr title="Société coopérative et participative">SCOP</abbr> dont je fais partie depuis six ans. Je recommande au préalable de lire <a href="/david/blog/2018/cout-site/" title="Attention ce qui va suivre peut vous choquer">combien coûte un site</a> et pourquoi je suis plutôt favorable à une <a href="/david/pro/devis/" title="Discutons-en !">non-demande de devis</a>.
  626. </p>
  627. <p>
  628. Je ne traque pas ta navigation mais mon
  629. <abbr title="Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33.184162340">hébergeur</abbr>
  630. conserve des logs d’accès.
  631. </p>
  632. </div>
  633. </footer>
  634. <script type="text/javascript">
  635. ;(_ => {
  636. const jumper = document.getElementById('jumper')
  637. jumper.addEventListener('click', e => {
  638. e.preventDefault()
  639. const anchor = e.target.getAttribute('href')
  640. const targetEl = document.getElementById(anchor.substring(1))
  641. targetEl.scrollIntoView({behavior: 'smooth'})
  642. })
  643. })()
  644. </script>