A place to cache linked articles (think custom and personal wayback machine)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4 年之前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  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 2) (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-2">
  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 2) (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-2">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>In <a href="https://blog.getify.com/not-awaiting-1/">part one</a>, I covered why <code>async..await</code> has built up such momentum on the hype train. The promise of sync-looking async is powerfully attractive.</p>
  455. <p>Now I’ll cover why I think we should slow that train down. A bunch.</p>
  456. <p>I’m going to make a case for sticking with generators+promises by exploring some shortcomings of <code>async..await</code>. As I’ve already shown, the syntax of generators+promises isn’t that much worse. But they’re more powerful.</p>
  457. <h3 id="missing-direct-delegation">Missing Direct Delegation</h3>
  458. <p><a href="https://blog.getify.com/not-awaiting-1/#a-rose-by-any-other-name">Remember in part one</a> where I asserted that <code>yield</code> is more about control transfer than value semantics? That’s more obvious once we look at <code>yield *</code>.</p>
  459. <p>In a generator, a <code>yield</code> transfers control back to the calling code. But what if we want to delegate to another piece of code, while still preserving the pause/resume async semantics.</p>
  460. <p>We can instead do a <code>yield *</code> delegation to another generator, like this:</p>
  461. <pre class="code">
  462. function *foo() {
  463. var x = yield getX();
  464. var y = yield * bar( x );
  465. console.log( x, y );
  466. }</p>
  467. <p>function *bar(x) {
  468. var y = yield getY( x );
  469. var z = yield getZ( y );
  470. return y + z;
  471. }</p>
  472. <p>run( foo() );
  473. </pre>
  474. <p>You'll notice that the <code>yield * bar(..)</code> lets the <code>bar()</code> generator run. But <strong>how</strong> does it run? We didn't use the <code>run(..)</code> utility on that call.</p>
  475. <p>We don't have to, because <code>yield *</code> actually <em>delegates control</em>, so the original <code>run(..)</code> from the last line of the snippet is now transparently controlling (pausing/resuming) the <code>bar()</code> generator as it proceeds, without even knowing it. Once <code>bar()</code> completes, its return value (if any) finally resumes the <code>yield *</code> with that value, returning execution to the <code>foo()</code> generator.</p>
  476. <p>The <code>yield *</code> can even be used to create a sort of generator recursion, where a generator instance delegates to another instance of itself.</p>
  477. <p>Because <code>async function</code>s are not about control transfer, there's no equivalent to <code>yield *</code> like <code>await *</code>. There <em>was</em> talk about appropriating the <code>await *</code> syntax to mean something like <code>await Promise.all(..)</code>, but that was eventually rejected.</p>
  478. <p>The <code>async function</code> equivalent would be:</p>
  479. <pre class="code">
  480. async function foo() {
  481. var x = await getX();
  482. var y = await bar( x );
  483. console.log( x, y );
  484. }</p>
  485. <p>async function bar(x) {
  486. var y = await getY( x );
  487. var z = await getZ( y );
  488. return y + z;
  489. }</p>
  490. <p>foo();
  491. </pre>
  492. <p>We don't need a <code>yield *</code>-type construct here because <code>bar()</code> creates a promise we can <code>await</code>.</p>
  493. <p>That may seem that it's basically just, again, nicer syntax sugar removing the need for the <code>*</code>. But it's subtly different. With the <code>yield *</code>, we're not creating a wrapper promise but rather just passing control directly through.</p>
  494. <p>What if <code>bar()</code> has some case where it can provide its result right away, such as pulling from a cache/memoization?</p>
  495. <pre class="code">
  496. function *bar(x) {
  497. if (x in cache) {
  498. return cache[x];
  499. }</p>
  500. <p>var y = yield getY( x );
  501. var z = yield getZ( y );
  502. return y + z;
  503. }
  504. </pre>
  505. <p>versus:</p>
  506. <pre class="code">
  507. async function bar(x) {
  508. if (x in cache) {
  509. return cache[x];
  510. }</p>
  511. <p>var y = await getY( x );
  512. var z = await getZ( y );
  513. return y + z;
  514. }
  515. </pre>
  516. <p>In both cases, we immediately <code>return cache[x]</code>.</p>
  517. <p>In the <code>yield *</code> case, <code>return</code> immediately terminates the generator instance, so we'd get that result back in <code>foo()</code> right away. But with promises (which is what <code>async function</code>s return), unwrapping is an async step, so we have to wait a tick before we can get that value.</p>
  518. <p>Depending on your situation, <strong>that may slow your program down a little bit</strong>, especially if you're using that pattern a lot.</p>
  519. <h3 id="cant-be-stopped">Can't Be Stopped</h3>
  520. <p>In part one, <a href="https://blog.getify.com/not-awaiting-1/#a-rose-by-any-other-name">I asserted</a> that <code>await</code> is declarative while <code>yield</code> is imperative. Pushing that to a higher level, <code>async function</code>s are declarative and generators are imperative.</p>
  521. <p>An <code>async function</code> is represented by (ie, returns) a promise. This promise represents one of two states: the whole thing finished, or something went wrong and it stopped. In that sense, the <code>async function</code> is an all-or-nothing proposition. But moreover, because it's a promise that's returned, all you can do is observe the outcome, not control it.</p>
  522. <p>By contrast, a generator is represented by (ie, returns) an iterator. The iterator is an external control for each of the steps in the generator. We don't just observe a generator's outcome, we participate in it and control it.</p>
  523. <p>The <code>run(..)</code> function that drives the generator+promise async flow control logic is actually negotiating via the iterator.</p>
  524. <p>The <code>next(..)</code> command on the iterator is how values are sent into the generator, and how values are received from it as it progresses. The iterator also has another method on it called <code>throw(..)</code>, which injects an exception into the generator at its paused position; <code>next(..)</code> and <code>throw(..)</code> pair together to signal (from the outside) success or failure of any given step in the generator.</p>
  525. <p>Of course, there's no requirement that the code controlling a generator's iterator choose to keep it going with <code>next(..)</code> or <code>throw(..)</code>. Merely opting to stop calling either has the effect of indefinitely pausing the generator. The iterator instance could be left in this paused state until the program ends or it's garbage collected.</p>
  526. <p>Even better, a generator's iterator has a third method on it that can be useful: <code>return(..)</code>. This method terminates the generator at its current paused point; you can send an affirmative signal into the generator from the outside telling it to stop right where it sits.</p>
  527. <p>You can even detect and respond to this <code>return(..)</code> signal inside the generator with the <code>finally</code> clause of a <code>try</code> statement; a <code>finally</code> will always be executed, even if the statement inside its associated <code>try</code> was terminated from the outside with an iterator's <code>return(..)</code> method.</p>
  528. <p>A <code>run(..)</code> that was slightly smarter could offer the advantage of allowing a generator to be stopped from the outside.</p>
  529. <p>Imagine a scenario where midway through a generator's steps, you can tell by the values emitted that it's appropriate to stop the progression of the generator -- to cancel any further actions. Maybe the generator makes a series of Ajax calls one after the other, but one of the results signals that none of the others should be made.</p>
  530. <p>Consider a more capable <code>run(..)</code> used like this:</p>
  531. <pre class="code">
  532. run( main(), function shouldTerminate(val){
  533. if (val == 42) return true;
  534. return false;
  535. } );
  536. </pre>
  537. <p>Each time a value is ready to be sent in to resume the <code>main()</code> generator, this <code>shouldTerminate(..)</code> function is first called with the value. If <em>it</em> returns <code>true</code>, then the iterator is <code>return(..)</code>d, otherwise the value is sent via <code>next(..)</code>.</p>
  538. <p>A generator provides the power of external cancelation capability whereas an <code>async function</code> can only cancel itself. Of course, the necessary cancelation logic can be baked into the <code>async function</code>, but that's not always appropriate.</p>
  539. <p>It's often much more appropriate to separate the async steps themselves from any logic that will consume or control (or cancel) them as a result. With generators that is possible, but with <code>async function</code>s it's not.</p>
  540. <h3 id="scheduling-is-hidden">Scheduling Is Hidden</h3>
  541. <p>It's tempting to assume that a program should always complete its async steps ASAP. It may be hard to conceive of a situation where you wouldn't want some part of the program to proceed <em>right away</em>.</p>
  542. <p>Let's consider a scenario where we have more than one set of asynchronous logic operating at the same time. For example, we might be making some HTTP requests and we might also be making some database calls. For the sake of this discussion, let's assume that each set of behavior is implemented in its own <code>async function</code>.</p>
  543. <p>Because <code>async function</code>s essentially come with their own <code>run(..)</code> utility built-in, the progression of steps is entirely opaque and advances strictly in ASAP fashion.</p>
  544. <p>But what if you needed to control the scheduling of the resumptions of each task's steps? What if, for the purposes of throttling resource usage, you needed to make sure that no more than one HTTP request/response was handled for each database request?</p>
  545. <p>Another problem could be that one set of async steps might have each step proceed so quickly (essentially immediately) that this task could "starve" the rest of the system by not letting any other tasks have a chance. To understand this possibility, you should read more about <a href="https://github.com/getify/You-Dont-Know-JS/blob/master/async%20&amp;%20performance/ch1.md#jobs">microtasks (aka Jobs)</a>. Basically, if each step in a promise chain resolves immediately, each subsequent step is scheduled "right away", such that no other waiting asynchronous operations get to run.</p>
  546. <p>Or what if you had ten sets of HTTP request/response cycles, but you need to make sure that no more than 3 of them were being processed at any given moment? Or perhaps you needed to not proceed ASAP but rather in a strictly round-robin order through the set of running tasks.</p>
  547. <p>There's probably half a dozen other scenarios where naive scheduling of the progression of async operations is not sufficient. In all those cases, we'd like to have more control.</p>
  548. <p>An <code>async function</code> affords no such control because the promise-resumption step is baked into the engine. But a smarter <code>run(..)</code> utility driving your generators could absolutely selectively decide which ones to resume in which order.</p>
  549. <p>By the way, while I'm being general about such scenarios for brevity sake, this is not purely theoretical. An open area of my research in async programming is <a href="..">CSP</a>, Communicating Sequential Processes, the model for concurrency used in the <em>go</em> language and also in ClojureScript's <code>core.async</code>.</p>
  550. <p>Normally CSP processes are modeled in JS using generators, because you will in general have many of them running at the same time, and usually need fine grained control over which ones resume after others pause. Attempts have been made to model CSP using <code>async function</code>s, but I believe these systems are inherently flawed in that the library is susceptible to all the above limitations because it has no control over the scheduling.</p>
  551. <p>CSP is one very concrete example of why it's important to have scheduling control over async operations. <code>async function</code>s just don't offer what we need, but generators do.</p>
  552. <h3 id="async-functions-vs-generators"><code>async function</code>s vs Generators</h3>
  553. <p>OK, so let's take stock of where we're at.</p>
  554. <p>First we examined how <code>async function</code>s work and what makes them so nice from a syntactic sugar perspective. But now we've seen several places where the tradeoff is that we lose control of potentially very important aspects of our asynchrony.</p>
  555. <p>You may consider these limitations unimportant because you haven't needed to do any of the fine-grained control described here. And that's fine. The <code>async function</code> will serve you just fine.</p>
  556. <p>But... what pain are you <em>really</em> enduring if you stuck with generators? Again, a side-by-side comparison:</p>
  557. <pre class="code">
  558. function *foo(x) {
  559. try {
  560. var y = yield getY( x );
  561. return x + y;
  562. }
  563. catch (err) {
  564. return x;
  565. }
  566. }</p>
  567. <p>run( foo( 3 ) )
  568. .then( function(result){
  569. console.log( result );
  570. } );
  571. </pre>
  572. <p>versus:</p>
  573. <pre class="code">
  574. async function foo(x) {
  575. try {
  576. var y = await getY( x );
  577. return x + y;
  578. }
  579. catch (err) {
  580. return x;
  581. }
  582. }</p>
  583. <p>foo( 3 )
  584. .then( function(result){
  585. console.log( result );
  586. } );
  587. </pre>
  588. <p>They're almost identical.</p>
  589. <p>In both versions you get localized synchronous semantics (including pause/resume and error handling), and you get a promise that represents the completion of the <code>foo(..)</code> task.</p>
  590. <p>For generators, you <em>do</em> need the simple <code>run(..)</code> library utility, whereas with <code>async function</code>s, you don't. But as we discussed, such a utility is only about a dozen lines of code in its simplest form. So is it actually that burdensome?</p>
  591. <p>For the extra control we get, I think <code>run(..)</code> is an easy price to justify.</p>
  592. <h3 id="finally"><code>finally { .. }</code></h3>
  593. <p>For the majority of my async code, I believe generators+promises is a better, more flexible, more powerful option, at only a minimal expense.</p>
  594. <p>I'd rather not start with <code>async function</code>s and then later realize that there are places where I need more control, and have to refactor back to generators. That's especially true considering the <a href="https://blog.getify.com/not-awaiting-1/#just-use-both">complications of mixing the two forms</a> that I addressed in part one.</p>
  595. <p><code>async..await</code> seems like a great feature. But it's too simplistic, IMO. The small syntactic benefits are not worth the loss of control.</p></p>
  596. </article>
  597. </section>
  598. <nav id="jumpto">
  599. <p>
  600. <a href="/david/blog/">Accueil du blog</a> |
  601. <a href="https://blog.getify.com/not-awaiting-2">Source originale</a> |
  602. <a href="/david/stream/2019/">Accueil du flux</a>
  603. </p>
  604. </nav>
  605. <footer>
  606. <div>
  607. <img src="/static/david/david-larlet-avatar.jpg" loading="lazy" class="avatar" width="200" height="200">
  608. <p>
  609. Bonjour/Hi!
  610. 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>
  611. 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>).
  612. </p>
  613. <p>
  614. 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>.
  615. </p>
  616. <p>
  617. Voici quelques articles choisis :
  618. <a href="/david/blog/2019/faire-equipe/" title="Accéder à l’article complet">Faire équipe</a>,
  619. <a href="/david/blog/2018/bivouac-automnal/" title="Accéder à l’article complet">Bivouac automnal</a>,
  620. <a href="/david/blog/2018/commodite-effondrement/" title="Accéder à l’article complet">Commodité et effondrement</a>,
  621. <a href="/david/blog/2017/donnees-communs/" title="Accéder à l’article complet">Des données aux communs</a>,
  622. <a href="/david/blog/2016/accompagner-enfant/" title="Accéder à l’article complet">Accompagner un enfant</a>,
  623. <a href="/david/blog/2016/senior-developer/" title="Accéder à l’article complet">Senior developer</a>,
  624. <a href="/david/blog/2016/illusion-sociale/" title="Accéder à l’article complet">L’illusion sociale</a>,
  625. <a href="/david/blog/2016/instantane-scopyleft/" title="Accéder à l’article complet">Instantané Scopyleft</a>,
  626. <a href="/david/blog/2016/enseigner-web/" title="Accéder à l’article complet">Enseigner le Web</a>,
  627. <a href="/david/blog/2016/simplicite-defaut/" title="Accéder à l’article complet">Simplicité par défaut</a>,
  628. <a href="/david/blog/2016/minimalisme-esthetique/" title="Accéder à l’article complet">Minimalisme et esthétique</a>,
  629. <a href="/david/blog/2014/un-web-omni-present/" title="Accéder à l’article complet">Un web omni-présent</a>,
  630. <a href="/david/blog/2014/manifeste-developpeur/" title="Accéder à l’article complet">Manifeste de développeur</a>,
  631. <a href="/david/blog/2013/confort-convivialite/" title="Accéder à l’article complet">Confort et convivialité</a>,
  632. <a href="/david/blog/2013/testament-numerique/" title="Accéder à l’article complet">Testament numérique</a>,
  633. et <a href="/david/blog/" title="Accéder aux archives">bien d’autres…</a>
  634. </p>
  635. <p>
  636. 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>.
  637. </p>
  638. <p>
  639. Je ne traque pas ta navigation mais mon
  640. <abbr title="Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33.184162340">hébergeur</abbr>
  641. conserve des logs d’accès.
  642. </p>
  643. </div>
  644. </footer>
  645. <script type="text/javascript">
  646. ;(_ => {
  647. const jumper = document.getElementById('jumper')
  648. jumper.addEventListener('click', e => {
  649. e.preventDefault()
  650. const anchor = e.target.getAttribute('href')
  651. const targetEl = document.getElementById(anchor.substring(1))
  652. targetEl.scrollIntoView({behavior: 'smooth'})
  653. })
  654. })()
  655. </script>