<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hugh Rawlinson</title>
    <description>The latest articles on DEV Community by Hugh Rawlinson (@hughrawlinson).</description>
    <link>https://dev.to/hughrawlinson</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F357963%2Fc53cc717-639b-434a-ab09-7288aa40c9f6.jpeg</url>
      <title>DEV Community: Hugh Rawlinson</title>
      <link>https://dev.to/hughrawlinson</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hughrawlinson"/>
    <language>en</language>
    <item>
      <title>How to Ship man pages with your Node Programs</title>
      <dc:creator>Hugh Rawlinson</dc:creator>
      <pubDate>Sat, 14 Aug 2021 12:07:38 +0000</pubDate>
      <link>https://dev.to/hughrawlinson/how-to-ship-man-pages-with-your-node-programs-3j4g</link>
      <guid>https://dev.to/hughrawlinson/how-to-ship-man-pages-with-your-node-programs-3j4g</guid>
      <description>&lt;p&gt;&lt;em&gt;This post was first published on &lt;a href="https://www.hughrawlinson.me/posts/2021/07/09/how-to-ship-man-pages-with-your-node-programs"&gt;my website&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The Javascript ecosystem uses the npm package registry to distribute libraries - and also binaries. If you've ever done &lt;code&gt;npm install -g yarn&lt;/code&gt;, you've used this feature. The yarn package exposes a binary (a node js script) that executes on your system as a program, often invoked in the command line. Your npm installation then installs that package in a central global directory, and links each binary exposed by the package to a central directory that's in your shell. Then you can invoke the binary on your command line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hugh@hugh-XPS-13-9343 ~&amp;gt; yarn --version
1.22.10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty neat right?&lt;/p&gt;

&lt;p&gt;In unix systems, the &lt;a href="https://en.wikipedia.org/wiki/Man_page"&gt;man utility&lt;/a&gt; is a common way to look up how to use a given command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hugh@hugh-XPS-13-9343 ~&amp;gt; man yarn
No manual entry for yarn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But not all packages provide man pages. This isn't terrible - in the case of yarn, there is a whole 'help' subcommand to look up information about how to use yarn's cli - plus &lt;a href="https://yarnpkg.com/en/docs/cli"&gt;a documentation website&lt;/a&gt;. But, if like me, you think the developer experience is improved by meeting your developer where &lt;em&gt;they&lt;/em&gt; expect you to be, you might like to distribute at least a nearly empty man page pointing devs in the right direction. If there's some chance of some portion of your users reaching for &lt;code&gt;man {your binary}&lt;/code&gt;, I would suggest that it's worth weighing the effort of providing at least &lt;em&gt;a&lt;/em&gt; man page with the benefit that those users would get from having docs (or a pointer to docs) where they expect.&lt;/p&gt;

&lt;p&gt;Isn't that effort huge? Aren't man pages in a weird format for fancy native developers? How would I even install them from an npm package? In this post I'll attempt to convince you that making a basic man page is not a huge lift, and might be worth the your work for the developer experience benefit.&lt;/p&gt;

&lt;p&gt;Let's start off with a creating a basic empty package for demonstration purposes, and installing some dependencies that will help us create our man pages.&lt;/p&gt;

&lt;p&gt;I've created &lt;a href="https://github.com/hughrawlinson/npm-man-page-example"&gt;an example repo&lt;/a&gt; for you to refer to if you like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir my-package &amp;amp;&amp;amp; cd my-package
$ npm init --yes
$ npm install --save marked&amp;lt;1 marked-man
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we find ourselves with an empty package with two dependencies - marked, and marked-man. marked is a peer dependency of marked-man, the package that will take our markdown document and convert it to roff, the format used by man-pages. If you'd like to cut down dependencies and write in roff directly, go ahead! But I figure most javascript developers will be more familiar with Markdown.&lt;/p&gt;

&lt;p&gt;Right now, there's a &lt;a href="https://github.com/kapouer/marked-man/issues/28"&gt;bug in marked-man&lt;/a&gt; causing it not to support versions of it's peerDependency marked greater than 1.0.0. At the moment, I suggest installing a version of marked below 1, and keeping an eye on the bug.&lt;/p&gt;

&lt;p&gt;Next, let's write a simple document. Store the following in &lt;code&gt;README.md&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# my-package(1) -- demo package

## Synopsis

my-package is a demonstration package that does nothing

## See also

example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's a git more going on here than simple markdown syntax. Let's walk through it. On the first line, we have a heading containing our package name, followed immediately by a number in brackets. This number refers to the man 'section number' for your page. In our case, we're using section number 1 to show that our documentation concerns "Executable programs or shell commands" - but you can specify library calls, special files, games, etc. Check out the table in the &lt;code&gt;man man&lt;/code&gt; document. The package name(section number) is followed by a &lt;code&gt;--&lt;/code&gt; spacer and a short description of your command.&lt;/p&gt;

&lt;p&gt;Later on in the document, we can see sections headed by h2s. These are man "section names". You can have custom sections, but conventional section names include NAME (which is automatically generated for you), SYNOPSIS, CONFIGURATION, DESCRIPTION, FILES, NOTES, BUGS, AUTHORS, SEE ALSO, and more that are shown in the &lt;code&gt;man man&lt;/code&gt; document.&lt;/p&gt;

&lt;p&gt;Time to use marked-man to generate the man file. Man files are stored in the &lt;code&gt;roff&lt;/code&gt; format, and we can use marked-man as follows to generate our roff file from our README.md.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./node_modules/.bin/marked-man README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.TH "MY\-PACKAGE" "1" "June 2021" "" ""
.SH "NAME"
\fBmy-package\fR \- demo package
.SH Synopsis
.P
my\-package is a demonstration package that does nothing
.SH See Also
.P
example\.com

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm certainly glad I don't have to manually write in that format! Let's store this in a directory, and add an npm script so that we don't have to type out the full command every time. Add the following entry to your 'scripts' object in your package.json.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    "generate-man-page": "mkdir -p man &amp;amp;&amp;amp; marked-man README.md &amp;gt; ./man/my-package.1"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the roff output is stored in the &lt;code&gt;man&lt;/code&gt; directory in a file called &lt;code&gt;my-package.1&lt;/code&gt;. For your man file, you should follow the same naming convention: &lt;code&gt;{packageName}.{section number}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;How do we make the man utility aware of the document when we install the package? We add an entry pointing at the file in our package.json as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  "man": ["./man/my-package.1"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can test this out by running "npm install --global .", which will install the package in the current directory onto the system globally. Then, run &lt;code&gt;man my-package&lt;/code&gt; to see the man page in action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MY-PACKAGE(1)                                                                        MY-PACKAGE(1)

NAME
       my-package - demo package

Synopsis
       my-package is a demonstration package that does nothing

See Also
       example.com

                                          June 2021                                  MY-PACKAGE(1)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There we have it! Man pages for your node packages. If you'd like to have multiple man pages for your package (say your command is configurable by a dotfile that you'd like to document, for example), you can write multiple markdown documents, modify your npm script to generate them all, and add them to the list of exports on your package.json &lt;code&gt;man&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;You may notice in the npm documentation that there's a &lt;code&gt;directories.man&lt;/code&gt; configuration in the package.json spec, which is documented as exporting &lt;em&gt;all&lt;/em&gt; the man pages to the system. I wasn't able to get that to work. If you are, please &lt;a href="https://twitter.com/hughrawlinson/"&gt;let me know&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Thanks for reading. I hope that I've covered the procedure for generating man pages from markdown documents such that it's clear to you - and I hope you agree that it's a relatively low amount of effort to add a touch of delight to your package's developer experience. If you have any feedback, please get in touch &lt;a href="https://twitter.com/hughrawlinson/"&gt;on twitter&lt;/a&gt; or &lt;a href="https://mastodon.technology/@hughrawlinson"&gt;on mastodon&lt;/a&gt;. I'd love to hear from you!&lt;/p&gt;

</description>
      <category>npm</category>
      <category>javascript</category>
      <category>documentation</category>
      <category>dx</category>
    </item>
    <item>
      <title>What to do if you publish a beta build as @latest</title>
      <dc:creator>Hugh Rawlinson</dc:creator>
      <pubDate>Sat, 26 Jun 2021 19:30:51 +0000</pubDate>
      <link>https://dev.to/hughrawlinson/what-to-do-if-you-publish-a-beta-build-as-latest-3n0g</link>
      <guid>https://dev.to/hughrawlinson/what-to-do-if-you-publish-a-beta-build-as-latest-3n0g</guid>
      <description>&lt;p&gt;I recently published a beta build of &lt;a href="https://meyda.js.org/"&gt;Meyda&lt;/a&gt; to the npm registry, with the intention of having one of our longest running users test it out to make sure it worked in their project. I hadn't done a manual release in a long time, since we use &lt;code&gt;semantic-release&lt;/code&gt;, so I skimmed the output of &lt;code&gt;npm publish --help&lt;/code&gt;, and figured out what command I would run. I&lt;br&gt;
set the version field of package.json to &lt;code&gt;5.1.7-beta.0&lt;/code&gt;, as instructed built the bundle, ran our test suite, and ran &lt;code&gt;npm publish . --dry-run&lt;/code&gt;, to verify that the manifest of files that &lt;em&gt;would&lt;/em&gt; be published was correct. It was correct, and so I ran&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm publish .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I checked &lt;a href="https://www.npmjs.com/package/meyda"&gt;Meyda's page on npm&lt;/a&gt;, I was rather surprised to see that &lt;code&gt;5.1.7-beta.0&lt;/code&gt; had been published as the &lt;code&gt;latest&lt;/code&gt; tagged version of Meyda. I had incorrectly been assuming that the magic incantation required to publish a beta package was the &lt;code&gt;-beta.*&lt;/code&gt; suffix in the package version. In fact, the way to publish a beta version of an npm package is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm publish . --tag beta
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, I became worried. Had I published a beta build of a package that might inadvertently contained breaking changes to all my users? While yes, technically I had done that (for the second time that week, but that's another story), I needn't have worried. Some research revealed that while packages once published to the registry in most cases cannot be removed or modified, tags can. The &lt;code&gt;npm dist-tag&lt;/code&gt; command saved the day! I'll leave you to read the &lt;code&gt;npm dist-tag --help&lt;/code&gt;, and instead show what I did to resolve my situation. The previous "good" latest version of my package was &lt;code&gt;5.1.7&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Tag the previous version as latest
npm dist-tag add meyda@5.1.7 latest

# Tag the beta as a beta
npm dist-tag add meyda@5.1.8-beta.0 beta
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the tags were properly set, none of our users were at risk of getting broken code they hadn't opted in for, and the user who agreed to test our beta release was able to install it with &lt;code&gt;npm install meyda@beta&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;No need to worry if you find yourself in this situation. Like most things, it's completely recoverable, and everything will be ok. We all lived happily ever after!&lt;/p&gt;

</description>
      <category>npm</category>
      <category>packaging</category>
      <category>javascript</category>
      <category>publishing</category>
    </item>
    <item>
      <title>Loading Audio in Node JS</title>
      <dc:creator>Hugh Rawlinson</dc:creator>
      <pubDate>Tue, 01 Jun 2021 18:49:27 +0000</pubDate>
      <link>https://dev.to/hughrawlinson/loading-audio-in-node-js-1l7e</link>
      <guid>https://dev.to/hughrawlinson/loading-audio-in-node-js-1l7e</guid>
      <description>&lt;p&gt;Working with audio as a developer can unlock many awesome features, and a lot of fun. You can generate music, analyze audio using machine learning, build audio visualizers, music information retrieval systems, and much more. It's an extremely fun field. But working with audio can be tricky - how is sound represented on a computer? How can we manipulate that sound? And how do we serialize sound data to disk?&lt;/p&gt;

&lt;h2&gt;
  
  
  Pulse Code Modulation Encoding
&lt;/h2&gt;

&lt;p&gt;This post won't be a deep dive into audio encoding - it's a practical guide for how to load audio in Node JS, into a state that you can work with it. Generally, digital signal processing (which means "working with audio data using code") operates on a kind of audio data called &lt;a href="https://dev.toor%20"&gt;Pulse Code Modulation&lt;/a&gt;. There's a lot of fancy theory and maths behind PCM encoding - but until you're ready to dive in to Wikipedia, you can think of it as "a long list of numbers that represent the change in air pressure over time that makes up a sound". This is, after all, what a microphone measures and converts into numbers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Samples
&lt;/h3&gt;

&lt;p&gt;Each number in the list that makes up a sound is called a "sample". The sample can be represented on disk as one of several kinds of numbers - floating point numbers, integers, or other representations. The number of bits that represent the number affect the precision of the number - for example, 16 bit numbers can have much more precision than 8 bit numbers. The number of bits in each sample is referred to as the "bit depth".&lt;/p&gt;

&lt;h3&gt;
  
  
  Sample Rate
&lt;/h3&gt;

&lt;p&gt;Another important attribute of PCM encoded audio is the "sample rate". This refers to the rate at which samples should be played in order for the sound to be at the right speed. For reasons outside the scope of this post, the sample rate dictates the highest frequency component that can be represented in a sound. For the purposes of most audio intended for human listening, it's important to store audio at a sample rate slightly higher than double the maximum frequencies that humans can hear. Since humans can't really hear audio over 20,000hz, a standard sample rate has emerged at 44,100hz. The "hz" unit here refers to hertz, which means "samples per second". Sometimes you can encounter audio with a higher or lower sample frequency - audio for movies can be up to 192,000hz, and signals representing things that aren't meant for human hearing (for example, geological sonar scans) might not need as many as 44,100 samples per second.&lt;/p&gt;

&lt;h2&gt;
  
  
  Loading PCM audio from disk
&lt;/h2&gt;

&lt;p&gt;Several audio file formats store PCM encoded audio directly - wav and aiff are examples.&lt;/p&gt;

&lt;p&gt;Luckily, other developers have implemented great libraries that handle the complexities of parsing wav files for you. I recommend &lt;a href="https://www.npmjs.com/package/node-wav"&gt;node-wav&lt;/a&gt;, by &lt;a href="https://github.com/andreasgal"&gt;Andreas Gal&lt;/a&gt;. It's got a simple API, and uses the metadata at the start of the wav file to automatically choose the correct sample rate, bit depth, and number encoding. From the readme, here is a code example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;wav&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node-wav&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file.wav&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sampleRate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;channelData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// array of Float32Arrays&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;result.channelData&lt;/code&gt; variable contains a list of signals that you can use as standard Javascript Float32Arrays. The result object also exposes the sample rate, which you will likely need to know for many operations.&lt;/p&gt;

&lt;p&gt;If you're using &lt;a href="https://meyda.js.org/"&gt;Meyda&lt;/a&gt; to analyze audio that you load in this way, you will need to make sure that the sample rate of the audio matches the sample rate that Meyda is set to use. Otherwise you'll end up with audio features that are incorrect, and based on a skewed frequency scale. You can either match the Meyda sample rate to the wav sample rate, or you can resample the audio to fit a standard sample rate (i.e. 44,100hz, or 48,000hz). Resampling audio is a complicated topic beyond the scope of this article, but if you have trouble finding information online, &lt;a href="https://twitter.com/hughrawlinson"&gt;let me know&lt;/a&gt; and I may find time to write an article.&lt;/p&gt;

&lt;p&gt;AIFF files also store PCM audio data, but differ from WAV files in that they have a different header format for storing metadata. &lt;a href="https://www.npmjs.com/package/node-wav"&gt;node-wav&lt;/a&gt; doesn't support AIFF files, and I haven't found a package I would recommend to do so. If you need to analyze AIFF files, I would suggest using a utility like &lt;a href="https://www.ffmpeg.org/"&gt;ffmpeg&lt;/a&gt; to transcode the audio to wav.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about non-PCM audio formats?
&lt;/h2&gt;

&lt;p&gt;But what about audio file formats like &lt;a href="https://en.wikipedia.org/wiki/MP3"&gt;mp3&lt;/a&gt;, &lt;a href="https://xiph.org/vorbis/"&gt;ogg&lt;/a&gt;, and &lt;a href="https://xiph.org/flac/"&gt;flac&lt;/a&gt;? The difference between these formats and wav is that the audio is compressed on disk. mp3 and ogg are what's called "lossy" compression - that means they change the actual sound in ways that are hopefully imperceptible to most listeners in order to get better compression. flac, meanwhile, is a format that implements lossless compression. This means that it encodes audio on disk in a more efficient format than storing each sample as a full integer or floating point number, but without modifying the audio itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Encoding agnostic signal processing code
&lt;/h2&gt;

&lt;p&gt;It's best to write signal processing code that works with one representation of audio, and reuse it by converting the audio - rather than having one implementation of your signal processing code for each audio encoding. We can achieve code reusability by converting all audio to a common format for signal processing, so that your code only has to think about one representation. Libraries that do this are called "codecs" which comes from "enCOding/DECoding". In order to support a particular file format in your program, you will need to make sure that you have the right codec. Luckily, you don't need to understand each audio format and implement a codec yourself - you can use packages to do this. So when you're writing your signal processing code, you should write code that works on raw signals, not encoded or compressed. In many cases, in Javascript, signals are represented as Float32Arrays - and unless you have specific requirements where this causes a limitation for you, I would recommend sticking to writing code that assumes signals are in Float32Arrays.&lt;/p&gt;

&lt;h2&gt;
  
  
  Loading alternative encodings from disk
&lt;/h2&gt;

&lt;p&gt;While there are some implementations of mp3 encoders in Javascript, I would actually recommend calling out to another technology to do the transcoding. &lt;a href="https://www.ffmpeg.org/"&gt;ffmpeg&lt;/a&gt; is a long running open source project that excels in media encoding. It can translate between many different media encodings, and I'm confident that it covers a &lt;em&gt;huge&lt;/em&gt; portion of transcoding needs. In Node, you can call out to ffmpeg using the &lt;a href="https://nodejs.org/dist/latest-v16.x/docs/api/child_process.html"&gt;child_process&lt;/a&gt; API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;exec&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;child_process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;mkdtemp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs/promises&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;os&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;os&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Create a temporary directory to store transcoded audio&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TEMP_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mkdtemp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tmpdir&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;transcoder-storage-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;transcodeToWav&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;output_filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TEMP_DIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;.wav`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// "shell out" to ffmpeg&lt;/span&gt;
    &lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`ffmpeg -i &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;output_filename&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ERROR: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;output_filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stderr&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;transcodeToWav&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./164064__cclaretc__rooster.mp3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// result.filename is the new filename of the transcoded audio.&lt;/span&gt;
  &lt;span class="c1"&gt;// We can now use node-wav as described above to read the audio&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file.wav&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;decodedAudio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;decodedAudio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sampleRate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;decodedAudio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;channelData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// array of Float32Arrays&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm using a modern version of Nodejs which allows imports, top level await in .mjs files, and exposes the &lt;code&gt;fs/promises&lt;/code&gt; interface, but this code refactors back to older versions of node if you need.&lt;/p&gt;

&lt;p&gt;One thing to bear in mind is that in order for this to work, you need to have a copy of ffmpeg on the system that you're running the code on. Luckily, there's a package for that - &lt;a href="https://www.npmjs.com/package/ffmpeg-static"&gt;ffmpeg-static&lt;/a&gt; is a dependency that you can include in your project that installs a statically linked copy of ffmpeg. You can use it to ensure that ffmpeg is always available to your code. Check it out!&lt;/p&gt;

&lt;h2&gt;
  
  
  But what about the web?
&lt;/h2&gt;

&lt;p&gt;While in theory it might be possible to run ffmpeg through emscripten and run it in a web worker (I certainly assume someone has done this), it's not necessarily practical to try and use the same technique from node to transcode audio on the web. The good news is that the w3c has chartered a working group to focus on &lt;a href="https://github.com/w3c/webcodecs"&gt;web codecs&lt;/a&gt;. While this is at the time of writing still in early stages, the working group is powering ahead on designing and proposing an API to enable media transcoding on the web, and hopefully that will become available to us in the near future.&lt;/p&gt;

&lt;h2&gt;
  
  
  What did we learn?
&lt;/h2&gt;

&lt;p&gt;In this blog post, I covered the basics of Pulse Code Modulation encoding, how to load wav files from disk, the difference between wav files and other audio encoding file formats, transcoding other file formats to wav for loading in node, and how transcoding might soon work outside of node, but on the web. I hope these explanations have been useful to you. If anything is unclear, or you have more questions, please &lt;a href="https://twitter.com/hughrawlinson"&gt;let me know&lt;/a&gt; on Twitter! Thanks for reading.&lt;/p&gt;

</description>
      <category>node</category>
      <category>audio</category>
      <category>meyda</category>
    </item>
    <item>
      <title>Handling Pagination with Async Iterators</title>
      <dc:creator>Hugh Rawlinson</dc:creator>
      <pubDate>Thu, 20 May 2021 16:36:16 +0000</pubDate>
      <link>https://dev.to/hughrawlinson/handling-pagination-with-async-iterators-1p4f</link>
      <guid>https://dev.to/hughrawlinson/handling-pagination-with-async-iterators-1p4f</guid>
      <description>&lt;p&gt;When you're interacting with a server from your frontend Javascript code, you might need to handle paging. Paging is a technique used by API designers to avoid enormous (and sometimes impossibly large) responses to requests when providing access to large collections of information to clients. Instead of returning every single item in a collection as a response to a request, an API might return the first 50 of the items in the collection, and a message to the client to say "this isn't all the items in the collection. If you want to get the next 50 items, here's how".&lt;/p&gt;

&lt;p&gt;That's what the Spotify API does. When you need to get a list of albums by particularly prolific performers, you won't necessarily be able to get them all in one page, and will have to handle pagination to get all the albums.&lt;/p&gt;

&lt;p&gt;It's possible to interact with pagination in an imperative way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;artistId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;6sFIWsNpZYqfjUpaCgueju&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;loadAlbums&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;artistId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;authToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://api.spotify.com/v1/artists/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;artistId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/albums?limit=20&amp;amp;include_groups=album`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;albums&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="c1"&gt;// We'll set endpoint to page.next when we receive it in the response.&lt;/span&gt;
  &lt;span class="c1"&gt;// When there is no more data, the API will set page.next to null, and we'll&lt;/span&gt;
  &lt;span class="c1"&gt;// escape this while loop.&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;authToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Request failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;albums&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;albums&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;albums&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;album&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;loadAlbums&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;artistId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;YOUR_OWN_AUTH_TOKEN&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;album&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code works, but there are some problems with it.&lt;/p&gt;

&lt;p&gt;The code that is consuming the data is mixed with the code that handles pagination.&lt;/p&gt;

&lt;p&gt;You can extract the code that handles the pagination by converting the whole block into an async function. But since functions can only return data once, you're stuck until all the requests are finished before you can return albums and use it.&lt;/p&gt;

&lt;p&gt;This is where async generators come in. Generators are functions that can &lt;code&gt;yield&lt;/code&gt; multiple results, rather than just one. Asynchronous (async) generators are analogous to Promises that can resolve multiple times. They also provide syntactic sugar to make it easier to iterate over the yielded values - &lt;code&gt;for await ... of&lt;/code&gt; syntax.&lt;/p&gt;

&lt;p&gt;Async Iterators are one solution to this problem - &lt;a href="https://rxjs-dev.firebaseapp.com/guide/observable"&gt;observables&lt;/a&gt; are another solution, but they haven't made it into the EcmaScript specification.&lt;/p&gt;

&lt;p&gt;The following is some example code that demonstrates how to use a recursive async generator to yield each page of albums one by one until we're out of pages. You'll see how the code that consumes the albums uses the &lt;code&gt;for await ... of&lt;/code&gt; syntax to access the results of the generator&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;pageThroughResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;authToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;makeRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_endpoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;headers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;authToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;makeRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;makeRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;loadAlbums&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;artistId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;authToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://api.spotify.com/v1/artists/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;artistId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/albums?limit=20&amp;amp;include_groups=album`&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pageThroughResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;authToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;album&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nx"&gt;album&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;album&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;loadAlbums&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;6sFIWsNpZYqfjUpaCgueju&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;YOUR_OWN_AUTH_TOKEN&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;album&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the code that's responsible for making requests to the paginated external service is abstract - the behavior responsible for managing the pagination (the &lt;code&gt;pageThroughResource&lt;/code&gt; function) doesn't know about &lt;em&gt;what&lt;/em&gt; it's paginating through. The logic that knows about loading albums (the &lt;code&gt;loadAlbums&lt;/code&gt;) function is what handles the specific details of the API that we're calling. The only assumption that the &lt;code&gt;pageThroughResource&lt;/code&gt; function makes is that the response object from the API returns a field called &lt;code&gt;next&lt;/code&gt; which provides the URL of the next page of the resource listing. This means that you can re-use the &lt;code&gt;pageThroughResource&lt;/code&gt; function on any API call you need to make that has the same pagination design.&lt;/p&gt;

&lt;p&gt;The code achieves the separation of these two distinct behaviors by creating functions that return asynchronous iterators. &lt;code&gt;pageThroughResource&lt;/code&gt; returns an asynchronous iterator, but also internally defines &lt;em&gt;another function&lt;/em&gt;, &lt;code&gt;makeRequest&lt;/code&gt;, that &lt;em&gt;also&lt;/em&gt; returns an asynchronous iterator. &lt;code&gt;pageThroughResource&lt;/code&gt; uses the &lt;code&gt;yield *&lt;/code&gt; syntax to yield to whatever &lt;code&gt;makeRequest&lt;/code&gt;'s resulting async iterator returns. The code is organized this way so that &lt;code&gt;makeRequest&lt;/code&gt; is able to call itself recursively. Inside makeRequest, first the JSON result of the response of the API call is yielded, and the user can use it immediately. After that, only if the response contains a &lt;code&gt;next&lt;/code&gt; field, &lt;code&gt;makeRequest&lt;/code&gt; will delegate control of the generator to another instance of itself, made to handle the next page. While that request is being made, the calling code already has access to the result of the first page. That means we don't have to wait until all the pages are loaded before we can start using the information we get from the API.&lt;/p&gt;

&lt;p&gt;These specific functions make a few assumptions, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the API you're calling will return JSON&lt;/li&gt;
&lt;li&gt;the JSON that your API returns will contain a field called &lt;code&gt;next&lt;/code&gt;, which provides the next page of the resource listing for you to call&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But you can use this pattern in your own code, tailored to however your API handles response types and pagination data. You could even use this pattern to page through a resource in a GraphQL API.&lt;/p&gt;

&lt;p&gt;One specific drawback to point out: iterators in Javascript don't have the &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;reduce&lt;/code&gt;, and &lt;code&gt;filter&lt;/code&gt; algorithms that you might know from arrays - you'll have to use the &lt;code&gt;for await .. of&lt;/code&gt; syntax to handle their output. Maybe one day we'll get that interface!&lt;/p&gt;

&lt;p&gt;I hope this helps you keep your code nice and maintainable!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>generators</category>
      <category>tutorial</category>
      <category>pagination</category>
    </item>
  </channel>
</rss>
