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.

index.html 100KB

5 years ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671
  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>ES6 generators in depth (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="http://www.2ality.com/2015/03/es6-generators.html">
  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. ES6 generators in depth (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="http://www.2ality.com/2015/03/es6-generators.html">Source originale du contenu</a></h3>
  445. <p>This blog post is part of a series on iteration in ES6:</p>
  446. <ol>
  447. <li><a href="http://www.2ality.com/2015/02/es6-iteration.html">Iterables and iterators in ECMAScript 6</a></li>
  448. <li><a href="http://www.2ality.com/2015/03/es6-generators.html">ES6 generators in depth</a></li>
  449. </ol>
  450. <p><hr/>
  451. <p>Generators, a new feature of ECMAScript 6 <a class="ptr">[4]</a>, are functions that can be paused and resumed. This helps with many applications: iterators, asynchronous programming, etc. This blog post explains how generators work and gives an overview of their applications.</p>
  452. <p>The following GitHub repository contains the example code: <a href="https://github.com/rauschma/generator-examples">generator-examples</a></p></p>
  453. <h3 id="overview" class="countheads">Overview</h3>
  454. <p>Two important applications of generators are:</p>
  455. <ul>
  456. <li>Implementing iterables</li>
  457. <li>Blocking on asynchronous function calls</li>
  458. </ul>
  459. <p>The following subsections give brief overviews of these applications, more thorough explanations are provided later (plus discussions of other topics).</p>
  460. <h4 id="implementing_iterables_via_generators">Implementing iterables via generators</h4>
  461. <p>The following function returns an iterable over the properties of an object, one <code>[key,value]</code> pair per property:</p>
  462. <pre class="javascript">
  463. // The asterisk after `function` means that
  464. // `objectEntries` is a generator
  465. function* objectEntries(obj) {
  466. let propKeys = Reflect.ownKeys(obj);
  467. for (let propKey of propKeys) {
  468. // `yield` returns a value and then pauses
  469. // the generator. Later, execution continues
  470. // where it was previously paused.
  471. yield [propKey, obj[propKey]];
  472. }
  473. }
  474. </pre>
  475. <p>How exactly <code>objectEntries()</code> works is explained later. It is used like this:</p>
  476. <pre class="javascript">
  477. let jane = { first: 'Jane', last: 'Doe' };
  478. for (let [key,value] of objectEntries(jane)) {
  479. console.log(`${key}: ${value}`);
  480. }
  481. // Output:
  482. // first: Jane
  483. // last: Doe
  484. </pre>
  485. <h4 id="blocking_on_asynchronous_function_calls">Blocking on asynchronous function calls</h4>
  486. <p>In the following code, I use <a href="https://github.com/tj/co">the control flow library co</a> to asynchronously retrieve two JSON files. Note how, in line (A), execution blocks (waits) until the result of <code>Promise.all()</code> is ready. That means that the code looks synchronous while performing asynchronous operations.</p>
  487. <pre class="javascript">
  488. co(function* () {
  489. try {
  490. let [croftStr, bondStr] = yield Promise.all([ // (A)
  491. getFile('http://localhost:8000/croft.json'),
  492. getFile('http://localhost:8000/bond.json'),
  493. ]);
  494. let croftJson = JSON.parse(croftStr);
  495. let bondJson = JSON.parse(bondStr);
  496. console.log(croftJson);
  497. console.log(bondJson);
  498. } catch (e) {
  499. console.log('Failure to read: ' + e);
  500. }
  501. });
  502. </pre>
  503. <p><code>getFile(url)</code> retrieves the file pointed to by <code>url</code>. Its implementation is shown later. I’ll also explain how <code>co</code> works.</p>
  504. <h3 id="what_are_generators%3F">What are generators?</h3>
  505. <p><em>Generators</em> are functions that can be paused and resumed, which enables a variety of applications.</p>
  506. <p>As a first example, consider the following generator function whose name is <code>genFunc</code>:</p>
  507. <pre class="javascript">
  508. function* genFunc() {
  509. console.log('First');
  510. yield; // (A)
  511. console.log('Second'); // (B)
  512. }
  513. </pre>
  514. <p>Two things distinguish <code>genFunc</code> from a normal function declaration:</p>
  515. <ul>
  516. <li>It starts with the “keyword” <code>function*</code>.</li>
  517. <li>It is paused in the middle via <code>yield</code>.</li>
  518. </ul>
  519. <p>Calling <code>genFunc</code> does not execute it. Instead, it returns a so-called <em>generator object</em> that lets us control <code>genFunc</code>’s execution:</p>
  520. <pre class="repl">
  521. &gt; let genObj = genFunc();
  522. </pre>
  523. <p><code>genFunc()</code> is initially suspended at the beginning of its body. The method <code>genObj.next()</code> continues the execution of <code>genFunc</code>, until the next <code>yield</code>:</p>
  524. <pre class="repl">
  525. &gt; genObj.next()
  526. First
  527. { value: undefined, done: false }
  528. </pre>
  529. <p>As you can see in the last line, <code>genObj.next()</code> also returns an object. Let’s ignore that for now. It will matter once we look at generators as iterators.</p>
  530. <p><code>genFunc</code> is now paused in line (A). If we call <code>next()</code> again, execution resumes and line (B) is executed:</p>
  531. <pre class="repl">
  532. &gt; genObj.next()
  533. Second
  534. { value: undefined, done: true }
  535. </pre>
  536. <p>Afterwards, the function is finished, execution has left the body and further calls of <code>genObj.next()</code> have no effect.</p>
  537. <h4 id="ways_of_creating_generators">Ways of creating generators</h4>
  538. <p>There are four ways in which you can create generators:</p>
  539. <ol>
  540. <li><p>Via a generator function declaration:</p>
  541. <pre class="javascript">
  542. function* genFunc() { ··· }
  543. let genObj = genFunc();
  544. </pre>
  545. </li>
  546. <li><p>Via a generator function expression:</p>
  547. <pre class="javascript">
  548. const genFunc = function* () { ··· };
  549. let genObj = genFunc();
  550. </pre>
  551. </li>
  552. <li><p>Via a generator method definition in an object literal:</p>
  553. <pre class="javascript">
  554. let obj = {
  555. * generatorMethod() {
  556. ···
  557. }
  558. };
  559. let genObj = obj.generatorMethod();
  560. </pre>
  561. </li>
  562. <li><p>Via a generator method definition in a class definition (which can be a class declaration or a class expression <a class="ptr">[11]</a>):</p>
  563. <pre class="javascript">
  564. class MyClass {
  565. * generatorMethod() {
  566. ···
  567. }
  568. }
  569. let myInst = new MyClass();
  570. let genObj = myInst.generatorMethod();
  571. </pre>
  572. </li>
  573. </ol>
  574. <h4 id="roles_played_by_generators">Roles played by generators</h4>
  575. <p>Generators can play three roles:</p>
  576. <ol>
  577. <li><p>Iterators (data producers): Each <code>yield</code> can return a value via <code>next()</code>, which means that generators can produce sequences of values via loops and recursion. Due to generator objects implementing the interface <code>Iterable</code> <a class="ptr">[5]</a>, these sequences can be processed by any ECMAScript 6 construct that supports iterables. Two examples are: <code>for-of</code> loops and the spread operator (<code>...</code>).</p>
  578. </li>
  579. <li><p>Observers (data consumers): <code>yield</code> can also receive a value from <code>next()</code> (via a parameter). That means that generators become data consumers that pause until a new value is pushed into them via <code>next()</code>.</p>
  580. </li>
  581. <li><p>Coroutines (data producers and consumers): Given that generators are pausable and can be both data producers and data consumers, not much work is needed to turn them into coroutines (cooperatively multitasked tasks).</p>
  582. </li>
  583. </ol>
  584. <p>The next sections provide deeper explanations of these roles.</p>
  585. <h3 id="generators_as_iterators_%28data_production%29">Generators as iterators (data production)</h3>
  586. <p>As explained before, generator objects can be data producers, data consumers or both. This section looks at them as data producers, where they implement both the interfaces <code>Iterable</code> and <code>Iterator</code> (shown below). Note that that means that the result of a generator function is both an iterable and an iterator. The full interface of generator objects will be shown later.</p>
  587. <pre class="javascript">
  588. interface Iterable {
  589. [Symbol.iterator]() : Iterator;
  590. }
  591. interface Iterator {
  592. next() : IteratorResult;
  593. return?(value? : any) : IteratorResult;
  594. }
  595. interface IteratorResult {
  596. value : any;
  597. done : boolean;
  598. }
  599. </pre>
  600. <p>A generator function produces a sequence of values via <code>yield</code>, a data consumer consumes thoses values via the iterator method <code>next()</code>. For example, the following generator function produces the values <code>'a'</code> and <code>'b'</code>:</p>
  601. <pre class="javascript">
  602. function* genFunc() {
  603. yield 'a';
  604. yield 'b';
  605. }
  606. </pre>
  607. <p>This interaction shows how to retrieve the yielded values via the generator object <code>genObj</code>:</p>
  608. <pre class="repl">
  609. &gt; let genObj = genFunc();
  610. &gt; genObj.next()
  611. { value: 'a', done: false }
  612. &gt; genObj.next()
  613. { value: 'b', done: false }
  614. &gt; genObj.next() // end of sequence reached
  615. ( value: undefined, done: true }
  616. </pre>
  617. <h4 id="ways_of_iterating_over_a_generator">Ways of iterating over a generator</h4>
  618. <p>As generator objects are iterable, ES6 language constructs that support iterables can be applied to them. The following three ones are especially important.</p>
  619. <p>First, the <code>for-of</code> loop:</p>
  620. <pre class="javascript">
  621. for (let x of genFunc()) {
  622. console.log(x);
  623. }
  624. // Output:
  625. // a
  626. // b
  627. </pre>
  628. <p>Second, the spread operator (<code>...</code>), which turns iterated sequences into elements of an array (consult <a class="ptr">[7]</a> for more information on this operator):</p>
  629. <pre class="javascript">
  630. let arr = [...genFunc()]; // ['a', 'b']
  631. </pre>
  632. <p>Third, destructuring <a class="ptr">[6]</a>:</p>
  633. <pre class="repl">
  634. &gt; let [x, y] = genFunc();
  635. &gt; x
  636. 'a'
  637. &gt; y
  638. 'b'
  639. </pre>
  640. <h4 id="returning_from_a_generator">Returning from a generator</h4>
  641. <p>The previous generator function did not contain an explicit <code>return</code>. An implicit <code>return</code> is equivalent to returning <code>undefined</code>. Let’s examine a generator with an explicit <code>return</code>:</p>
  642. <pre class="javascript">
  643. function* genFuncWithReturn() {
  644. yield 'a';
  645. yield 'b';
  646. return 'result';
  647. }
  648. </pre>
  649. <p>The returned value shows up in the last object returned by <code>next()</code>, whose property <code>done</code> is <code>true</code>:</p>
  650. <pre class="repl">
  651. &gt; let genObjWithReturn = genFuncWithReturn();
  652. &gt; genObjWithReturn.next()
  653. { value: 'a', done: false }
  654. &gt; genObjWithReturn.next()
  655. { value: 'b', done: false }
  656. &gt; genObjWithReturn.next()
  657. { value: 'result', done: true }
  658. </pre>
  659. <p>However, most constructs that work with iterables ignore the value inside the <code>done</code> object:</p>
  660. <pre class="javascript">
  661. for (let x of genFuncWithReturn()) {
  662. console.log(x);
  663. }
  664. // Output:
  665. // a
  666. // b
  667. let arr = [...genFuncWithReturn()]; // ['a', 'b']
  668. </pre>
  669. <p><code>yield*</code>, an operator for making recursive generator calls, does consider values inside <code>done</code> objects. It is explained later.</p>
  670. <h4 id="example%3A_iterating_over_properties">Example: iterating over properties</h4>
  671. <p>Let’s look at an example that demonstrates how convenient generators are for implementing iterables. The following function, <code>objectEntries()</code>, returns an iterable over the properties of an object:</p>
  672. <pre class="javascript">
  673. function* objectEntries(obj) {
  674. // In ES6, you can use strings or symbols as property keys,
  675. // Reflect.ownKeys() retrieves both
  676. let propKeys = Reflect.ownKeys(obj);
  677. for (let propKey of propKeys) {
  678. yield [propKey, obj[propKey]];
  679. }
  680. }
  681. </pre>
  682. <p>This function enables you to iterate over the properties of an object <code>jane</code> via the <code>for-of</code> loop:</p>
  683. <pre class="javascript">
  684. let jane = { first: 'Jane', last: 'Doe' };
  685. for (let [key,value] of objectEntries(jane)) {
  686. console.log(`${key}: ${value}`);
  687. }
  688. // Output:
  689. // first: Jane
  690. // last: Doe
  691. </pre>
  692. <p>For comparison – an implementation of <code>objectEntries()</code> that doesn’t use generators is much more complicated:</p>
  693. <pre class="javascript">
  694. function objectEntries(obj) {
  695. let index = 0;
  696. let propKeys = Reflect.ownKeys(obj);
  697. return {
  698. [Symbol.iterator]() {
  699. return this;
  700. },
  701. next() {
  702. if (index &lt; propKeys.length) {
  703. let key = propKeys[index];
  704. index++;
  705. return { value: [key, obj[key]] };
  706. } else {
  707. return { done: true };
  708. }
  709. }
  710. };
  711. }
  712. </pre>
  713. <h4 id="recursion_via_yield*_%28for_output%29">Recursion via <code>yield*</code> (for output)</h4>
  714. <p>The <code>yield*</code> operator lets you call another generator from within a generator, as if you made a function call. For now, I only explain how this works if both generators produce output, I’ll later explain how things work if input is involved.</p>
  715. <p>How can one generator recursively call another generator? Let’s assume you have written a generator function <code>foo</code>:</p>
  716. <pre class="javascript">
  717. function* foo() {
  718. yield 'a';
  719. yield 'b';
  720. }
  721. </pre>
  722. <p>How would you call <code>foo</code> from another generator function <code>bar</code>? The following approach does not work!</p>
  723. <pre class="javascript">
  724. function* bar() {
  725. yield 'x';
  726. foo(); // does nothing!
  727. yield 'y';
  728. }
  729. </pre>
  730. <p>Calling <code>foo()</code> returns an object, but does not actually execute <code>foo()</code>. That’s why ECMAScript 6 has the operator <code>yield*</code> for making recursive generator calls:</p>
  731. <pre class="javascript">
  732. function* bar() {
  733. yield 'x';
  734. yield* foo();
  735. yield 'y';
  736. }
  737. // Collect all values yielded by bar() in an array
  738. let arr = [...bar()];
  739. // ['x', 'a', 'b', 'y']
  740. </pre>
  741. <p>Internally, <code>yield*</code> works roughly as follows:</p>
  742. <pre class="javascript">
  743. function* bar() {
  744. yield 'x';
  745. for (let value of foo()) {
  746. yield value;
  747. }
  748. yield 'y';
  749. }
  750. </pre>
  751. <p>The operand of <code>yield*</code> does not have to be a generator object, it can be any iterable:</p>
  752. <pre class="javascript">
  753. function* bla() {
  754. yield 'sequence';
  755. yield* ['of', 'yielded'];
  756. yield 'values';
  757. }
  758. let arr = [...bla()];
  759. // ['sequence', 'of', 'yielded', 'values']
  760. </pre>
  761. <h5 id="yield*_considers_end-of-iteration_values"><code>yield*</code> considers end-of-iteration values</h5>
  762. <p>Most constructs that support iterables ignore the value included in the end-of-iteration object (whose property <code>done</code> is <code>true</code>). Generators provide that value via <code>return</code>. The result of <code>yield*</code> is the end-of-iteration value:</p>
  763. <pre class="javascript">
  764. function* genFuncWithReturn() {
  765. yield 'a';
  766. yield 'b';
  767. return 'The result';
  768. }
  769. function* logReturned(genObj) {
  770. let result = yield* genObj;
  771. console.log(result); // (A)
  772. }
  773. </pre>
  774. <p>If we want to get to line (A), we first must iterate over all values yielded by <code>logReturned()</code>:</p>
  775. <pre class="repl">
  776. &gt; [...logReturned(genFuncWithReturn())]
  777. The result
  778. [ 'a', 'b' ]
  779. </pre>
  780. <h5 id="iterating_over_trees">Iterating over trees</h5>
  781. <p>Iterating over a tree with recursion is simple, writing an iterator for a tree with traditional means is complicated. That’s why generators shine here: they let you implement an iterator via recursion. As an example, consider the following data structure for binary trees. It is iterable, because it has a method whose key is <code>Symbol.iterator</code>. That method is a generator method and returns an iterator when called.</p>
  782. <pre class="javascript">
  783. class BinaryTree {
  784. constructor(value, left=null, right=null) {
  785. this.value = value;
  786. this.left = left;
  787. this.right = right;
  788. }
  789. /** Prefix iteration */
  790. * [Symbol.iterator]() {
  791. yield this.value;
  792. if (this.left) {
  793. yield* this.left;
  794. }
  795. if (this.right) {
  796. yield* this.right;
  797. }
  798. }
  799. }
  800. </pre>
  801. <p>The following code creates a binary tree and iterates over it via <code>for-of</code>:</p>
  802. <pre class="javascript">
  803. let tree = new BinaryTree('a',
  804. new BinaryTree('b',
  805. new BinaryTree('c'),
  806. new BinaryTree('d')),
  807. new BinaryTree('e'));
  808. for (let x of tree) {
  809. console.log(x);
  810. }
  811. // Output:
  812. // a
  813. // b
  814. // c
  815. // d
  816. // e
  817. </pre>
  818. <h4 id="you_can_only_yield_in_generators">You can only <code>yield</code> in generators</h4>
  819. <p>A significant limitation of generators is that you can only yield while you are (statically) inside a generator function. That is, yielding in callbacks doesn’t work:</p>
  820. <pre class="javascript">
  821. function* genFunc() {
  822. ['a', 'b'].forEach(x =&gt; yield x); // SyntaxError
  823. }
  824. </pre>
  825. <p><code>yield</code> is not allowed inside non-generator functions, which is why the previous code causes a syntax error. In this case, it is easy to rewrite the code so that it doesn’t use callbacks (as shown below). But unfortunately that isn’t always possibe.</p>
  826. <pre class="javascript">
  827. function* genFunc() {
  828. for (let x of ['a', 'b']) {
  829. yield x; // OK
  830. }
  831. }
  832. </pre>
  833. <p>The upsides of this limitation are explained later: they make generators easier to implement and compatible with event loops.</p>
  834. <h3 id="generators_as_observers_%28data_consumption%29">Generators as observers (data consumption)</h3>
  835. <p>As consumers of data, generator objects conform to the second half of the generator interface, <code>Observer</code>:</p>
  836. <pre class="javascript">
  837. interface Observer {
  838. next(value? : any) : void;
  839. return(value? : any) : void;
  840. throw(error) : void;
  841. }
  842. </pre>
  843. <p>As an observer, a generator pauses until it receives input. There are three kinds of input, transmitted via the methods specified by the interface:</p>
  844. <ul>
  845. <li><code>next()</code> sends normal input.</li>
  846. <li><code>return()</code> terminates the generator.</li>
  847. <li><code>throw()</code> signals an error.</li>
  848. </ul>
  849. <h4 id="sending_values_via_next%28%29">Sending values via <code>next()</code></h4>
  850. <p>If you use a generator as an observer, you send values to it via <code>next()</code> and it receives those values via <code>yield</code>:</p>
  851. <pre class="javascript">
  852. function* dataConsumer() {
  853. console.log('Started');
  854. console.log(`1. ${yield}`); // (A)
  855. console.log(`2. ${yield}`);
  856. return 'result';
  857. }
  858. </pre>
  859. <p>Let’s use this generator interactively. First, we create a generator object:</p>
  860. <pre class="repl">
  861. &gt; let genObj = dataConsumer();
  862. </pre>
  863. <p>We now call <code>genObj.next()</code>, which starts the generator. Execution continues until the first <code>yield</code>, which is where the generator pauses. The result of <code>next()</code> is the value yielded in line (A) (<code>undefined</code>, because <code>yield</code> doesn’t have an operand). In this section, we are not interested in what <code>next()</code> returns, because we only use it to send values, not to retrieve values.</p>
  864. <pre class="javascript">
  865. &gt; genObj.next()
  866. Started
  867. { value: undefined, done: false }
  868. </pre>
  869. <p>We call <code>next()</code> two more times, in order to send the value <code>'a'</code> to the first <code>yield</code> and the value <code>'b'</code> to the second <code>yield</code>:</p>
  870. <pre class="javascript">
  871. &gt; genObj.next('a')
  872. 1. a
  873. { value: undefined, done: false }
  874. &gt; genObj.next('b')
  875. 2. b
  876. { value: 'result', done: true }
  877. </pre>
  878. <p>The result of the last <code>next()</code> is the value returned from <code>dataConsumer()</code>. <code>done</code> being <code>true</code> indicates that the generator is finished.</p>
  879. <p>Unfortunately, <code>next()</code> is asymmetric, but that can’t be helped: It always sends a value to the currently suspended <code>yield</code>, but returns the operand of the following <code>yield</code>.</p>
  880. <h5 id="the_first_next%28%29">The first <code>next()</code></h5>
  881. <p>When using a generator as an observer, it is important to note that the only purpose of the first invocation of <code>next()</code> is to start the observer. It is only ready for input afterwards, because this first invocation has advanced execution to the first <code>yield</code>. Therefore, you can’t send input via the first <code>next()</code> – you even get an error if you do:</p>
  882. <pre class="repl">
  883. &gt; function* g() { yield }
  884. &gt; g().next('hello')
  885. TypeError: attempt to send 'hello' to newborn generator
  886. </pre>
  887. <p>The following utility function fixes this issue:</p>
  888. <pre class="javascript">
  889. /**
  890. * Returns a function that, when called,
  891. * returns a generator object that is immediately
  892. * ready for input via `next()`
  893. */
  894. function coroutine(generatorFunction) {
  895. return function (...args) {
  896. let generatorObject = generatorFunction(...args);
  897. generatorObject.next();
  898. return generatorObject;
  899. };
  900. }
  901. </pre>
  902. <p>To see how <code>coroutine()</code> works, let’s compare a wrapped generator with a normal one:</p>
  903. <pre class="javascript">
  904. const wrapped = coroutine(function* () {
  905. console.log(`First input: ${yield}`);
  906. return 'DONE';
  907. });
  908. const normal = function* () {
  909. console.log(`First input: ${yield}`);
  910. return 'DONE';
  911. };
  912. </pre>
  913. <p>The wrapped generator is immediately ready for input:</p>
  914. <pre class="repl">
  915. &gt; wrapped().next('hello!')
  916. First input: hello!
  917. </pre>
  918. <p>The normal generator needs an extra <code>next()</code> until it is ready for input:</p>
  919. <pre class="repl">
  920. &gt; let genObj = normal();
  921. &gt; genObj.next()
  922. { value: undefined, done: false }
  923. &gt; genObj.next('hello!')
  924. First input: hello!
  925. { value: 'DONE', done: true }
  926. </pre>
  927. <h4 id="yield_binds_loosely"><code>yield</code> binds loosely</h4>
  928. <p><code>yield</code> binds very loosely, so that we don’t have to put its operand in parentheses:</p>
  929. <pre class="javascript">
  930. yield a + b + c;
  931. </pre>
  932. <p>This is treated as:</p>
  933. <pre class="javascript">
  934. yield (a + b + c);
  935. </pre>
  936. <p>Not as:</p>
  937. <pre class="javascript">
  938. (yield a) + b + c;
  939. </pre>
  940. <p>As a consequence, many operators bind more tightly than <code>yield</code> and you have to put <code>yield</code> in parentheses if you want to use it as an operand. For example, you get a SyntaxError if you make an unparenthesized <code>yield</code> an operand of plus:</p>
  941. <pre class="javascript">
  942. console.log('Hello' + yield); // SyntaxError
  943. console.log('Hello' + yield 123); // SyntaxError
  944. console.log('Hello' + (yield)); // OK
  945. console.log('Hello' + (yield 123)); // OK
  946. </pre>
  947. <p>You do not need parens if <code>yield</code> is a direct argument in a function or method call:</p>
  948. <pre class="javascript">
  949. foo(yield 'a', yield 'b');
  950. </pre>
  951. <p>You also don’t need parens if you use <code>yield</code> on the right-hand side of an assignment:</p>
  952. <pre class="javascript">
  953. let input = yield;
  954. </pre>
  955. <h5 id="grammar">Grammar</h5>
  956. <p>The need for parens can be seen in the following grammar rules in the <a href="https://people.mozilla.org/~jorendorff/es6-draft.html#sec-expressions">ECMAScript 6 specification</a>. These rules describe how expressions are parsed. I list them here from general (loose binding, lower precedence) to specific (tight binding, higher precedence). Wherever a certain kind of expression is demanded, you can also use more specific ones. The opposite is not true. The hierarchy ends with <code>ParenthesizedExpression</code>, which means that you can mention any expression anywhere, if you put it in parentheses.</p>
  957. <pre class="undefined">
  958. Expression :
  959. AssignmentExpression
  960. Expression , AssignmentExpression
  961. AssignmentExpression :
  962. ConditionalExpression
  963. YieldExpression
  964. ArrowFunction
  965. LeftHandSideExpression = AssignmentExpression
  966. LeftHandSideExpression AssignmentOperator AssignmentExpression
  967. ···
  968. AdditiveExpression :
  969. MultiplicativeExpression
  970. AdditiveExpression + MultiplicativeExpression
  971. AdditiveExpression - MultiplicativeExpression
  972. MultiplicativeExpression :
  973. UnaryExpression
  974. MultiplicativeExpression MultiplicativeOperator UnaryExpression
  975. ···
  976. PrimaryExpression :
  977. this
  978. IdentifierReference
  979. Literal
  980. ArrayLiteral
  981. ObjectLiteral
  982. FunctionExpression
  983. ClassExpression
  984. GeneratorExpression
  985. RegularExpressionLiteral
  986. TemplateLiteral
  987. ParenthesizedExpression
  988. ParenthesizedExpression :
  989. ( Expression )
  990. </pre>
  991. <p>The operands of an <code>AdditiveExpression</code> are an <code>AdditiveExpression</code> and a <code>MultiplicativeExpression</code>. Therefore, using a (more specific) <code>ParenthesizedExpression</code> as an operand is OK, but using a (more general) <code>YieldExpression</code> isn’t.</p>
  992. <h4 id="return%28%29_and_throw%28%29"><code>return()</code> and <code>throw()</code></h4>
  993. <p>Let’s recap how <code>next(x)</code> works (after the first invocation):</p>
  994. <ol>
  995. <li>The generator is currently suspended at a <code>yield</code> operator.</li>
  996. <li>Send the value <code>x</code> to that <code>yield</code>, which means that it evaluates to <code>x</code>.</li>
  997. <li>Proceed to the next <code>yield</code> or <code>return</code>:<ul>
  998. <li><code>yield x</code> leads to <code>next()</code> returning with <code>{ value: x, done: false }</code></li>
  999. <li><code>return x</code> leads to <code>next()</code> returning with <code>{ value: x, done: true }</code></li>
  1000. </ul>
  1001. </li>
  1002. </ol>
  1003. <p><code>return()</code> and <code>throw()</code> work similarly to <code>next()</code>, but they do something different in step 2:</p>
  1004. <ul>
  1005. <li><code>return(x)</code> executes <code>return x</code> at the location of <code>yield</code>.</li>
  1006. <li><code>throw(x)</code> executes <code>throw x</code> at the location of <code>yield</code>.</li>
  1007. </ul>
  1008. <h4 id="return%28%29_terminates_the_generator"><code>return()</code> terminates the generator</h4>
  1009. <p><code>return()</code> performs a <code>return</code> at the location of the <code>yield</code> that led to the last suspension of the generator. Let’s use the following generator function to see how that works.</p>
  1010. <pre class="javascript">
  1011. function* genFunc1() {
  1012. try {
  1013. console.log('Started');
  1014. yield; // (A)
  1015. } finally {
  1016. console.log('Exiting');
  1017. }
  1018. }
  1019. </pre>
  1020. <p>In the following interaction, we first use <code>next()</code> to start the generator and to proceed until the <code>yield</code> in line (A). Then we return from that location via <code>return()</code>.</p>
  1021. <pre class="repl">
  1022. &gt; let genObj1 = genFunc1();
  1023. &gt; genObj1.next()
  1024. Started
  1025. { value: undefined, done: false }
  1026. &gt; genObj1.return('Result')
  1027. Exiting
  1028. { value: 'Result', done: true }
  1029. </pre>
  1030. <h5 id="preventing_termination">Preventing termination</h5>
  1031. <p>You can prevent <code>return()</code> from terminating the generator if you yield inside the <code>finally</code> clause (using a <code>return</code> statement in that clause is also possible):</p>
  1032. <pre class="javascript">
  1033. function* genFunc2() {
  1034. try {
  1035. console.log('Started');
  1036. yield;
  1037. } finally {
  1038. yield 'Not done, yet!';
  1039. }
  1040. }
  1041. </pre>
  1042. <p>This time, <code>return()</code> does not exit the generator function. Accordingly, the property <code>done</code> of the object it returns is <code>false</code>.</p>
  1043. <pre class="repl">
  1044. &gt; let genObj2 = genFunc2();
  1045. &gt; genObj2.next()
  1046. Started
  1047. { value: undefined, done: false }
  1048. &gt; genObj2.return('Result')
  1049. { value: 'Not done, yet!', done: false }
  1050. </pre>
  1051. <p>You can invoke <code>next()</code> one more time. Similarly to non-generator functions, the return value of the generator function is the value that was queued prior to entering the <code>finally</code> clause.</p>
  1052. <pre class="repl">
  1053. &gt; genObj2.next()
  1054. { value: 'Result', done: true }
  1055. </pre>
  1056. <h5 id="returning_from_a_newborn_generator">Returning from a newborn generator</h5>
  1057. <p>Returning a value from a <em>newborn</em> generator (that hasn’t started yet) is allowed:</p>
  1058. <pre class="repl">
  1059. &gt; function* genFunc() {}
  1060. &gt; genFunc().return('yes')
  1061. { value: 'yes', done: true }
  1062. </pre>
  1063. <h4 id="throw%28%29_signals_an_error"><code>throw()</code> signals an error</h4>
  1064. <p><code>throw()</code> throws an exception at the location of the <code>yield</code> that led to the last suspension of the generator. Let’s examine how that works via the following generator function.</p>
  1065. <pre class="javascript">
  1066. function* genFunc1() {
  1067. try {
  1068. console.log('Started');
  1069. yield; // (A)
  1070. } catch (error) {
  1071. console.log('Caught: ' + error);
  1072. }
  1073. }
  1074. </pre>
  1075. <p>In the following interaction, we first use <code>next()</code> to start the generator and proceed until the <code>yield</code> in line (A). Then we throw an exception from that location.</p>
  1076. <pre class="javascript">
  1077. &gt; let genObj1 = genFunc1();
  1078. &gt; genObj1.next()
  1079. Started
  1080. { value: undefined, done: false }
  1081. &gt; genObj1.throw(new Error('Problem!'))
  1082. Caught: Error: Problem!
  1083. { value: undefined, done: true }
  1084. </pre>
  1085. <p>The result of <code>throw()</code> (shown in the last line) stems from us leaving the function with an implicit <code>return</code>.</p>
  1086. <h5 id="uncaught_exceptions">Uncaught exceptions</h5>
  1087. <p>If you don’t catch the exception inside the generator, it is thrown by <code>throw()</code>. For example, the following generator function does not catch exceptions:</p>
  1088. <pre class="javascript">
  1089. function* genFunc2() {
  1090. console.log('Started');
  1091. yield; // (A)
  1092. }
  1093. </pre>
  1094. <p>If we use <code>throw()</code> to throw an instance of <code>Error</code> at line (A), the method itself throws that error:</p>
  1095. <pre class="repl">
  1096. &gt; let genObj2 = genFunc2();
  1097. &gt; genObj2.next()
  1098. Started
  1099. { value: undefined, done: false }
  1100. &gt; genObj2.throw(new Error('Problem!'))
  1101. Error: Problem!
  1102. </pre>
  1103. <h5 id="throwing_from_a_newborn_generator">Throwing from a newborn generator</h5>
  1104. <p>Throwing an exception in a <em>newborn</em> generator (that hasn’t started yet) is allowed:</p>
  1105. <pre class="repl">
  1106. &gt; function* genFunc() {}
  1107. &gt; genFunc().throw(new Error('Problem!'))
  1108. Error: Problem!
  1109. </pre>
  1110. <h4 id="example%3A_processing_asynchronously_pushed_data">Example: processing asynchronously pushed data</h4>
  1111. <p>The fact that generators-as-observers pause while they wait for input makes them perfect for on-demand processing of data that is received asynchronously. The pattern for setting up a chain of generators for processing is as follows:</p>
  1112. <ul>
  1113. <li><p>First chain member: a normal function that has a parameter <code>target</code>, which is the generator object of the next element in the chain of generators. The function makes an asynchronous request and pushes the results to the target via <code>target.next()</code>.</p>
  1114. </li>
  1115. <li><p>Intermediate chain members: generators that have a parameter <code>target</code>. They receive data via <code>yield</code> and send data via <code>target.next()</code>.</p>
  1116. </li>
  1117. <li><p>Last chain member: a generator that has no parameter <code>target</code> and only receives data.</p>
  1118. </li>
  1119. </ul>
  1120. <p>As an example, let’s chain generators to process a file that is read asynchronously. The code shown in this section is a Node.js script that is executed via <code>babel-node</code> <a class="ptr">[8]</a>.</p>
  1121. <p>The following code sets up the chain, which starts with the normal function <code>readFile</code>, continues with the generators <code>splitLines</code> and <code>numberLines</code> and ends with the generator <code>printLines</code>:</p>
  1122. <pre class="javascript">
  1123. let fileName = process.argv[2];
  1124. readFile(fileName, chain(splitLines, numberLines, printLines));
  1125. </pre>
  1126. <p>I’ll explain what these functions do when I show their code. The following helper function sets up a chain of generators: Starting with the last generator, each generator function is called and the resulting generator object is used to start the generator via an initial <code>next()</code>. If a generator has a successor, it receives the successor’s generator object via the parameter <code>target</code>. The result of <code>chain()</code> is the generator object of the first generator function (in our example: <code>splitLines</code>).</p>
  1127. <pre class="javascript">
  1128. function chain(...generatorFuncs) {
  1129. if (generatorFuncs.length &lt; 1) {
  1130. throw new Error('Need at least 1 argument');
  1131. }
  1132. let generatorObject = generatorFuncs[generatorFuncs.length-1]();
  1133. generatorObject.next(); // generator is now ready for input
  1134. for (let i=generatorFuncs.length-2; i &gt;= 0; i--) {
  1135. let generatorFunction = generatorFuncs[i];
  1136. // Link current generator to successor
  1137. generatorObject = generatorFunction(generatorObject);
  1138. // Start the generator
  1139. generatorObject.next();
  1140. }
  1141. return generatorObject;
  1142. }
  1143. </pre>
  1144. <p><code>readFile()</code> is the non-generator function that starts everything.</p>
  1145. <pre class="javascript">
  1146. import {createReadStream} from 'fs';
  1147. /**
  1148. * Create an asynchronous ReadStream for the file whose name
  1149. * is `fileName` and feed it to the generator object `target`.
  1150. *
  1151. * @see ReadStream https://nodejs.org/api/fs.html#fs_class_fs_readstream
  1152. */
  1153. function readFile(fileName, target) {
  1154. let readStream = createReadStream(fileName,
  1155. { encoding: 'utf8', bufferSize: 1024 });
  1156. readStream.on('data', buffer =&gt; {
  1157. let str = buffer.toString('utf8');
  1158. target.next(str);
  1159. });
  1160. readStream.on('end', () =&gt; {
  1161. // Signal end of output sequence
  1162. target.return();
  1163. });
  1164. }
  1165. </pre>
  1166. <p>The chain of generators starts with <code>splitLines</code>:</p>
  1167. <pre class="javascript">
  1168. /**
  1169. * Turns a sequence of text chunks into a sequence of lines
  1170. * (where lines are separated by newlines)
  1171. */
  1172. function* splitLines(target) {
  1173. let previous = '';
  1174. try {
  1175. while (true) {
  1176. previous += yield;
  1177. let eolIndex;
  1178. while ((eolIndex = previous.indexOf('\n')) &gt;= 0) {
  1179. let line = previous.slice(0, eolIndex);
  1180. target.next(line);
  1181. previous = previous.slice(eolIndex+1);
  1182. }
  1183. }
  1184. } finally {
  1185. // Handle the end of the input sequence
  1186. // (signaled via `return()`)
  1187. if (previous.length &gt; 0) {
  1188. target.next(previous);
  1189. }
  1190. // Signal end of output sequence
  1191. target.return();
  1192. }
  1193. }
  1194. </pre>
  1195. <p>Note an important pattern:</p>
  1196. <ul>
  1197. <li><code>readFile</code> uses the generator object method <code>return()</code> to signal the end of the sequence of chunks that it sends.</li>
  1198. <li><code>readFile</code> sends that signal while <code>splitLines</code> is waiting for input via <code>yield</code>, inside an infinite loop. <code>return()</code> breaks from that loop.</li>
  1199. <li><code>splitLines</code> uses a <code>finally</code> clause to handle the end-of-sequence.</li>
  1200. </ul>
  1201. <p>The next generator is <code>numberLines</code>:</p>
  1202. <pre class="javascript">
  1203. /**
  1204. * Prefixes numbers to a sequence of lines
  1205. */
  1206. function* numberLines(target) {
  1207. try {
  1208. for (let lineNo = 0; ; lineNo++) {
  1209. let line = yield;
  1210. target.next(`${lineNo}: ${line}`);
  1211. }
  1212. } finally {
  1213. // Signal end of output sequence
  1214. target.return();
  1215. }
  1216. }
  1217. </pre>
  1218. <p>The last generator is <code>printLines</code>:</p>
  1219. <pre class="javascript">
  1220. /**
  1221. * Receives a sequence of lines (without newlines)
  1222. * and logs them (adding newlines).
  1223. */
  1224. function* printLines() {
  1225. while (true) {
  1226. let line = yield;
  1227. console.log(line);
  1228. }
  1229. }
  1230. </pre>
  1231. <p>The neat thing about this code is that everything happens lazily (on demand): lines are split, numbered and printed as they arrive; we don’t have to wait for all of the text before we can start printing.</p>
  1232. <h4 id="yield*%3A_the_full_story"><code>yield*</code>: the full story</h4>
  1233. <p>So far, we have only seen one aspect of <code>yield</code>: it propagates yielded values from the callee to the caller. Now that we are interested in generators receiving input, another aspect becomes relevant: <code>yield*</code> also forwards input received by the caller to the callee.</p>
  1234. <p>I’ll first explain the complete semantics of <code>yield*</code> by showing how you’d implemented it in JavaScript. Then I give simple examples where input received by the caller via <code>next()</code>, <code>return()</code> and <code>throw()</code> is forwarded to the callee.</p>
  1235. <p>The following statement:</p>
  1236. <pre class="javascript">
  1237. let yieldStarResult = yield* calleeFunc();
  1238. </pre>
  1239. <p>is roughly equivalent to:</p>
  1240. <pre class="javascript">
  1241. let yieldStarResult;
  1242. let calleeObj = calleeFunc();
  1243. let prevReceived = undefined;
  1244. while (true) {
  1245. try {
  1246. // Forward input previously received
  1247. let item = calleeObj.next(prevReceived);
  1248. if (item.done) {
  1249. yieldStarResult = item.value;
  1250. break;
  1251. }
  1252. prevReceived = yield item.value;
  1253. } catch (e) {
  1254. // Pretend `return` can be caught like an exception
  1255. if (e instanceof Return) {
  1256. // Forward input received via return()
  1257. calleeObj.return(e.returnedValue);
  1258. return e.returnedValue; // “re-throw”
  1259. } else {
  1260. // Forward input received via throw()
  1261. calleeObj.throw(e); // may throw
  1262. }
  1263. }
  1264. }
  1265. </pre>
  1266. <p>To keep things simple, several things are missing in this code:</p>
  1267. <ul>
  1268. <li><p>The operand of <code>yield*</code> can be any iterable value.</p>
  1269. </li>
  1270. <li><p><code>return()</code> and <code>throw()</code> are optional iterator methods. We should only call them if they exist.</p>
  1271. </li>
  1272. <li><p>If an exception is received and <code>throw()</code> does not exist, but <code>return()</code> does then <code>return()</code> is called (before throwing an exception) to give <code>calleeObject</code> the opportunity to clean up.</p>
  1273. </li>
  1274. <li><p><code>calleeObj</code> can refuse to close, by returning an object whose property <code>done</code> is <code>false</code>. Then the caller also has to refuse to close and <code>yield*</code> must continue to iterate.</p>
  1275. </li>
  1276. </ul>
  1277. <h5 id="example%3A_yield*_forwards_next%28%29">Example: <code>yield*</code> forwards <code>next()</code></h5>
  1278. <p>The following generator function <code>caller()</code> invokes the generator function <code>callee()</code> via <code>yield*</code>.</p>
  1279. <pre class="javascript">
  1280. function* callee() {
  1281. console.log('callee: ' + (yield));
  1282. }
  1283. function* caller() {
  1284. while (true) {
  1285. yield* callee();
  1286. }
  1287. }
  1288. </pre>
  1289. <p><code>callee</code> logs values received via <code>next()</code>, which allows us to check whether it receives the value <code>'a'</code> and <code>'b'</code> that we send to <code>caller</code>.</p>
  1290. <pre class="repl">
  1291. &gt; let callerObj = caller();
  1292. &gt; callerObj.next() // start
  1293. { value: undefined, done: false }
  1294. &gt; callerObj.next('a')
  1295. callee: a
  1296. { value: undefined, done: false }
  1297. &gt; callerObj.next('b')
  1298. callee: b
  1299. { value: undefined, done: false }
  1300. </pre>
  1301. <h5 id="example%3A_yield*_forwards_throw%28%29">Example: <code>yield*</code> forwards <code>throw()</code></h5>
  1302. <p>Let’s use the following code to demonstrate how <code>throw()</code> works while <code>yield*</code> is delegating to another generator.</p>
  1303. <pre class="javascript">
  1304. function* callee() {
  1305. try {
  1306. yield 'b'; // (A)
  1307. yield 'c';
  1308. } finally {
  1309. console.log('finally callee');
  1310. }
  1311. }
  1312. function* caller() {
  1313. try {
  1314. yield 'a';
  1315. yield* callee();
  1316. yield 'd';
  1317. } catch (e) {
  1318. console.log('[caller] ' + e);
  1319. }
  1320. }
  1321. </pre>
  1322. <p>We first create a generator object and advance until line (A).</p>
  1323. <pre class="repl">
  1324. &gt; let genObj = caller();
  1325. &gt; genObj.next().value
  1326. 'a'
  1327. &gt; genObj.next().value
  1328. 'b'
  1329. </pre>
  1330. <p>In that line, we throw an exception:</p>
  1331. <pre class="repl">
  1332. &gt; genObj.throw(new Error('Problem!'))
  1333. finally callee
  1334. [caller] Error: Problem!
  1335. { value: undefined, done: true }
  1336. </pre>
  1337. <p><code>callee</code> doesn’t catch the exception, which is why it is propagated into <code>caller</code>, where it is logged before <code>caller</code> finishes.</p>
  1338. <h5 id="example%3A_yield*_forwards_return%28%29">Example: <code>yield*</code> forwards <code>return()</code></h5>
  1339. <p>Let’s use the following code to demonstrate how <code>return()</code> works while <code>yield*</code> is delegating to another generator.</p>
  1340. <pre class="javascript">
  1341. function* callee() {
  1342. try {
  1343. yield 'b';
  1344. yield 'c';
  1345. } finally {
  1346. console.log('finally callee');
  1347. }
  1348. }
  1349. function* caller() {
  1350. try {
  1351. yield 'a';
  1352. yield* callee();
  1353. yield 'd';
  1354. } finally {
  1355. console.log('finally caller');
  1356. }
  1357. }
  1358. </pre>
  1359. <p>Destructuring closes an iterator via <code>return()</code> if one doesn’t iterate until the end:</p>
  1360. <pre class="javascript">
  1361. let [x, y] = caller(); // ['a', 'b']
  1362. // Output:
  1363. // finally callee
  1364. // finally caller
  1365. </pre>
  1366. <p>Interestingly, the <code>return()</code> is sent to <code>caller</code> and forwarded to <code>callee</code> (which it terminates early), but then also terminates <code>caller</code> (which is what someone invoking <code>return()</code> would expect). That is, <code>return</code> is propagated much like an exception.</p>
  1367. <h5 id="how_to_think_about_yield*">How to think about <code>yield*</code></h5>
  1368. <p>There are two ways to think about <code>yield*</code>:</p>
  1369. <ul>
  1370. <li>As a function call from generator to generator.</li>
  1371. <li>In order to understand <code>return()</code>, it helps to ask yourself: what should happen if I copy-pasted the code of the callee function into the code of the caller function.</li>
  1372. </ul>
  1373. <h3 id="generators_as_coroutines_%28cooperative_multitasking%29">Generators as coroutines (cooperative multitasking)</h3>
  1374. <p>We have seen generators being used as either sources or sinks of data. For many applications, it’s good practice to strictly separate these two roles, because it keeps things simpler. This section describes the full generator interface (which combines both roles) and one application where both roles are needed: cooperative multitasking, where tasks must be able to both send and receive information.</p>
  1375. <h4 id="the_full_generator_interface">The full generator interface</h4>
  1376. <p>The full interface of generator objects, <code>Generator</code>, handles both output and input and combines two interfaces that we have seen previously: <code>Iterator</code> for output and <code>Observer</code> for input.</p>
  1377. <pre class="javascript">
  1378. interface Iterator { // data producer
  1379. next() : IteratorResult;
  1380. return?(value? : any) : IteratorResult;
  1381. }
  1382. interface Observer { // data consumer
  1383. next(value? : any) : void;
  1384. return(value? : any) : void;
  1385. throw(error) : void;
  1386. }
  1387. </pre>
  1388. <p>This is the full interface of generator objects (<a href="https://people.mozilla.org/~jorendorff/es6-draft.html#sec-properties-of-generator-prototype">as described by the ECMAScript language specification</a>):</p>
  1389. <pre class="javascript">
  1390. interface Generator {
  1391. next(value? : any) : IteratorResult;
  1392. throw(value? : any) : IteratorResult;
  1393. return(value? : any) : IteratorResult;
  1394. }
  1395. interface IteratorResult {
  1396. value : any;
  1397. done : boolean;
  1398. }
  1399. </pre>
  1400. <h4 id="cooperative_multitasking">Cooperative multitasking</h4>
  1401. <p>Cooperative multitasking is an application of generators where we need them to handle both output and input. Before we get into how that works, let’s first review the current state of parallelism in JavaScript <a class="ptr">[9]</a>.</p>
  1402. <p>JavaScript runs in a single process. There are two ways in which this limitation is being abolished:</p>
  1403. <ul>
  1404. <li><p>Multiprocessing: <em>Web Workers</em> let you run JavaScript in multiple processes. Shared access to data is one of the biggest pitfalls of multiprocessing. Web Workers avoid it by not sharing any data. That is, if you want a Web Worker to have a piece of data, you must send it a copy or transfer your data to it (after which you can’t access it anymore).</p>
  1405. </li>
  1406. <li><p>Cooperative multitasking: There are various patterns and libraries that experiment with cooperative multitasking. Multiple tasks are run, but only one at a time. Each task must explicitly suspend itself, giving it full control over when a task switch happens. In these experiments, data is often shared between tasks. But due to explicit suspension, there are few risks.</p>
  1407. </li>
  1408. </ul>
  1409. <p>Two use cases benefit from cooperative multitasking, because they involve control flows that are mostly sequential, anyway, with occasional pauses:</p>
  1410. <ul>
  1411. <li>Asynchronous computations: A task blocks (pauses) until it receives the result of a long-running computation.</li>
  1412. <li>Streams: A task sequentially processes a stream of data and pauses if there is no data available.</li>
  1413. </ul>
  1414. <p>For binary streams, WHATWG is currently working on a <a href="https://streams.spec.whatwg.org/">standard proposal</a> that is based on callbacks and promises.</p>
  1415. <p>For streams of data, Communicating Sequential Processes (CSP) are an interesting solution. A generator-based CSP library is covered later in this blog post.</p>
  1416. <p>For asynchronous computations, Promises <a class="ptr">[10]</a> have become popular and are included in ECMAScript 6.</p>
  1417. <h5 id="simplifying_asynchronous_code_via_generators">Simplifying asynchronous code via generators</h5>
  1418. <p>Several promise-based libraries simplify asynchronous code via generators. Generators are ideal as clients of promises, because they can be suspended until a result arrives.</p>
  1419. <p>The following example demonstrates what that looks like if one uses <a href="https://github.com/tj/co">the library <em>co</em></a> by T.J. Holowaychuk. We need two libraries (if we run Node.js code via babel-node <a class="ptr">[8]</a>):</p>
  1420. <pre class="javascript">
  1421. require('isomorphic-fetch'); // polyfill
  1422. let co = require('co');
  1423. </pre>
  1424. <p><code>co</code> is the actual library for cooperative multitasking, <code>isomorphic-fetch</code> is a polyfill for the new promise-based <code>fetch</code> API (a replacement of <code>XMLHttpRequest</code>; read “<a href="http://jakearchibald.com/2015/thats-so-fetch/">That's so fetch!</a>” by Jake Archibald for more information). <code>fetch</code> makes it easy to write a function <code>getFile</code> that returns the text of a file at a <code>url</code> via a Promise:</p>
  1425. <pre class="javascript">
  1426. function getFile(url) {
  1427. return fetch(url)
  1428. .then(request =&gt; request.text());
  1429. }
  1430. </pre>
  1431. <p>We now have all the ingredients to use <code>co</code>. The following task reads the texts of two files, parses the JSON inside them and logs the result.</p>
  1432. <pre class="javascript">
  1433. co(function* () {
  1434. try {
  1435. let [croftStr, bondStr] = yield Promise.all([ // (A)
  1436. getFile('http://localhost:8000/croft.json'),
  1437. getFile('http://localhost:8000/bond.json'),
  1438. ]);
  1439. let croftJson = JSON.parse(croftStr);
  1440. let bondJson = JSON.parse(bondStr);
  1441. console.log(croftJson);
  1442. console.log(bondJson);
  1443. } catch (e) {
  1444. console.log('Failure to read: ' + e);
  1445. }
  1446. });
  1447. </pre>
  1448. <p>Note how nicely synchronous this code looks, even though it makes an asynchronous call in line (A). A generator-as-task makes an async call by yielding a promise to the scheduler function <code>co</code>. The yielding pauses the generator. Once the promise returns a result, the scheduler resumes the generator by passing it the result via <code>next()</code>. A simple version of <code>co</code> looks as follows.</p>
  1449. <pre class="javascript">
  1450. function co(genFunc) {
  1451. let genObj = genFunc();
  1452. run();
  1453. function run(promiseResult = undefined) {
  1454. let item = genObj.next(promiseResult);
  1455. if (!item.done) {
  1456. // A promise was yielded
  1457. item.value
  1458. .then(result =&gt; run(result))
  1459. .catch(error =&gt; {
  1460. genObj.throw(error);
  1461. });
  1462. }
  1463. }
  1464. }
  1465. </pre>
  1466. <h4 id="the_limitations_of_cooperative_multitasking_via_generators">The limitations of cooperative multitasking via generators</h4>
  1467. <p><em>Coroutines</em> are cooperatively multitasked tasks that have no limitations: Inside a coroutine, any function can suspend the whole coroutine (the function activation itself, the activation of the function’s caller, the caller’s caller, etc.).</p>
  1468. <p>In contrast, you can only suspend a generator from directly within a generator and only the current function activation is suspended. Due to these limitations, generators are occasionally called <em>shallow coroutines</em> <a class="ptr">[3]</a>.</p>
  1469. <h5 id="the_benefits_of_the_limitations_of_generators">The benefits of the limitations of generators</h5>
  1470. <p>The limitations of generators have two main benefits:</p>
  1471. <ul>
  1472. <li><p>Generators are compatible with <em>event loops</em>, which provide simple cooperative multitasking in browsers. I’ll explain the details momentarily.</p>
  1473. </li>
  1474. <li><p>Generators are relatively easy to implement, because only a single function activation needs to be suspended and because browsers can continue to use event loops.</p>
  1475. </li>
  1476. </ul>
  1477. <p>JavaScript already has a very simple style of cooperative multitasking: the event loop <a class="ptr">[9]</a>, which schedules the execution of tasks in a queue. Each task is started by calling a function and finished once that function is finished (that’s a simplification of how things actually work, but it’ll do for now). Events, <code>setTimeout()</code> and other mechanisms add tasks to the queue.</p>
  1478. <p>This style of multitasking makes one important guarantee: <em>run to completion</em>; every function can rely on not being interrupted by another task until it is finished. Functions become transactions and can perform complete algorithms without anyone seeing the data they operate on in an itermediate state. Concurrent access to shared data makes multitasking complicated and is not allowed by JavaScript’s concurrency model. That’s why run to completion is a good thing.</p>
  1479. <p>Alas, coroutines prevent run to completion, because any function could suspend its caller. For example, the following algorithm consists of multiple steps:</p>
  1480. <pre class="undefined">
  1481. step1(sharedData);
  1482. step2(sharedData);
  1483. lastStep(sharedData);
  1484. </pre>
  1485. <p>If <code>step2</code> was to suspend the algorithm, other tasks could run before the last step of the algorithm is performed. Those tasks could contain other parts of the application which would see <code>sharedData</code> in an unfinished state.</p>
  1486. <p>Generators preserve run to completion, they only suspend themselves and return to their caller. <code>co</code> and similar libraries give you most of the power of coroutines:</p>
  1487. <ul>
  1488. <li>They provide schedulers for tasks defined via generators.</li>
  1489. <li>Tasks start with generators and can thus be fully suspended.</li>
  1490. <li>A recursive (generator) function call is only suspendable if it is done via <code>yield*</code>. That gives callers control over suspension.</li>
  1491. </ul>
  1492. <h3 id="examples_of_generators">Examples of generators</h3>
  1493. <p>This section gives several examples of what generators can be used for.</p>
  1494. <h4 id="implementing_iterables_via_generators_2">Implementing iterables via generators</h4>
  1495. <p>In the blog post “<a href="http://www.2ality.com/2015/02/es6-iteration.html">Iterables and iterators in ECMAScript 6</a>”, I implemented several iterables “by hand”. In this section, I use generators, instead.</p>
  1496. <h5 id="the_iterable_combinator_take%28%29">The iterable combinator <code>take()</code></h5>
  1497. <p><code>take()</code> converts a (potentially infinite) sequence of iterated values into a sequence of length <code>n</code>:</p>
  1498. <pre class="javascript">
  1499. function* take(n, iterable) {
  1500. for (let x of iterable) {
  1501. if (n &lt;= 0) return;
  1502. n--;
  1503. yield x;
  1504. }
  1505. }
  1506. </pre>
  1507. <p>The following is an example of using it:</p>
  1508. <pre class="javascript">
  1509. let arr = ['a', 'b', 'c', 'd'];
  1510. for (let x of take(2, arr)) {
  1511. console.log(x);
  1512. }
  1513. // Output:
  1514. // a
  1515. // b
  1516. </pre>
  1517. <p>An implementation of <code>take()</code> without generators is more complicated:</p>
  1518. <pre class="javascript">
  1519. function take(n, iterable) {
  1520. let iter = iterable[Symbol.iterator]();
  1521. return {
  1522. [Symbol.iterator]() {
  1523. return this;
  1524. },
  1525. next() {
  1526. if (n &gt; 0) {
  1527. n--;
  1528. return iter.next();
  1529. } else {
  1530. iter.return()
  1531. return { done: true };
  1532. }
  1533. },
  1534. return() {
  1535. n = 0;
  1536. iter.return();
  1537. }
  1538. };
  1539. }
  1540. </pre>
  1541. <p>Note that the iterable combinator <code>zip()</code> does not profit much from being implemented via a generator, because multiple iterables are involved (and <code>for-of</code> can’t be used).</p>
  1542. <h5 id="infinite_iterables">Infinite iterables</h5>
  1543. <p><code>naturalNumbers()</code> returns an iterable over all natural numbers:</p>
  1544. <pre class="javascript">
  1545. function* naturalNumbers() {
  1546. for (let n=0;; n++) {
  1547. yield n;
  1548. }
  1549. }
  1550. </pre>
  1551. <p>This function is often used in conjunction with a combinator:</p>
  1552. <pre class="javascript">
  1553. for (let x of take(3, naturalNumbers())) {
  1554. console.log(x);
  1555. }
  1556. // Output
  1557. // 0
  1558. // 1
  1559. // 2
  1560. </pre>
  1561. <p>One last time, I show the non-generator implementation, so you can compare:</p>
  1562. <pre class="javascript">
  1563. function naturalNumbers() {
  1564. let n = 0;
  1565. return {
  1566. [Symbol.iterator]() {
  1567. return this;
  1568. },
  1569. next() {
  1570. return { value: n++ };
  1571. }
  1572. }
  1573. }
  1574. </pre>
  1575. <h5 id="array-inspired_iterable_combinators%3A_map_filter">Array-inspired iterable combinators: <code>map</code>, <code>filter</code></h5>
  1576. <p>Arrays can be transformed via the methods <code>map</code> and <code>filter</code>. Those methods can be generalized to have iterables as input and iterables as output.</p>
  1577. <p>This is the generalized version of <code>map</code>:</p>
  1578. <pre class="javascript">
  1579. function* map(iterable, mapFunc) {
  1580. for (let x of iterable) {
  1581. yield mapFunc(x);
  1582. }
  1583. }
  1584. // Works with infinite iterables!
  1585. let arr = [...take(4, map(naturalNumbers(), x =&gt; x * x))];
  1586. // [0, 1, 4, 9]
  1587. </pre>
  1588. <p>This is the generalized version of <code>filter</code>:</p>
  1589. <pre class="javascript">
  1590. function* filter(iterable, filterFunc) {
  1591. for (let x of iterable) {
  1592. if (filterFunc(x)) {
  1593. yield x;
  1594. }
  1595. }
  1596. }
  1597. // Works with infinite iterables!
  1598. let arr = [...take(4, filter(naturalNumbers(), x =&gt; (x % 2) === 0))];
  1599. // [0, 2, 4, 6]
  1600. </pre>
  1601. <h4 id="generators_for_lazy_evaluation">Generators for lazy evaluation</h4>
  1602. <p>The next two examples show how generators can be used to process a stream of characters.</p>
  1603. <ul>
  1604. <li><p>The input is a stream of characters.</p>
  1605. </li>
  1606. <li><p>Step 1 – tokenize (characters → words): The characters are grouped into <em>words</em>, strings that match the regular expression <code>/^[A-Za-z0-9]$/</code>. Non-word characters are ignored, but they separate words. The input of this step is a stream of characters, the output a stream of words.</p>
  1607. </li>
  1608. <li><p>Step 2 – extract numbers (words → numbers): only keep words that match the regular expression <code>/^[0-9]+$/</code> and convert them to numbers.</p>
  1609. </li>
  1610. <li><p>Step 3 – add numbers (numbers → number): produce a single number by computing the total of all numbers in a stream.</p>
  1611. </li>
  1612. </ul>
  1613. <p>The neat thing is that everything is computed <em>lazily</em> (incrementally and on demand): computation starts as soon as the first character arrives. For example, we don’t have to wait until we have all characters to get the first word.</p>
  1614. <h5 id="lazy_pull_%28generators_as_iterators%29">Lazy pull (generators as iterators)</h5>
  1615. <p><strong>Step 1 – tokenize.</strong> The following trick makes the code a bit simpler: the end-of-sequence iterator result (whose property <code>done</code> is <code>false</code>) is converted into the sentinel value <code>END_OF_SEQUENCE</code>.</p>
  1616. <pre class="javascript">
  1617. function* tokenize(chars) {
  1618. let iterator = chars[Symbol.iterator]();
  1619. let ch;
  1620. do {
  1621. ch = getNextItem(iterator);
  1622. if (isWordChar(ch)) {
  1623. let word = '';
  1624. do {
  1625. word += ch;
  1626. ch = getNextItem(iterator);
  1627. } while (isWordChar(ch));
  1628. yield word;
  1629. }
  1630. // Ignore all other characters
  1631. } while (ch !== END_OF_SEQUENCE);
  1632. }
  1633. const END_OF_SEQUENCE = Symbol();
  1634. function getNextItem(iterator) {
  1635. let item = iterator.next();
  1636. return item.done ? END_OF_SEQUENCE : item.value;
  1637. }
  1638. function isWordChar(ch) {
  1639. return typeof ch === 'string' &amp;&amp; /^[A-Za-z0-9]$/.test(ch);
  1640. }
  1641. </pre>
  1642. <p>Let’s try out tokenization. Note that the spaces and the dot are non-words. They are ignored, but they separate words. We use the fact that strings are iterables over characters (Unicode code points). The final result is an iterable over words, which we turn into an array via the spread operator (<code>...</code>).</p>
  1643. <pre class="repl">
  1644. &gt; [...tokenize('2 apples and 5 oranges.')]
  1645. [ '2', 'apples', 'and', '5', 'oranges' ]
  1646. </pre>
  1647. <p><strong>Step 2 – extract numbers:</strong> This step is relatively simple, we only <code>yield</code> words that contain nothing but digits, after converting them to numbers via <code>Number()</code>.</p>
  1648. <pre class="javascript">
  1649. function* extractNumbers(words) {
  1650. for (let word of words) {
  1651. if (/^[0-9]+$/.test(word)) {
  1652. yield Number(word);
  1653. }
  1654. }
  1655. }
  1656. </pre>
  1657. <p>The following example shows the transformation steps: characters → words → numbers.</p>
  1658. <pre class="repl">
  1659. &gt; [...extractNumbers(tokenize('2 apples and 5 oranges.'))]
  1660. [ 2, 5 ]
  1661. </pre>
  1662. <p><strong>Step 3 – add numbers:</strong> This last step is performed by a normal function that pulls the results and reports their total.</p>
  1663. <pre class="javascript">
  1664. function summarize(numbers) {
  1665. let result = 0;
  1666. for (let n of numbers) {
  1667. result += n;
  1668. }
  1669. return result;
  1670. }
  1671. </pre>
  1672. <p>The final result shows us that there are 7 things in the input sentence.</p>
  1673. <pre class="repl">
  1674. &gt; summarize(extractNumbers(tokenize('2 apples and 5 oranges.')))
  1675. 7
  1676. </pre>
  1677. <h5 id="lazy_push_%28generators_as_observables%29">Lazy push (generators as observables)</h5>
  1678. <p>Not much work is needed to convert the previous pull-based algorithm into a push-based one. The steps are the same. But instead of finishing via pulling, we start via pushing (in both cases, non-generator functions are used).</p>
  1679. <p>As previously explained, if generators receive input via <code>yield</code>, the first invocation of <code>next()</code> on the generator object doesn’t do anything. That’s why we again use the following helper function:</p>
  1680. <pre class="javascript">
  1681. /**
  1682. * Returns a function that, when called,
  1683. * returns a generator object that is immediately
  1684. * ready for input via `next()`
  1685. */
  1686. function coroutine(generatorFunction) {
  1687. return function (...args) {
  1688. let generatorObject = generatorFunction(...args);
  1689. generatorObject.next();
  1690. return generatorObject;
  1691. };
  1692. }
  1693. </pre>
  1694. <p>The following function <code>send()</code> wraps the chain of generators. Its parameter <code>receiver</code> holds the generator object of the first generator in the chain. <code>send()</code> pushes the contents of <code>iterable</code> to the <code>receiver</code>, via <code>next()</code>.</p>
  1695. <pre class="javascript">
  1696. function send(iterable, receiver) {
  1697. for (let x of iterable) {
  1698. receiver.next(x);
  1699. }
  1700. receiver.return(); // signal end of stream
  1701. }
  1702. </pre>
  1703. <p>When a generator processes a stream, it needs to be aware of the end of the stream, so that it can clean up properly. For pull, we did this via a special end-of-stream sentinel. For push, the end-of-stream is signaled via <code>return()</code>.</p>
  1704. <p>Let’s test <code>send()</code> via a generator that simply outputs everything it receives:</p>
  1705. <pre class="javascript">
  1706. const logItems = coroutine(function* () {
  1707. try {
  1708. while (true) {
  1709. let item = yield;
  1710. console.log(item);
  1711. }
  1712. } finally {
  1713. console.log('DONE');
  1714. }
  1715. });
  1716. </pre>
  1717. <p>Let’s send <code>logItems()</code> three “characters” via a string (which is an iterable over Unicode code points).</p>
  1718. <pre class="repl">
  1719. &gt; send('abc', logItems());
  1720. a
  1721. b
  1722. c
  1723. DONE
  1724. </pre>
  1725. <p><strong>Step 1 – tokenize.</strong> Note how this generator reacts to the end of the stream (as signaled via <code>return()</code>) in two <code>finally</code> clauses. We depend on <code>return()</code> being sent to either one of the two <code>yield</code>s. Otherwise, the generator would never terminate, because the infinite loop starting in line (A) would never terminate.</p>
  1726. <pre class="javascript">
  1727. const tokenize = coroutine(function* (receiver) {
  1728. try {
  1729. while (true) { // (A)
  1730. let ch = yield;
  1731. if (isWordChar(ch)) {
  1732. // A word has started
  1733. let word = '';
  1734. try {
  1735. do {
  1736. word += ch;
  1737. ch = yield;
  1738. } while (isWordChar(ch));
  1739. } finally {
  1740. // The word is finished.
  1741. // We get here if
  1742. // (a) the loop terminates normally
  1743. // (b) the loop is terminated via `return()`
  1744. receiver.next(word);
  1745. }
  1746. }
  1747. // Ignore all other characters
  1748. }
  1749. } finally {
  1750. // We only get here if the infinite loop is terminated
  1751. // via `return()`. Forward `return()` to `receiver` so
  1752. // that it is also aware of the end of stream.
  1753. receiver.return();
  1754. }
  1755. });
  1756. function isWordChar(ch) {
  1757. return /^[A-Za-z0-9]$/.test(ch);
  1758. }
  1759. </pre>
  1760. <p><code>tokenize()</code> demonstrates that generators work well as implementations of linear state machines. In this case, the machine has two states: “inside a word” and “not inside a word”.</p>
  1761. <p>Let’s tokenize a string:</p>
  1762. <pre class="repl">
  1763. &gt; send('2 apples and 5 oranges.', tokenize(logItems()));
  1764. 2
  1765. apples
  1766. and
  1767. 5
  1768. oranges
  1769. </pre>
  1770. <p><strong>Step 2 – extract numbers:</strong> This step is straightforward.</p>
  1771. <pre class="javascript">
  1772. const extractNumbers = coroutine(function* (receiver) {
  1773. try {
  1774. while (true) {
  1775. let word = yield;
  1776. if (/^[0-9]+$/.test(word)) {
  1777. receiver.next(Number(word));
  1778. }
  1779. }
  1780. } finally {
  1781. // Only reached via `return()`, forward.
  1782. receiver.return();
  1783. }
  1784. });
  1785. </pre>
  1786. <p>Let’s log the numbers appearing in a string:</p>
  1787. <pre class="repl">
  1788. &gt; send('2 apples and 5 oranges.', tokenize(extractNumbers(logItems())));
  1789. 2
  1790. 5
  1791. DONE
  1792. </pre>
  1793. <p><strong>Step 3 – add numbers:</strong> This time, we react to the end of the stream by pushing a single value and then closing the receiver.</p>
  1794. <pre class="javascript">
  1795. const addNumbers = coroutine(function* (receiver) {
  1796. let result = 0;
  1797. try {
  1798. while (true) {
  1799. result += yield;
  1800. }
  1801. } finally {
  1802. // We received an end-of-stream
  1803. receiver.next(result);
  1804. receiver.return(); // signal end of stream
  1805. }
  1806. });
  1807. </pre>
  1808. <p>Let’s sum up the numbers appearing inside a string:</p>
  1809. <pre class="repl">
  1810. &gt; send('2 apples and 5 oranges.', tokenize(extractNumbers(addNumbers(logItems()))));
  1811. 7
  1812. DONE
  1813. </pre>
  1814. <p>As mentioned before, one benefit of a push-based approach is that it allows you to process data that you receive asynchronously.</p>
  1815. <h4 id="cooperative_multi-tasking_via_generators">Cooperative multi-tasking via generators</h4>
  1816. <h5 id="pausing_long-running_tasks">Pausing long-running tasks</h5>
  1817. <p>In this example, we create a counter that is displayed on a web page. We improve an initial version until we have a cooperatively multitasked version that doesn’t block the main thread and the user interface.</p>
  1818. <p>This is the part of the web page in which the counter should be displayed:</p>
  1819. <pre class="html">
  1820. &lt;body&gt;
  1821. Counter: &lt;span id="counter"&gt;&lt;/span&gt;
  1822. &lt;/body&gt;
  1823. </pre>
  1824. <p>This function displays a counter that counts up forever (well, until the number overflows):</p>
  1825. <pre class="javascript">
  1826. function countUp(start = 0) {
  1827. const counterSpan = document.querySelector('#counter');
  1828. while (true) {
  1829. counterSpan.textContent = String(start);
  1830. start++;
  1831. }
  1832. }
  1833. </pre>
  1834. <p>If you ran this function, it would completely block the user interface thread in which it runs and its tab would become unresponsive.</p>
  1835. <p>Let’s implement the same functionality via a generator that periodically pauses via <code>yield</code> (a scheduling function for running this generator is shown at the end):</p>
  1836. <pre class="javascript">
  1837. function* countUp(start = 0) {
  1838. const counterSpan = document.querySelector('#counter');
  1839. while (true) {
  1840. counterSpan.textContent = String(start);
  1841. start++;
  1842. yield; // pause
  1843. }
  1844. }
  1845. </pre>
  1846. <p>Let’s add one small improvement. We move the update of the user interface to another generator, <code>displayCounter</code>, which we call via <code>yield*</code>. As it is a generator, it can also take care of pausing.</p>
  1847. <pre class="javascript">
  1848. function* countUp(start = 0) {
  1849. while (true) {
  1850. start++;
  1851. yield* displayCounter(start);
  1852. }
  1853. }
  1854. function* displayCounter(counter) {
  1855. const counterSpan = document.querySelector('#counter');
  1856. counterSpan.textContent = String(counter);
  1857. yield; // pause
  1858. }
  1859. </pre>
  1860. <p>Lastly, this is a scheduling function that we can use to run <code>countUp()</code>. Each execution step of the generator is handled by a separate task, which is created via <code>setTimeout()</code>. That means that the user interface can schedule other tasks in between and will remain responsive.</p>
  1861. <pre class="javascript">
  1862. function run(generatorObject) {
  1863. if (!generatorObject.next().done) {
  1864. // Add a new task to the event queue
  1865. setTimeout(function () {
  1866. run(generatorObject);
  1867. }, 1000);
  1868. }
  1869. }
  1870. </pre>
  1871. <p>With the help of <code>run</code>, we get a (nearly) infinite count-up that doesn’t block the user interface:</p>
  1872. <pre class="javascript">
  1873. run(countUp());
  1874. </pre>
  1875. <h5 id="cooperative_multitasking_with_generators_and_node.js-style_callbacks">Cooperative multitasking with generators and Node.js-style callbacks</h5>
  1876. <p>If you call a generator function (or method), it does not have access to its generator object; its <code>this</code> is the <code>this</code> it would have if it were a non-generator function. A work-around is to pass the generator object into the generator function via <code>yield</code>.</p>
  1877. <p>The following Node.js script uses this technique, but wraps the generator object in a callback (<code>next</code>, line (A)). It must be run via <code>babel-node</code> <a class="ptr">[8]</a>.</p>
  1878. <pre class="javascript">
  1879. import {readFile} from 'fs';
  1880. let fileNames = process.argv.slice(2);
  1881. console.log(fileNames, readFile);
  1882. run(function* () {
  1883. let next = yield; // (A)
  1884. for (let f of fileNames) {
  1885. let contents = yield readFile(f, { encoding: 'utf8' }, next);
  1886. console.log('-------------', f);
  1887. console.log(contents);
  1888. }
  1889. });
  1890. </pre>
  1891. <p>In line (A), we get a callback that we can use with functions that follow Node.js callback conventions. The callback uses the generator object to wake up the generator, as you can see in the implementation of <code>run()</code>:</p>
  1892. <pre class="javascript">
  1893. function run(generatorFunction) {
  1894. let generatorObject = generatorFunction();
  1895. // Step 1: Proceed to first `yield`
  1896. generatorObject.next();
  1897. // Step 2: Pass in a function that the generator can use as a callback
  1898. let nextFunction = createNextFunction(generatorObject);
  1899. generatorObject.next(nextFunction);
  1900. // Subsequent invocations of `next()` are triggered by `nextFunction`
  1901. }
  1902. function createNextFunction(generatorObject) {
  1903. return function(error, result) {
  1904. if (error) {
  1905. generatorObject.throw(error);
  1906. } else {
  1907. generatorObject.next(result);
  1908. }
  1909. };
  1910. }
  1911. </pre>
  1912. <h5 id="communicating_sequential_processes_%28csp%29">Communicating Sequential Processes (CSP)</h5>
  1913. <p>The library <a href="https://github.com/ubolonton/js-csp"><code>js-csp</code></a> brings Communicating Sequential Processes (CSP) to JavaScript, a style of cooperative multitasking that is similar to ClojureScript’s core.async and Go’s <em>goroutines</em>. <code>js-csp</code> has two abstractions:</p>
  1914. <ul>
  1915. <li>Processes: are cooperatively multitasked tasks and implemented by handing a generator function to the scheduling function <code>go()</code>.</li>
  1916. <li>Channels: are queues for communication between processes. Channels are created by calling <code>chan()</code>.</li>
  1917. </ul>
  1918. <p>As an example, let’s use CSP to handle DOM events, in a manner reminiscent of Functional Reactive Programming. The following code uses the function <code>listen()</code> (which is shown later) to create a channel that outputs <code>mousemove</code> events. It then continuously retrieves the output via <code>take</code>, inside an infinite loop. Thanks to <code>yield</code>, the process blocks until the channel has output.</p>
  1919. <pre class="javascript">
  1920. import csp from 'js-csp';
  1921. csp.go(function* () {
  1922. let element = document.querySelector('#uiElement1');
  1923. let channel = listen(element, 'mousemove');
  1924. while (true) {
  1925. let event = yield csp.take(channel);
  1926. let x = event.layerX || event.clientX;
  1927. let y = event.layerY || event.clientY;
  1928. element.textContent = `${x}, ${y}`;
  1929. }
  1930. });
  1931. </pre>
  1932. <p><code>listen()</code> is implemented as follows.</p>
  1933. <pre class="javascript">
  1934. function listen(element, type) {
  1935. let channel = csp.chan();
  1936. element.addEventListener(type,
  1937. event =&gt; {
  1938. csp.putAsync(channel, event);
  1939. });
  1940. return channel;
  1941. }
  1942. </pre>
  1943. <p>This example is taken from the blog post “<a href="http://jlongster.com/Taming-the-Asynchronous-Beast-with-CSP-in-JavaScript">Taming the Asynchronous Beast with CSP Channels in JavaScript</a>” by James Long. Consult this blog post for more information on CSP.</p>
  1944. <h3 id="inheritance">Inheritance</h3>
  1945. <p>This is a diagram of how various objects are connected in ECMAScript 6 (it is based on <a href="https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorfunction-objects">Allen Wirf-Brock’s diagram</a> in the ECMAScript specification):</p>
  1946. <div class="separator">
  1947. <a href="http://3.bp.blogspot.com/-kGgpHT0wjqU/VRkKCblRpRI/AAAAAAAAA70/BuiA8d-aZto/s1600/generator_inheritance.jpg" imageanchor="1"><img border="0" src="http://3.bp.blogspot.com/-kGgpHT0wjqU/VRkKCblRpRI/AAAAAAAAA70/BuiA8d-aZto/s1600/generator_inheritance.jpg"/></a></div>
  1948. <p>Legend:</p>
  1949. <ul>
  1950. <li>The white (hollow) arrows express the has-prototype relationship (inheritance) between objects. In other words: a white arrow from <code>x</code> to <code>y</code> means that <code>Object.getPrototypeOf(x) === y</code>.</li>
  1951. <li>Parentheses indicate that an object exists, but is not accessible via a global variable.</li>
  1952. <li>An <code>instanceof</code> arrow from <code>x</code> to <code>y</code> means that <code>x instanceof y</code>.<ul>
  1953. <li>Remember that <code>o instanceof C</code> is equivalent to <code>C.prototype.isPrototypeOf(o)</code>.</li>
  1954. </ul>
  1955. </li>
  1956. <li>A <code>prototype</code> arrow from <code>x</code> to <code>y</code> means that <code>x.prototype === y</code>.</li>
  1957. </ul>
  1958. <p>The diagram reveals two interesting facts:</p>
  1959. <p>First, a generator function <code>g</code> works very much like a constructor (you can even invoke it via <code>new</code>): The generator objects it creates are instances of it, methods added to <code>g.prototype</code> become prototype methods, etc.:</p>
  1960. <pre class="javascript">
  1961. &gt; function* g() {}
  1962. &gt; g.prototype.hello = function () { return 'hi!'};
  1963. &gt; let obj = g();
  1964. &gt; obj instanceof g
  1965. true
  1966. &gt; obj.hello()
  1967. 'hi!'
  1968. </pre>
  1969. <p>Second, if you want to make methods available for all generator objects, it’s best to add them to <code>(Generator.object)</code>. One way of accessing that object is as follows:</p>
  1970. <pre class="javascript">
  1971. &gt; let Generator_prototype = Object.getPrototypeOf(function* () {}).prototype;
  1972. &gt; Generator_prototype.hello = function () { return 'hi!'};
  1973. &gt; let generatorObject = (function* () {})();
  1974. &gt; generatorObject.hello()
  1975. 'hi!'
  1976. </pre>
  1977. <h4 id="iteratorprototype"><code>IteratorPrototype</code></h4>
  1978. <p>There is no <code>(Iterator)</code> in the diagram, because no such object exists. But, given how <code>instanceof</code> works and because <code>(IteratorPrototype)</code> is a prototype of <code>g1()</code>, you could still say that <code>g1()</code> is an instance of <code>Iterator</code>.</p>
  1979. <p>All iterators in ES6 have <code>(IteratorPrototype)</code> in their prototype chain. That object is iterable, because it has the following method. Therefore, all ES6 iterators are iterable (as a consequence, you can apply <code>for-of</code> etc. to them).</p>
  1980. <pre class="javascript">
  1981. [Symbol.iterator]() {
  1982. return this;
  1983. }
  1984. </pre>
  1985. <p>The specification recommends to use the following code to access <code>(IteratorPrototype)</code>:</p>
  1986. <pre class="javascript">
  1987. const proto = Object.getPrototypeOf.bind(Object);
  1988. let IteratorPrototype = proto(proto([][Symbol.iterator]()));
  1989. </pre>
  1990. <p>You could also use:</p>
  1991. <pre class="javascript">
  1992. let IteratorPrototype = proto(proto(function* () {}.prototype));
  1993. </pre>
  1994. <p>Quoting the ECMAScript 6 specification:</p>
  1995. <blockquote>
  1996. <p>ECMAScript code may also define objects that inherit from <code>IteratorPrototype</code>. The <code>IteratorPrototype</code> object provides a place where additional methods that are applicable to all iterator objects may be added.</p>
  1997. </blockquote>
  1998. <p><code>IteratorPrototype</code> will probably become directly accessible in an upcoming version of ECMAScript and contain tool methods such as <code>map()</code> and <code>filter()</code> (<a href="https://github.com/rwaldron/tc39-notes/blob/master/es6/2014-07/jul-30.md#47-revisit-comprehension-decision-from-last-meeting">source</a>).</p>
  1999. <h4 id="the_value_of_this_in_generators">The value of <code>this</code> in generators</h4>
  2000. <p>A generator function combines two concerns:</p>
  2001. <ol>
  2002. <li>It is a function that sets up and returns a generator object.</li>
  2003. <li>It contains the code that the generator object steps through.</li>
  2004. </ol>
  2005. <p>That’s why it’s not immediately obvious what the value of <code>this</code> should be inside a generator.</p>
  2006. <p>In function calls and method calls, <code>this</code> is what it would be if <code>gen()</code> wasn’t a generator function, but a normal function:</p>
  2007. <pre class="javascript">
  2008. function* gen() {
  2009. 'use strict'; // just in case
  2010. yield this;
  2011. }
  2012. // Retrieve the yielded value via destructuring
  2013. let [functionThis] = gen();
  2014. console.log(functionThis); // undefined
  2015. let obj = { method: gen };
  2016. let [methodThis] = obj.method();
  2017. console.log(methodThis === obj); // true
  2018. </pre>
  2019. <p>If you access <code>this</code> in a generator that was invoked via <code>new</code>, you get a <code>ReferenceError</code> (<a href="https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generator-function-definitions-runtime-semantics-evaluatebody">source: ES6 spec</a>):</p>
  2020. <pre class="javascript">
  2021. function* gen() {
  2022. console.log(this); // ReferenceError
  2023. }
  2024. new gen();
  2025. </pre>
  2026. <p>We have previously seen a simple work-around: wrap the generator in a normal function that hands the generator its generator object via <code>yield</code>.</p>
  2027. <h3 id="style_consideration%3A_whitespace_before_and_after_the_asterisk">Style consideration: whitespace before and after the asterisk</h3>
  2028. <p>Reasonable – and legal – variations of formatting the asterisk are:</p>
  2029. <ul>
  2030. <li><p>A space before and after it:<br/><code>function * foo(x, y) { ... }</code></p>
  2031. </li>
  2032. <li><p>A space before it:<br/><code>function *foo(x, y) { ... }</code></p>
  2033. </li>
  2034. <li><p>A space after it:<br/><code>function* foo(x, y) { ... }</code></p>
  2035. </li>
  2036. <li><p>No whitespace before and after it:<br/><code>function*foo(x, y) { ... }</code></p>
  2037. </li>
  2038. </ul>
  2039. <p>Let’s figure out which of these variations make sense for which constructs and why.</p>
  2040. <h4 id="generator_function_declarations_and_expressions">Generator function declarations and expressions</h4>
  2041. <p>Here, the star is only used because <code>generator</code> (or something similar) isn’t available as a keyword. If it were, then a generator function declaration would look like this:</p>
  2042. <pre class="javascript">
  2043. generator foo(x, y) {
  2044. ...
  2045. }
  2046. </pre>
  2047. <p>Instead of <code>generator</code>, ECMAScript 6 marks the <code>function</code> keyword with an asterisk. Thus, <code>function*</code> can be seen as a synonym for <code>generator</code>, which suggests writing generator function declarations as follows.</p>
  2048. <pre class="javascript">
  2049. function* foo(x, y) {
  2050. ...
  2051. }
  2052. </pre>
  2053. <p>Anonymous generator functions would be formatted like this:</p>
  2054. <pre class="javascript">
  2055. const foo = function* (x, y) {
  2056. ...
  2057. }
  2058. </pre>
  2059. <h4 id="concise_generator_method_definitions">Concise generator method definitions</h4>
  2060. <p>When writing a concise generator method definitions, I recommend to format the asterisk as follows.</p>
  2061. <pre class="javascript">
  2062. let obj = {
  2063. * generatorMethod(x, y) {
  2064. ...
  2065. }
  2066. };
  2067. </pre>
  2068. <p>There are three arguments in favor of writing a space after the asterisk.</p>
  2069. <p>First, the asterisk shouldn’t be part of the method name. On one hand, it isn’t part of the name of a generator function. On the other hand, the asterisk is only mentioned when defining a generator, not when using it.</p>
  2070. <p>Second, a concise generator method definition is an abbreviation for the following syntax. (To make my point, I’m redundantly giving the function expression a name, too.)</p>
  2071. <pre class="javascript">
  2072. let obj = {
  2073. generatorMethod: function* generatorMethod(x, y) {
  2074. ...
  2075. }
  2076. };
  2077. </pre>
  2078. <p>If concise method definitions are about omitting the <code>function</code> keyword then the asterisk should probably be followed by a space.</p>
  2079. <p>Third, generator method definitions are syntactically similar to getters and setters (which are already available in ECMAScript 5):</p>
  2080. <pre class="javascript">
  2081. let obj = {
  2082. get foo() {
  2083. ...
  2084. }
  2085. set foo(value) {
  2086. ...
  2087. }
  2088. }
  2089. </pre>
  2090. <p>The keywords <code>get</code> and <code>set</code> can be seen as modifiers of a normal concise method definition. Arguably, an asterisk is also such a modifier.</p>
  2091. <h4 id="recursive_yield">Recursive <code>yield</code></h4>
  2092. <p>The following is an example of a generator function yielding its own yielded values recursively:</p>
  2093. <pre class="javascript">
  2094. function* foo(x) {
  2095. ...
  2096. yield* foo(x - 1);
  2097. ...
  2098. }
  2099. </pre>
  2100. <p>The asterisk marks a different kind of <code>yield</code> operator, which is why the above way of writing it makes sense.</p>
  2101. <h4 id="documenting_generator_functions_and_methods">Documenting generator functions and methods</h4>
  2102. <p>Kyle Simpson (@getify) proposed something interesting: Given that we often append parentheses when we write about functions and methods such as <code>Math.max()</code>, wouldn’t it make sense to prepend an asterisk when writing about generator functions and methods? For example: should we write <code>*foo()</code> to refer to the generator function in the previous subsection? Let me argue against that.</p>
  2103. <p>When it comes to writing a function that returns an iterable, a generator is only one of the several options. I think it is better to not give away this implementation detail via marked function names.</p>
  2104. <p>Furthermore, you don’t use the asterisk when calling a generator function, but you do use parentheses.</p>
  2105. <p>Lastly, the asterisk doesn’t provide useful information – <code>yield*</code> can also be used with functions that return an iterable. But it may make sense to mark the names of functions and methods that return iterables (including generators). For example, via the suffix <code>Iter</code>.</p>
  2106. <h3 id="conclusion">Conclusion</h3>
  2107. <p>I hope that this blog post convinced you that generators are a useful and versatile tool.</p>
  2108. <p>I like that generators let you implement cooperatively multitasked tasks that block while making asynchronous function calls. In my opinion that’s the right mental model for async calls. I hope that JavaScript goes further in this direction in the future. If one generator async-calls another generator, the indirection via promises is not needed. It could be avoided by <a href="https://github.com/lukehoban/ecmascript-asyncawait">the generator-based async functions</a> that have been proposed for ECMAScript 2016.</p>
  2109. <h3 id="references">References</h3>
  2110. <p>Acknowledgement: items 1–3 are sources of this blog post.</p>
  2111. <ol class="reflist">
  2112. <li>“<a href="https://github.com/jhusain/asyncgenerator">Async Generator Proposal</a>” by Jafar Husain</li>
  2113. <li>“<a href="http://www.dabeaz.com/coroutines/">A Curious Course on Coroutines and Concurrency</a>” by David Beazley</li>
  2114. <li>“<a href="http://calculist.org/blog/2011/12/14/why-coroutines-wont-work-on-the-web/">Why coroutines won't work on the web</a>” by David Herman</li>
  2115. <li>“<a href="http://exploringjs.com/">Exploring ES6: Upgrade to the next version of JavaScript</a>”, book by Axel</li>
  2116. <li><a href="http://www.2ality.com/2015/02/es6-iteration.html">Iterables and iterators in ECMAScript 6</a></li>
  2117. <li><a href="http://www.2ality.com/2015/01/es6-destructuring.html">Destructuring and parameter handling in ECMAScript 6</a></li>
  2118. <li>”<a href="the_spread_operator_%28...%29">The spread operator (<code>...</code>)</a>”, a section in the blog post “Destructuring and parameter handling in ECMAScript 6”.</li>
  2119. <li><a href="http://www.2ality.com/2015/03/babel-on-node.html">Using the ES6 transpiler Babel on Node.js</a></li>
  2120. <li><a href="http://www.2ality.com/2014/09/es6-promises-foundations.html">ECMAScript 6 promises (1/2): foundations</a> [explains the event loop and more]</li>
  2121. <li><a href="http://www.2ality.com/2014/10/es6-promises-api.html">ECMAScript 6 promises (2/2): the API</a></li>
  2122. <li><a href="http://www.2ality.com/2015/02/es6-classes-final.html">Classes in ECMAScript 6 (final semantics)</a></li>
  2123. </ol>
  2124. </article>
  2125. </section>
  2126. <nav id="jumpto">
  2127. <p>
  2128. <a href="/david/blog/">Accueil du blog</a> |
  2129. <a href="http://www.2ality.com/2015/03/es6-generators.html">Source originale</a> |
  2130. <a href="/david/stream/2019/">Accueil du flux</a>
  2131. </p>
  2132. </nav>
  2133. <footer>
  2134. <div>
  2135. <img src="/static/david/david-larlet-avatar.jpg" loading="lazy" class="avatar" width="200" height="200">
  2136. <p>
  2137. Bonjour/Hi!
  2138. 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>
  2139. 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>).
  2140. </p>
  2141. <p>
  2142. 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>.
  2143. </p>
  2144. <p>
  2145. Voici quelques articles choisis :
  2146. <a href="/david/blog/2019/faire-equipe/" title="Accéder à l’article complet">Faire équipe</a>,
  2147. <a href="/david/blog/2018/bivouac-automnal/" title="Accéder à l’article complet">Bivouac automnal</a>,
  2148. <a href="/david/blog/2018/commodite-effondrement/" title="Accéder à l’article complet">Commodité et effondrement</a>,
  2149. <a href="/david/blog/2017/donnees-communs/" title="Accéder à l’article complet">Des données aux communs</a>,
  2150. <a href="/david/blog/2016/accompagner-enfant/" title="Accéder à l’article complet">Accompagner un enfant</a>,
  2151. <a href="/david/blog/2016/senior-developer/" title="Accéder à l’article complet">Senior developer</a>,
  2152. <a href="/david/blog/2016/illusion-sociale/" title="Accéder à l’article complet">L’illusion sociale</a>,
  2153. <a href="/david/blog/2016/instantane-scopyleft/" title="Accéder à l’article complet">Instantané Scopyleft</a>,
  2154. <a href="/david/blog/2016/enseigner-web/" title="Accéder à l’article complet">Enseigner le Web</a>,
  2155. <a href="/david/blog/2016/simplicite-defaut/" title="Accéder à l’article complet">Simplicité par défaut</a>,
  2156. <a href="/david/blog/2016/minimalisme-esthetique/" title="Accéder à l’article complet">Minimalisme et esthétique</a>,
  2157. <a href="/david/blog/2014/un-web-omni-present/" title="Accéder à l’article complet">Un web omni-présent</a>,
  2158. <a href="/david/blog/2014/manifeste-developpeur/" title="Accéder à l’article complet">Manifeste de développeur</a>,
  2159. <a href="/david/blog/2013/confort-convivialite/" title="Accéder à l’article complet">Confort et convivialité</a>,
  2160. <a href="/david/blog/2013/testament-numerique/" title="Accéder à l’article complet">Testament numérique</a>,
  2161. et <a href="/david/blog/" title="Accéder aux archives">bien d’autres…</a>
  2162. </p>
  2163. <p>
  2164. 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>.
  2165. </p>
  2166. <p>
  2167. Je ne traque pas ta navigation mais mon
  2168. <abbr title="Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33.184162340">hébergeur</abbr>
  2169. conserve des logs d’accès.
  2170. </p>
  2171. </div>
  2172. </footer>
  2173. <script type="text/javascript">
  2174. ;(_ => {
  2175. const jumper = document.getElementById('jumper')
  2176. jumper.addEventListener('click', e => {
  2177. e.preventDefault()
  2178. const anchor = e.target.getAttribute('href')
  2179. const targetEl = document.getElementById(anchor.substring(1))
  2180. targetEl.scrollIntoView({behavior: 'smooth'})
  2181. })
  2182. })()
  2183. </script>