<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:wfw="http://wellformedweb.org/CommentAPI/"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
  xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
  >
  <channel>
    <title xml:lang="en">Human Who Codes</title>
    <atom:link href="http://feeds.nczonline.net/blog/" rel="self" type="application/rss+xml"/>
    <link>https://humanwhocodes.com/</link>
    <pubDate>Tue, 10 Jan 2023 15:05:23 +0000</pubDate>
    <lastBuildDate>Tue, 10 Jan 2023 15:05:23 +0000</lastBuildDate>
    <language>en-US</language>
    <generator>Jekyll v3.8.7</generator>
    <description>The Official Web Site of Nicholas C. Zakas</description>

    
    <item>
      <title>Setting up a Rust development environment with Visual Studio Code</title>
      <link>https://humanwhocodes.com/blog/2022/12/rust-development-environment-visual-studio-code/</link>
      <pubDate>Thu, 15 Dec 2022 00:00:00 +0000</pubDate>
      <dc:creator>Nicholas C. Zakas</dc:creator>
      
      <category>Rust</category>
      
      <category>Visual Studio Code</category>
      
      
      <category>Programming</category>
      
      <guid isPermaLink="true">https://humanwhocodes.com/blog/2022/12/rust-development-environment-visual-studio-code/</guid>
      <description>When I decided to teach myself Rust, I naturally started looking around at what editors I could use. It turned out that most of the folks I asked online were still using Visual Studio Code for Rust. To my surprise, though, setting up a complete development environment in Visual Studio Code wasn’t as straightforward as...</description>
      <content:encoded>&lt;p&gt;When I decided to teach myself Rust, I naturally started looking around at what editors I could use. It turned out that most of the folks I asked online were still using Visual Studio Code for Rust. To my surprise, though, setting up a complete development environment in Visual Studio Code wasn’t as straightforward as I expected. I need to find and download different tools in order to get started.&lt;/p&gt;

&lt;p&gt;(I’m assuming you already have Visual Studio Code installed, but if not, &lt;a href=&quot;https://code.visualstudio.com&quot;&gt;go install it&lt;/a&gt;.)&lt;/p&gt;

&lt;h2 id=&quot;step-1-download-and-install-the-rustup&quot;&gt;Step 1: Download and install the &lt;code class=&quot;highlighter-rouge&quot;&gt;rustup&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;The easiest starting point for most folks is to install &lt;a href=&quot;https://rustup.rs/&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;rustup&lt;/code&gt;&lt;/a&gt;. If you’re coming from Node.js development, the closest analog would be the &lt;a href=&quot;https://nvm.sh&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;nvm&lt;/code&gt;&lt;/a&gt; in that &lt;code class=&quot;highlighter-rouge&quot;&gt;rustup&lt;/code&gt; not only installs everying that you need to start Rust development but also allows you to easily switch back and forth between release channels and install additional components.&lt;/p&gt;

&lt;p&gt;When you install &lt;code class=&quot;highlighter-rouge&quot;&gt;rustup&lt;/code&gt;, you get a complete Rust toolchain including the compiler (&lt;code class=&quot;highlighter-rouge&quot;&gt;rustc&lt;/code&gt;) and the &lt;code class=&quot;highlighter-rouge&quot;&gt;cargo&lt;/code&gt; (closest Node.js analog is &lt;code class=&quot;highlighter-rouge&quot;&gt;npm&lt;/code&gt;). With those installed, you can start compiling Rust programs immediately.&lt;/p&gt;

&lt;p&gt;You can read more about &lt;code class=&quot;highlighter-rouge&quot;&gt;rustup&lt;/code&gt; in the &lt;a href=&quot;https://rust-lang.github.io/rustup/index.html&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;rustup&lt;/code&gt; Handbook&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;step-2-create-a-project&quot;&gt;Step 2: Create a project&lt;/h2&gt;

&lt;p&gt;Technically it’s not necessary to create a project to proceed to the next steps, but it will help you ensure that Visual Studio Code is set up correctly. So, create a simple project to get started by running:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cargo new hello-world
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This command creates a new directory called &lt;code class=&quot;highlighter-rouge&quot;&gt;hello-world&lt;/code&gt; that contains a Rust project scaffold, including:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Cargo.toml&lt;/code&gt; - the equivalent of &lt;code class=&quot;highlighter-rouge&quot;&gt;package.json&lt;/code&gt; for Rust&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;src/main.rs&lt;/code&gt; - the Rust source file to execute&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can then &lt;code class=&quot;highlighter-rouge&quot;&gt;cd&lt;/code&gt; into &lt;code class=&quot;highlighter-rouge&quot;&gt;hello-world&lt;/code&gt; and run:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cargo run
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will execute the &lt;code class=&quot;highlighter-rouge&quot;&gt;src/main.rs&lt;/code&gt; file and should print out “Hello world” if everything is installed correctly. (You can think of &lt;code class=&quot;highlighter-rouge&quot;&gt;cargo run&lt;/code&gt; as an analog to &lt;code class=&quot;highlighter-rouge&quot;&gt;npm start&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;Now we are ready to prepare Visual Studio Code for Rust development!&lt;/p&gt;

&lt;h2 id=&quot;step-3-install-the-rust-analyzer-visual-studio-code-extension&quot;&gt;Step 3: Install the &lt;code class=&quot;highlighter-rouge&quot;&gt;rust-analyzer&lt;/code&gt; Visual Studio Code extension&lt;/h2&gt;

&lt;p&gt;The most important Visual Studio Code extension to install is &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;rust-analyzer&lt;/code&gt;&lt;/a&gt;. You can think of &lt;code class=&quot;highlighter-rouge&quot;&gt;rust-analyzer&lt;/code&gt; as the equivalent of the default Visual Studio Code JavaScript language server (which include TypeScript) and ESLint. Without &lt;code class=&quot;highlighter-rouge&quot;&gt;rust-analyzer&lt;/code&gt;, you are basically just using Visual Studio Code as a text editor without all of the additional type checking, lints, and code completion that you expected.&lt;/p&gt;

&lt;p&gt;Once installed, you can open up &lt;code class=&quot;highlighter-rouge&quot;&gt;src/main.rs&lt;/code&gt; (or any other Rust file) and you’ll get the Visual Studio Code experience that you expect. (If you get an error message saying that &lt;code class=&quot;highlighter-rouge&quot;&gt;rust-analyzer&lt;/code&gt; couldn’t find a Rust workspace, that just means the &lt;code class=&quot;highlighter-rouge&quot;&gt;Cargo.toml&lt;/code&gt; file is missing or invalid.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Make sure you &lt;code class=&quot;highlighter-rouge&quot;&gt;cargo run&lt;/code&gt; your project at least once before opening a &lt;code class=&quot;highlighter-rouge&quot;&gt;.rs&lt;/code&gt; file in Visual Studio code. The &lt;code class=&quot;highlighter-rouge&quot;&gt;rust-analyzer&lt;/code&gt; extension requires the build information found in the &lt;code class=&quot;highlighter-rouge&quot;&gt;target&lt;/code&gt; directory in order to do things like code completion and symbol lookup. If it seems like you aren’t getting any of that info in Visual Studio Code, stop and run &lt;code class=&quot;highlighter-rouge&quot;&gt;cargo run&lt;/code&gt;. If that still doesn’t work, make sure the file you are working on is referenced in &lt;code class=&quot;highlighter-rouge&quot;&gt;main.rs&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;lib.rs&lt;/code&gt;. Files that aren’t referenced by your primary file in some way aren’t compiled and so &lt;code class=&quot;highlighter-rouge&quot;&gt;rust-analyzer&lt;/code&gt; doesn’t know about them.&lt;/p&gt;

&lt;h2 id=&quot;step-4-install-the-codelldb-visual-studio-code-extension&quot;&gt;Step 4: Install the CodeLLDB Visual Studio Code extension&lt;/h2&gt;

&lt;p&gt;Out of the box, you won’t be able to debug Rust code in Visual Studio Code. You’ll need to install either the &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb&quot;&gt;CodeLLDB&lt;/a&gt; extension or the &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools&quot;&gt;Microsoft C/C++ Tools&lt;/a&gt; extension. Because the Rust compiler is build on &lt;a href=&quot;https://llvm.org/&quot;&gt;LLVM&lt;/a&gt;, you’ll need one of these two extensions to generate the debug information you’ll need to debug Rust programs in Visual Studio Code. Most folks seem to prefer CodeLLDB, like because it’s not Microsoft-related, but either will work.&lt;/p&gt;

&lt;h2 id=&quot;bonus-step-install-the-crates-visual-studio-code-extension&quot;&gt;Bonus Step: Install the &lt;code class=&quot;highlighter-rouge&quot;&gt;crates&lt;/code&gt; Visual Studio Code extension&lt;/h2&gt;

&lt;p&gt;While this extension isn’t strictly necessary, the &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=serayuzgur.crates&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;crates&lt;/code&gt;&lt;/a&gt; extension helps ensure that you are using the most up-to-date versions of your dependencies. It does this by placing a small green checkmark to the right of each dependency in your &lt;code class=&quot;highlighter-rouge&quot;&gt;Cargo.toml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://humanwhocodes.com/images/posts/2022/rust-crates-extension.png&quot; alt=&quot;The crates extension showing up-to-date green checkmarks in Cargo.toml&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;It took me several days to get Visual Studio Code set up for Rust development by finding all of the various tools and extensions that needed to be installed to work properly. Once set up, though, Visual Studio Code is just as suitable a development environment as any other editor or IDE could be for Rust. The &lt;code class=&quot;highlighter-rouge&quot;&gt;rust-analyzer&lt;/code&gt; extension, in particular, gives you almost everything you need to develop Rust as rapidly as JavaScript in Visual Studio Code. Adding in the other extensions really pushes Visual Studio Code into the “great” category for Rust development.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Sponsoring dependencies: The next step in open source sustainability</title>
      <link>https://humanwhocodes.com/blog/2022/06/sponsoring-dependencies-open-source-sustainability/</link>
      <pubDate>Tue, 14 Jun 2022 00:00:00 +0000</pubDate>
      <dc:creator>Nicholas C. Zakas</dc:creator>
      
      <category>Open Source</category>
      
      <category>Fundraising</category>
      
      <category>Sustainability</category>
      
      
      <category>Open Source</category>
      
      <guid isPermaLink="true">https://humanwhocodes.com/blog/2022/06/sponsoring-dependencies-open-source-sustainability/</guid>
      <description>When the JavaScript Standard Style (StandardJS) project1 decided to show ads during installation, the backlash was swift and harsh. The project is an opinionated JavaScript style guide, formatter, and linter all in one, and it was also the first npm project to try and raise money by inserting an ad in the command line.2 The...</description>
      <content:encoded>&lt;p&gt;When the JavaScript Standard Style (StandardJS) project&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; decided to show ads during installation, the backlash was swift and harsh. The project is an opinionated JavaScript style guide, formatter, and linter all in one, and it was also the first npm project to try and raise money by inserting an ad in the command line.&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; The “experiment” was terminated soon after it was started.&lt;sup id=&quot;fnref:3&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; But to me, this opened up a big question: given that StandardJS is simply a wrapper around ESLint that disables customization, what responsibility does StandardJS have to ESLint?&lt;/p&gt;

&lt;p&gt;Open source maintainers should be able to accept sponsorships for their work, but does that funded project have a responsibility to the project on which it was built to pass some portion of the funds along? After all, if the majority of the benefit of your project is based on the work of someone else’s project, is it really fair for you to profit?&lt;/p&gt;

&lt;p&gt;Isn’t there some level of responsibility when a project receives funding to help all of the projects on which it is built?&lt;/p&gt;

&lt;h2 id=&quot;the-stratified-open-source-ecosystem&quot;&gt;The stratified open source ecosystem&lt;/h2&gt;

&lt;p&gt;When people say “open source,” it could mean any number of things. Vue,&lt;sup id=&quot;fnref:4&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; the UI framework upon which many modern web applications are built, is an open source project with 2.8 million weekly npm downloads; &lt;code class=&quot;highlighter-rouge&quot;&gt;he&lt;/code&gt;,&lt;sup id=&quot;fnref:5&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; a small npm package that helps with encoding and decoding HTML entities in strings, is also an open source project, and it has 13.5 million weekly npm downloads. Which one would you rather donate to? Does one inherently deserve more funding than the other?&lt;/p&gt;

&lt;p&gt;Some may say that both projects have a responsibility to find their own funding. They are both equally capable of setting up a website to accept donations. They are both equally capable of reaching out to individuals and companies for funding. They are both equally capable of giving presentations about their project to attract interest. But there is a fundamental difference between these two projects.&lt;/p&gt;

&lt;p&gt;The reality is that we live in a stratified open source ecosystem. The very top spot is reserved for those projects that get a lot of name recognition and use. Projects like Vue that are end-user facing (the result is visible directly to end-users because the user interface is built with it) and those that are developer-facing (developers specifically choose to use the project) get a lot of attention as people share their experiences, best practices, tooling, testing, and other tips and tricks to best use the project. Because Vue is constantly being mentioned, the project gets more mindshare, and that makes it easier to attract sponsorships; because Vue is end-user facing, it’s easier to understand its value proposition to the companies that use it. Both companies and developers are more motivated to support these projects both financially and through code contributions.&lt;/p&gt;

&lt;p&gt;On the other end of the spectrum, there are ecosystem projects that provide utilities or low-level functionality, like &lt;code class=&quot;highlighter-rouge&quot;&gt;he&lt;/code&gt;, that most people will never hear of. These projects are included as dependencies by the projects that companies and developers choose to use, and so sit in the background without much visibility. And even if companies and developers hear about a dependency like &lt;code class=&quot;highlighter-rouge&quot;&gt;he&lt;/code&gt;, would they think it’s important enough to fund? Can you imagine approaching a company for funding and explaining that your utility encodes and decodes HTML entities? They would probably think you were joking, and in any case would laugh.&lt;/p&gt;

&lt;p&gt;There is no doubt that Vue requires more ongoing maintenance and development than &lt;code class=&quot;highlighter-rouge&quot;&gt;he&lt;/code&gt;, so &lt;code class=&quot;highlighter-rouge&quot;&gt;he&lt;/code&gt; clearly doesn’t need as much funding, but does it deserve zero? Yet, Vue depends on &lt;code class=&quot;highlighter-rouge&quot;&gt;he&lt;/code&gt;, so clearly it has value to Vue. What responsibility does Vue then have to &lt;code class=&quot;highlighter-rouge&quot;&gt;he&lt;/code&gt;?&lt;/p&gt;

&lt;h2 id=&quot;the-consequences-of-stratified-open-source&quot;&gt;The consequences of stratified open source&lt;/h2&gt;

&lt;p&gt;We have already seen major security issues in open source projects that weren’t properly funded. The heartbleed bug in OpenSSL essentially made the web unsafe until it could be fixed.&lt;sup id=&quot;fnref:6&quot;&gt;&lt;a href=&quot;#fn:6&quot; class=&quot;footnote&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; At the time (2014), OpenSSL was receiving just $2,000 per year in donations and had one developer working on it. OpenSSL is still a foundational piece of internet technology, embedded in everything from web browsers to web server software, and yet it was terminally underfunded. Eventually, the Linux Foundation stepped up to pledge support to ensure that such an important project was never left without funding again.&lt;/p&gt;

&lt;p&gt;Closer to the JavaScript ecosystem, the creator of faker.js and colors.js Marak Squires intentionally crippled&lt;sup id=&quot;fnref:7&quot;&gt;&lt;a href=&quot;#fn:7&quot; class=&quot;footnote&quot;&gt;7&lt;/a&gt;&lt;/sup&gt; the projects over frustration about a lack of funding. He is quoted as saying he was, “no longer going to support Fortune 500s (and other smaller-sized companies) with my free work.” He had tried to set up a GitHub Sponsors account and an Open Collective account, but he still wasn’t able to make enough money.&lt;/p&gt;

&lt;p&gt;If a foundational piece of internet security like OpenSSL has struggled with a lack of funding for years, how do you think that the conversation would have gone asking to sponsor faker.js (a project for creating fake data) and colors.js (a project to add colors and styles into console applications) would have gone?&lt;/p&gt;

&lt;p&gt;The harsh reality is that not all open source projects have the same opportunity to raise funds. So how can we expect these projects to survive?&lt;/p&gt;

&lt;h2 id=&quot;with-great-power&quot;&gt;With great power…&lt;/h2&gt;

&lt;p&gt;It’s too easy to say that each project should be responsible for its own funding when there is such a clear stratification of projects. If a company has an open source sponsorship budget, a package like &lt;code class=&quot;highlighter-rouge&quot;&gt;he&lt;/code&gt; has very little chance of competing for those funds against Vue. Vue will win 99% of the time. When scaled across all open source projects, we end up with a small number of large projects that receive the majority of funding, and then a large number of smaller projects that receive little or no funding. And when the bigger projects rely on the smaller projects, this is unsustainable. A single broken low-level dependency can risk the entire larger project and all of its users.&lt;/p&gt;

&lt;p&gt;The solution to this problem really is simple: open source projects should fund their own dependencies. I would even go so far as to say this isn’t just the best solution to the problem, it is, in fact, a &lt;em&gt;responsibility&lt;/em&gt; of those larger, well-funded projects to support their dependencies. The spirit of open source is one of giving recognition to those pieces upon which your software is built, and there isn’t any reason that recognition can’t be extended to funding.&lt;/p&gt;

&lt;p&gt;There are several projects in the JavaScript ecosystem that receive over $100,000 each year in donations, including:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Babel&lt;sup id=&quot;fnref:8&quot;&gt;&lt;a href=&quot;#fn:8&quot; class=&quot;footnote&quot;&gt;8&lt;/a&gt;&lt;/sup&gt; - $303,000&lt;/li&gt;
  &lt;li&gt;Webpack&lt;sup id=&quot;fnref:9&quot;&gt;&lt;a href=&quot;#fn:9&quot; class=&quot;footnote&quot;&gt;9&lt;/a&gt;&lt;/sup&gt; - $250,000&lt;/li&gt;
  &lt;li&gt;ESLint&lt;sup id=&quot;fnref:10&quot;&gt;&lt;a href=&quot;#fn:10&quot; class=&quot;footnote&quot;&gt;10&lt;/a&gt;&lt;/sup&gt; - $190,000&lt;/li&gt;
  &lt;li&gt;Vue&lt;sup id=&quot;fnref:11&quot;&gt;&lt;a href=&quot;#fn:11&quot; class=&quot;footnote&quot;&gt;11&lt;/a&gt;&lt;/sup&gt; - $150,000 (plus unknown amount on GitHub Sponsors)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Imagine if each of these projects carved out a percentage of their sponsorships to give to their dependencies. At 10%, that would mean over $80,000 would end up in the hands of other projects that are less able to attract funding. That amount of money would make a significant difference to smaller projects.&lt;/p&gt;

&lt;h2 id=&quot;a-practical-sponsorship-pledge&quot;&gt;A practical sponsorship pledge&lt;/h2&gt;

&lt;p&gt;What does a practical dependency sponsorship program look like? Certainly, a project can’t be expected to give away the majority of its funding to dependencies, and I wouldn’t suggest that. Instead, there are a few simple steps maintainers can take to get started:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Budget to give away 10% of your funding to your dependencies.&lt;/strong&gt; The best way to plan is just to budget right from the start. In most cases, 10% of your funding isn’t enough to hurt your project’s maintenance and development. If in your case it is too much, then cut it down to 5%. Or 1%. The important part is that you plan to start supporting your dependencies on a monthly basis by budgeting for it.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Start with the have-nots and work your way up.&lt;/strong&gt; When deciding which projects to sponsor, start with the ones that have little or no funding. You can use BackYourStack&lt;sup id=&quot;fnref:12&quot;&gt;&lt;a href=&quot;#fn:12&quot; class=&quot;footnote&quot;&gt;12&lt;/a&gt;&lt;/sup&gt; to identify your dependencies and determine if they have an Open Collective account. If they don’t have a mechanism set up to accept donations, you can always pledge them on Open Collective&lt;sup id=&quot;fnref:13&quot;&gt;&lt;a href=&quot;#fn:13&quot; class=&quot;footnote&quot;&gt;13&lt;/a&gt;&lt;/sup&gt; or reach out and encourage them to set up a way to donate. It’s fine to start with sponsoring one project rather than trying to spread funding across multiple. As you raise more money, you can always sponsor more projects. It doesn’t make sense to donate money to projects that already have more money than yours. Even though ESLint uses Webpack, Webpack has more funding than ESLint, so it doesn’t make sense for ESLint to donate to Webpack. Instead, ESLint donates to dependencies like lint-staged, which have significantly less funding.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Reward excellence.&lt;/strong&gt; In addition to financial need, you may want to add other criteria that show how well the project is maintained. Does it have a proper open source license? Does it have a code of conduct? You can use donations as a way to help level up your dependencies in addition to supporting them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this way, projects of any size can contribute to the well-being of their dependencies. Sponsorships then flow from the largest, best-funded projects all the way down the dependency tree to the zero-dependency packages. Even if your project is receiving as little as $100 each month, you can still make a difference to another project that isn’t receiving any funding.&lt;/p&gt;

&lt;h2 id=&quot;a-call-to-action-do-your-part&quot;&gt;A call to action: do your part&lt;/h2&gt;

&lt;p&gt;If you are the maintainer of a well-funded open source project, &lt;em&gt;now&lt;/em&gt; is the time to step up. For too long, we have sat back and taken in sponsorships for our own use even as the projects that we depend upon have struggled to find funding. We have the power and responsibility to improve the situation for maintainers of all open source projects. ESLint&lt;sup id=&quot;fnref:14&quot;&gt;&lt;a href=&quot;#fn:14&quot; class=&quot;footnote&quot;&gt;14&lt;/a&gt;&lt;/sup&gt; and Astro&lt;sup id=&quot;fnref:15&quot;&gt;&lt;a href=&quot;#fn:15&quot; class=&quot;footnote&quot;&gt;15&lt;/a&gt;&lt;/sup&gt; have already established programs to donate to dependencies. You can join them in spreading the wealth around.&lt;/p&gt;

&lt;p&gt;If you maintain a smaller open source project that needs funding, figure out if there are any well-funded projects that depend on your project directly and contact the maintainer. Feel free to ask for their financial support and reference this blog post. Make sure that you have set up both a GitHub Sponsors&lt;sup id=&quot;fnref:16&quot;&gt;&lt;a href=&quot;#fn:16&quot; class=&quot;footnote&quot;&gt;16&lt;/a&gt;&lt;/sup&gt; account and an Open Collective&lt;sup id=&quot;fnref:17&quot;&gt;&lt;a href=&quot;#fn:17&quot; class=&quot;footnote&quot;&gt;17&lt;/a&gt;&lt;/sup&gt; account to make it easy to accept donations. Don’t be afraid to approach these folks and ask for their help. They have benefited from your work for a long time, so you’re not asking for something in return for nothing; you’re asking for compensation for the value you’ve already provided.&lt;/p&gt;

&lt;p&gt;If you are sponsoring an open source project, ask them what their dependency support program looks like. Sponsors are in a unique position to apply pressure on projects to distribute their funds appropriately. For the ESLint project, we have heard from sponsors that we were chosen over other projects partly because we were supporting our dependencies. As in any marketplace, the people with the money have an outsize effect on how the marketplace runs.&lt;/p&gt;

&lt;p&gt;We all need to do our parts to ensure that we are moving towards an open source ecosystem where the most valuable projects don’t struggle with funding. Let’s open up a downstream transfer of funding wherever possible.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;“We need to fund projects based on what we use rather than what we see.”
– Ben Nickolls, Executive Director, Open Source Collective&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Open source isn’t going to survive if we keep doing things the same way they’ve been done. Relying on individual donations of $5-$10 each month isn’t anywhere near enough for most projects to survive. The only solution is corporate funding. And while more companies are willing to donate to open source, the number of projects they are willing to donate to is small and often limited to what is providing the most immediate value to them. Those projects that are fortunate enough to have the visibility and mindshare to attract funding have a responsibility to their dependencies to ensure the entire ecosystem’s survival. If we can end up in a place where companies donate to a small number of open source projects, and those projects, in turn, donate to others, we will help spread funding throughout the entire ecosystem and truly create a path to sustainability.&lt;/p&gt;

&lt;p&gt;&lt;i&gt;Thanks to &lt;a href=&quot;https://fredkschott.com/&quot;&gt;Fred Schott&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/benjam&quot;&gt;Ben Nickolls&lt;/a&gt; for reviewing early drafts of this post.&lt;/i&gt;&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://standardjs.com&quot;&gt;JavaScript Standard Style&lt;/a&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://github.com/standard/standard/issues/1381&quot;&gt;npm install funding&lt;/a&gt; &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://wptavern.com/standardjs-pauses-experiment-with-ads-in-the-terminal-after-linode-pulls-sponsorship&quot;&gt;StandardJS Pauses Experiment with Ads in the Terminal after Linode Pulls Sponsorship&lt;/a&gt; &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:4&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://vuejs.org&quot;&gt;Vue&lt;/a&gt; &lt;a href=&quot;#fnref:4&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:5&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://npmjs.com/package/he&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;he&lt;/code&gt;&lt;/a&gt; &lt;a href=&quot;#fnref:5&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:6&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://arstechnica.com/information-technology/2014/04/tech-giants-chastened-by-heartbleed-finally-agree-to-fund-openssl/&quot;&gt;Tech giants, chastened by Heartbleed, finally agree to fund OpenSSL&lt;/a&gt; &lt;a href=&quot;#fnref:6&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:7&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://www.theregister.com/2022/01/10/npm_fakerjs_colorsjs/&quot;&gt;JavaScript dev deliberately screws up own popular npm packages to make a point of some sort&lt;/a&gt; &lt;a href=&quot;#fnref:7&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:8&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://opencollective.com/webpack&quot;&gt;Open Collective: Webpack&lt;/a&gt; &lt;a href=&quot;#fnref:8&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:9&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://opencollective.com/babel&quot;&gt;Open Collective: Babel&lt;/a&gt; &lt;a href=&quot;#fnref:9&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:10&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://opencollective.com/eslint&quot;&gt;Open Collective: ESLint&lt;/a&gt; &lt;a href=&quot;#fnref:10&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:11&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://opencollective.com/vuejs&quot;&gt;Open Collective: Vue&lt;/a&gt; &lt;a href=&quot;#fnref:11&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:12&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://backyourstack.com&quot;&gt;BackYourStack&lt;/a&gt; &lt;a href=&quot;#fnref:12&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:13&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://opencollective.com/pledges/new&quot;&gt;Open Collective: Make a Pledge&lt;/a&gt; &lt;a href=&quot;#fnref:13&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:14&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://eslint.org/blog/2020/09/supporting-eslint-dependencies&quot;&gt;ESLint: Supporting our dependencies&lt;/a&gt; &lt;a href=&quot;#fnref:14&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:15&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://twitter.com/astrodotbuild/status/1512144331666247694&quot;&gt;Astro dependencies donation announcement&lt;/a&gt; &lt;a href=&quot;#fnref:15&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:16&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://github.com/sponsors&quot;&gt;GitHub Sponsors&lt;/a&gt; &lt;a href=&quot;#fnref:16&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:17&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://docs.oscollective.org/what-we-offer&quot;&gt;Open Source Collective: What We Offer&lt;/a&gt; &lt;a href=&quot;#fnref:17&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Making your open source project sponsor-ready, Part 3: Accepting sponsorships</title>
      <link>https://humanwhocodes.com/blog/2021/12/making-open-source-project-sponsor-ready-accepting-sponsorships/</link>
      <pubDate>Tue, 28 Dec 2021 00:00:00 +0000</pubDate>
      <dc:creator>Nicholas C. Zakas</dc:creator>
      
      <category>Open Source</category>
      
      <category>Fundraising</category>
      
      <category>Sponsorship</category>
      
      
      <category>Open Source</category>
      
      <guid isPermaLink="true">https://humanwhocodes.com/blog/2021/12/making-open-source-project-sponsor-ready-accepting-sponsorships/</guid>
      <description>In the previous two posts in this series, I described why companies sponsor open source projects1 and how following some basic project hygiene can help attract sponsors2. Now that your project is functioning at a high level and is attractive to companies, it’s time to talk specifics about accepting sponsorships from companies. Making it easy...</description>
      <content:encoded>&lt;p&gt;In the previous two posts in this series, I described why companies sponsor open source projects&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; and how following some basic project hygiene can help attract sponsors&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. Now that your project is functioning at a high level and is attractive to companies, it’s time to talk specifics about accepting sponsorships from companies.&lt;/p&gt;

&lt;h2 id=&quot;making-it-easy-for-companies-to-donate&quot;&gt;Making it easy for companies to donate&lt;/h2&gt;

&lt;p&gt;If you only accept donations through Bitcoin, paper checks, or direct deposit into your personal checking account, what signal do you think you’re sending to companies? How you accept sponsorships largely determines how many you will receive in two ways:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Establishes (or destroys) trust with the company&lt;/li&gt;
  &lt;li&gt;Makes it easy (or hard) for the company to donate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Companies are not going to create a new process to pay you. You need to find a way to accept donations using the processes that companies already use. By accepting donations through a trusted intermediary, you’re signaling that you’ve taken the time to think through your sponsorship program and are comfortable with some transparency and traceability when it comes to your sponsorships. Both are important for making companies comfortable with donating. Fortunately, there are two excellent options available:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Open Collective&lt;/strong&gt;&lt;sup id=&quot;fnref:3&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; - Open Collective is an open source-focused sponsorship site that is  popular among many projects and contributors. Open Collective offers a number of different ways for companies to donate, including through direct debit, lump sum payments that are distributed to many projects over time, and gift cards. Many large companies signed up for Open Collective early on and are familiar with the platform. Donations are received through the Open Source Collective, which is a nonprofit, which may be important for some companies. There is a small fee for handling funds.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;GitHub Sponsors&lt;/strong&gt;&lt;sup id=&quot;fnref:4&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; - If a company cares about open source, then they likely have a corporate GitHub account. That is good news for you because they can donate through GitHub Sponsors using their existing billing relationship with GitHub. If you already have a GitHub account, there’s no reason not to set up a GitHub Sponsors page. It’s free, and GitHub doesn’t take any fees from the donations collected. You can also have money from GitHub Sponsors transferred to your Open Collective account so the money is all in one place (more on why you might want to do this later).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you have set up your funding options, be sure to configure your funding.yml file so GitHub will display a sponsor button on your project page.&lt;sup id=&quot;fnref:5&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; You can add Open Collective, GitHub Sponsors, and other links that will appear on your project page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action item:&lt;/strong&gt; Set up Open Collective, GitHub Sponsors, or both. Set up your GitHub project to show how people can donate.&lt;/p&gt;

&lt;h2 id=&quot;establish-where-sponsor-logos-will-go-and-how-they-get-there&quot;&gt;Establish where sponsor logos will go and how they get there&lt;/h2&gt;

&lt;p&gt;As discussed in part 2, companies will sponsor your project to generate good publicity for them in the open source community. The most common way to deliver this to your sponsors is through placing their logos in highly visible places. If you’ve ever been to a tech conference then you’ve probably seen sponsor logos placed on the projector screen in between or at the end of talks as well as on the print schedules. As a maintainer, you are trying to deliver the same level of attention to your sponsors.&lt;/p&gt;

&lt;p&gt;Projects typically display their sponsors’ logos in at least two places:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Website -&lt;/strong&gt; if your project has a website, then displaying sponsor logos on the homepage is the best way to promote your sponsors. Some projects also choose to show these logos throughout the site, sometimes in a sidebar or a footer.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;README -&lt;/strong&gt; at a minimum, sponsor logos should be displayed on your README. Sometimes the README gets more views than a project’s website. Not every user will go to your website but most will take a look at the README. Because READMEs are often copied to other locations (such as npm for JavaScript projects), sponsor logos get the most visibility.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you’ve determined where the sponsor logos will go, the next step is to set up some automation to update those logos. Both Open Collective and GitHub offer APIs that allow you to pull your sponsor information for this purpose. Investing in this automation early will save you the time it takes to manually track down, resize, and place logos in the correct places for each new sponsor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action item:&lt;/strong&gt; Determine where your sponsor logos will go and establish automation to update logos in those locations.&lt;/p&gt;

&lt;h2 id=&quot;explain-how-the-funds-are-or-will-be-used&quot;&gt;Explain how the funds are (or will be) used&lt;/h2&gt;

&lt;p&gt;If you’ve ever driven along a highway and seen a sign next to some construction that reads “Your tax dollars at work,” you’ll know the importance of letting sponsors know how their money is being spent. Similarly, another consideration for accepting sponsorships is how you will spend the funds. It’s important to think this through before you accept your first sponsorship because companies will want to know your plans for the money.&lt;/p&gt;

&lt;p&gt;This is where having a roadmap (discussed in part 2) helps. It’s an easy story to tell that money collected will go towards fulfilling the plans on the roadmap. While it’s not easy to estimate how much it costs to implement a feature, it can help to think about how many hours it will take. Then, multiply that by some hourly rate you’d feel comfortable working at and use that as the cost to implement that feature. That way, you can put together a funding goal that companies can contribute towards. For instance, let’s say that you estimated the cost to implement your 12-month roadmap is $20,000 USD. You can then set up your goal on Open Collective and GitHub Sponsors and explain how much of your funding goal needs to be reached to implement which parts of your roadmap.&lt;/p&gt;

&lt;p&gt;The next step after a target funding goal is to describe how the money will actually be spent. Not every maintainer wants to work 40 hours a week on their project, nor is that required to receive sponsorships. ESLint, for example, started receiving sponsorships in 2019 and has never (up to the time of my writing) had a single full-time maintainer. Other projects like Babel and Webpack do have full-time maintainers. How you plan to spend your sponsorship money is up to you, but it is helpful to explain that to potential sponsors.&lt;/p&gt;

&lt;p&gt;The best place to do this is right alongside your call for donations, but you can also do it in a blog post or a paragraph on your README. Just make sure this information is readily available for any potential sponsors who come along. Here are some examples:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Donations will be used to pay the maintainer(s) with an eventual goal of working full time on the project.&lt;/li&gt;
  &lt;li&gt;Donations will be used to pay the maintainer(s) with an eventual goal of working one eight-hour day each week on the project.&lt;/li&gt;
  &lt;li&gt;Donations will be used to pay all contributors for their contributions.&lt;/li&gt;
  &lt;li&gt;Donations will be used to fund community events, T-shirts, promotional materials, and speaking engagements.&lt;/li&gt;
  &lt;li&gt;Donations will be used to implement a specific feature or hire outside help.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After you receive your first sponsorships, be sure to update everyone on how the money was actually spent. This can also be in the form of a blog post or a message to your sponsors (both Open Collective and GitHub Sponsors allow you to send messages directly to your sponsors). In general, it’s beneficial to post this information publicly because it can also send a signal to potential new sponsors about how your project is operating with the money it has already collected.&lt;/p&gt;

&lt;p&gt;If you are using Open Collective to disburse funds, all transactions are visible to the public, which is a great way to show how the funds are being used. This is why you may want to consider having your GitHub Sponsors funds deposited into your Open Collective account. That way, all funds can be disbursed from Open Collective in a public ledger that makes it easy for sponsors to see how the funds are being used and also allows you to easily generate reports off the transaction data.&lt;/p&gt;

&lt;p&gt;Regardless of how you decide to explain how funds are used, make sure you issue regular updates. Never give the appearance that the money is disappearing into a black hole. It’s going somewhere, so be sure to tell that story.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action item:&lt;/strong&gt; Come up with a plan for how sponsorship money will be used and publish it on your README or website in addition to Open Collective and GitHub Sponsors.&lt;/p&gt;

&lt;h2 id=&quot;be-prepared-to-say-no&quot;&gt;Be prepared to say “no”&lt;/h2&gt;

&lt;p&gt;An underrated part of accepting sponsorships is to choose your sponsors wisely. While you may think any company that is willing to give you money is a sponsor you’d want, think again. Some companies will sponsor your project because they use it internally and want to ensure its success, but others will see sponsorship a lot like buying an advertisement: it’s just publicity in exchange for money, and they don’t care what happens to the project.&lt;/p&gt;

&lt;p&gt;Open Collective, in particular, has a problem with less-than-reputable companies signing up to sponsor projects just for publicity. While Open Collective is putting more controls in place to limit these companies’ ability to donate to projects, you’ll still need to monitor incoming sponsorships to ensure that these are companies you want to do business with.&lt;/p&gt;

&lt;p&gt;Aside from ensuring your sponsors align with your values, you also need to make sure you’re accepting sponsorships from companies that other companies want to have their logo next to. If your first couple of sponsor logos are for overseas online casinos, is that something Google or Microsoft or Amazon would want their logos next to? Of course not.&lt;/p&gt;

&lt;p&gt;Your first sponsorships, in particular, may have a disproportionate impact on how potential sponsors see your project. If your first logo is Microsoft, that will encourage other companies to sponsor your project; if your first logo is Joe’s Tackle and Sports Book Shop, that’s not going to entice new sponsors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action item:&lt;/strong&gt; Create a sponsorship policy that lists out what types of companies you will (or won’t) accept sponsorships from. Post this publicly so everyone is aware, though keep in mind that few companies will read this upfront. It’s just helpful to be able to point them to your policy when questions arise.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Early on in ESLint’s fundraising, several companies told us they would sponsor the project if we signed up for Open Collective. Not all companies will be that forward with projects they want to sponsor. That’s where the suggestions in this post come in. By using trusted third parties to collect donations, display sponsor logos, explaining how you’ll use the funds, and being careful about which companies you accept sponsorships from, your project will be more attractive to companies. Meeting companies where they are, especially if they are already sponsoring other open source projects, is the best way to get started.&lt;/p&gt;

&lt;p&gt;I hope you’ve enjoyed this three-part series about making your open source project sponsor-ready. I wish your project a lot of success and sponsors.&lt;/p&gt;

&lt;p&gt;&lt;i&gt;Thanks to &lt;a href=&quot;https://fredkschott.com/&quot;&gt;Fred Schott&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/benjam&quot;&gt;Ben Nickolls&lt;/a&gt; for reviewing early drafts of this post.&lt;/i&gt;&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://humanwhocodes.com/blog/2021/12/making-open-source-project-sponsor-ready-companies-trust/&quot;&gt;Making your open source project sponsor-ready, Part 1: Companies and trust&lt;/a&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://humanwhocodes.com/blog/2021/12/making-open-source-project-sponsor-ready-project-hygiene/&quot;&gt;Making your open source project sponsor-ready, Part 2: Project hygiene&lt;/a&gt; &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://opencollective.com&quot;&gt;Open Collective&lt;/a&gt; &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:4&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://github.com/sponsors&quot;&gt;GitHub Sponsors&lt;/a&gt; &lt;a href=&quot;#fnref:4&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:5&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository&quot;&gt;GitHub - Displaying a sponsor button in your repository&lt;/a&gt; &lt;a href=&quot;#fnref:5&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Making your open source project sponsor-ready, Part 2: Project hygiene</title>
      <link>https://humanwhocodes.com/blog/2021/12/making-open-source-project-sponsor-ready-project-hygiene/</link>
      <pubDate>Tue, 21 Dec 2021 00:00:00 +0000</pubDate>
      <dc:creator>Nicholas C. Zakas</dc:creator>
      
      <category>Open Source</category>
      
      <category>Fundraising</category>
      
      <category>Sponsorship</category>
      
      
      <category>Open Source</category>
      
      <guid isPermaLink="true">https://humanwhocodes.com/blog/2021/12/making-open-source-project-sponsor-ready-project-hygiene/</guid>
      <description>In part 11 of this series, I described how companies make decisions about spending their money and why they might (or might not) sponsor an open source project. If you haven’t yet read that post, I’d suggest going back to do so now before continuing. Everything in this post builds off the topics discussed in...</description>
      <content:encoded>&lt;p&gt;In part 1&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; of this series, I described how companies make decisions about spending their money and why they might (or might not) sponsor an open source project. If you haven’t yet read that post, I’d suggest going back to do so now before continuing. Everything in this post builds off the topics discussed in part 1, the most important of which is establishing a level of trustworthiness as both a maintainer and a project.&lt;/p&gt;

&lt;p&gt;You are either building or destroying trust with everything that you and your project do online. Making sure that the trail you are leaving speaks well of both is important for attracting potential sponsorships. Fortunately, most of the steps are straightforward and, if you’ve been running your project for some time, you are likely already doing them.&lt;/p&gt;

&lt;h2 id=&quot;use-an-osi-approved-license&quot;&gt;Use an OSI-approved license&lt;/h2&gt;

&lt;p&gt;The Open Source Initiative (OSI), which is the organization that officially defined the term “open source,” has a list of approved open source licenses.&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; These licenses have been fully verified to be open source licenses, ensuring software using these licenses can be “freely used, modified, and shared.” This list is used by governments, companies, and lawyers when evaluating open source projects.&lt;/p&gt;

&lt;p&gt;Double-check to make sure you are using an OSI-approved license without any custom modifications. Using other licenses creates a legal gray area for companies, which means they need to spend time reviewing the licenses with their legal team, and that might prevent them from using the project or sponsoring it. Companies are more likely to sponsor free and open source software than they are projects with custom licenses that might send a signal that the project isn’t going to be free and open source forever. Don’t make the companies do work to validate your license.&lt;/p&gt;

&lt;p&gt;If you have already started your project with one license, don’t be afraid to change it. There are numerous projects that have changed their licenses based on feedback from their users, including JSHint&lt;sup id=&quot;fnref:3&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; (which inherited the infamous “no evil” license from JSLint) and React&lt;sup id=&quot;fnref:4&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;. Depending on how dramatic the license change, you may want to adjust your version number accordingly.&lt;/p&gt;

&lt;p&gt;If you are just starting out and unsure what license to use, I personally prefer the Apache 2.0 license&lt;sup id=&quot;fnref:5&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;. This license contains an explicit patent grant that companies tend to favor as opposed to the MIT or BSD license, neither of which mention patents in any way (which makes some companies nervous).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action item:&lt;/strong&gt; Double-check that your project is using an OSI-approved license and feature the license prominently on the README.&lt;/p&gt;

&lt;h2 id=&quot;set-up-a-code-of-conduct&quot;&gt;Set up a code of conduct&lt;/h2&gt;

&lt;p&gt;Imagine that you are in charge of open source sponsorships at a company and someone suggests a certain project. Wanting to do your due diligence, you go to the project’s GitHub page and look through recent issues and pull requests. What you see is a maintainer who is swearing and being defensive, commenters insulting code submissions, and just overall aggressive behavior by more than a few people. What message does that send about the project? Is that a recipe for attracting contributors and long-term support? Will a company want to be associated with such behavior? Of course not.&lt;/p&gt;

&lt;p&gt;How you behave as a maintainer, and to a larger extent, the behavior of those who are collaborating on your project, reflects on the project as a whole. For the best example, look no further than Linux, which for years had a culture of conflict that ultimately led to the formal adoption of a code of conduct&lt;sup id=&quot;fnref:6&quot;&gt;&lt;a href=&quot;#fn:6&quot; class=&quot;footnote&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;. Don’t wait for things to reach that point. Establishing a code of conduct early on in the life of a project makes it more appealing to both contributors and sponsors.&lt;/p&gt;

&lt;p&gt;The Contributor Covenant[^7] is a good place to start if you don’t want to do much research. Keep in mind that you are expected to follow your code of conduct as well. Make sure your behavior is above reproach while interacting with (sometimes irrational) people on GitHub. Companies absolutely do look at issues to see how things are being handled. (And you never know when a new contributor might turn into an ally in getting their company to sponsor your project.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action item:&lt;/strong&gt; Establish a code of conduct and feature it on your README and in GitHub.&lt;/p&gt;

&lt;h2 id=&quot;publish-documentation&quot;&gt;Publish documentation&lt;/h2&gt;

&lt;p&gt;A project without users is a project without sponsors, and one of the biggest barriers to open source project adoption is a lack of documentation. At a minimum, your project needs two sets of documentation:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Getting Started Guide&lt;/strong&gt; - this documentation is targeted at your project’s users and guides them through installation and setup. If there are any prerequisites to installing your project, this should also be mentioned. The guide should continue through to using the project (executing it if it’s an executable, using the API if it’s an API, etc.).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Developer Guide&lt;/strong&gt; - this documentation is targeted at people who want to contribute code. At a minimum, this should describe how to get the source code and set up a local development environment. Ideally, it also describes any commit message formatting you require, the process for accepting contributions, and anything else between writing code and submitting a pull request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Where the documentation lives is up to you. To start, it’s fine to put most of the documentation on your README. As the documentation grows, you may want to have a dedicated documentation folder in your project that the README links to. Eventually, if your project gets popular enough, having a dedicated documentation website will help your users tremendously.&lt;/p&gt;

&lt;p&gt;Remember, the more users and contributors you have, the more likely one of them will lead to a sponsorship, and documentation is a key part of attracting people to your project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action item:&lt;/strong&gt; Write your Getting Started and Developer guides. Include them in, or link to them from, your README.&lt;/p&gt;

&lt;h2 id=&quot;develop-a-roadmap-and-release-plan&quot;&gt;Develop a roadmap and release plan&lt;/h2&gt;

&lt;p&gt;The most important question people ask about a project is “What’s next?” Both users and sponsors want to know where the project is headed. For users, they want to know that the project is continuing to grow and develop; for sponsors, they want to know what their money is paying for. To answer both groups, it helps to develop a roadmap and a release plan.&lt;/p&gt;

&lt;p&gt;A roadmap is simply a list of the work you plan to do within a specific time period. Your roadmap can be for any length of time you think is reasonable, but typically they are in the range of three months to one year. On your roadmap, list out anything that you think is important for people to know is coming. Features are an obvious addition to any roadmap, but you can also add bug fixes, documentation updates, integration tasks, and more. People just want to know how you plan on spending your time to improve the project going forward, and a roadmap lays this out for easy reference.&lt;/p&gt;

&lt;p&gt;A release plan is a companion to a roadmap and establishes a schedule for when to expect releases. A lot of smaller open source projects have a “whenever I feel like it” release plan that, unfortunately, doesn’t work as the project grows. Larger projects, and especially those that would like sponsorship, are well-served to come up with a release plan so users know when changes are coming. For example, Node.js has a well-defined release plan[^8] based on six-month intervals; ESLint does minor releases every two weeks. You can choose a schedule that works best for you. The most important thing is that this schedule is communicated so you can answer the question “When will this be released?”&lt;/p&gt;

&lt;p&gt;When you do a release, be sure to explain how it relates to your roadmap. The easiest way to do this is to generate release notes from your commit history. GitHub can generate these release notes for you[^9] based on your pull request labels. Release notes are an important part of the release process so users and sponsors can see what changes are made.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action item:&lt;/strong&gt; Come up with a roadmap for the next year and establish a release plan for your project. Document both, either on your README or with your other documentation.&lt;/p&gt;

&lt;h2 id=&quot;be-responsive-on-github&quot;&gt;Be responsive on GitHub&lt;/h2&gt;

&lt;p&gt;No one starts an open source project dreaming of the fun they’ll have triaging issues and pull requests, yet maintaining a project often means spending a significant amount of time doing so.&lt;/p&gt;

&lt;p&gt;Your level of responsiveness on GitHub speaks volumes about your level of commitment to the project. Few things say “abandoned project” more than GitHub issues and pull requests that don’t get responses from maintainers.&lt;/p&gt;

&lt;p&gt;Does that mean you need to rush to respond to every issue or pull request? No. Those will come in 24 hours a day, 7 days a week, and it’s not reasonable to expect maintainers to be on call for every question or suggestion. However, you should have a regular schedule for responding to issues and pull requests. Maybe you respond just once or twice a week, and that’s fine. It’s more important to come up with a schedule you can keep than it is to respond immediately. If you know you won’t be able to respond within a week, then you might want to note this on your README or with an automated message on your issues and pull requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action item:&lt;/strong&gt; Come up with a schedule for dealing with incoming issues and pull requests. Optionally, publish this schedule in your README or with an automated message.&lt;/p&gt;

&lt;h2 id=&quot;establish-support-channels&quot;&gt;Establish support channels&lt;/h2&gt;

&lt;p&gt;Even though you’ll spend time writing documentation, people will still have questions about your project, and for that, you’ll need to establish one or more support channels. You probably don’t want people opening issues for every question they have, so where would you like those questions to go? You can choose whatever will work best for you. Some popular options include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;GitHub Discussions&lt;/li&gt;
  &lt;li&gt;Discord or Slack&lt;/li&gt;
  &lt;li&gt;A mailing list&lt;/li&gt;
  &lt;li&gt;A forum&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s reasonable to just have one support channel for your project based on your preferred way of communicating, or you can establish multiple support channels if you have the team to respond. The important thing is that users (and sponsors) have a way to contact you that doesn’t involve opening an issue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action item:&lt;/strong&gt; Set up at least one support channel for your users and mention it on your README.&lt;/p&gt;

&lt;h2 id=&quot;establish-communication-channels&quot;&gt;Establish communication channels&lt;/h2&gt;

&lt;p&gt;Along similar lines, it’s important for you to have a way to communicate with your users directly. When there is a new release, or a new team member joins, or a new sponsor signs up, you need a way to let your project’s community know about these changes. Once again, the channel(s) you choose should fit with how you prefer to communicate. Some popular options are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Blog&lt;/li&gt;
  &lt;li&gt;Twitter account&lt;/li&gt;
  &lt;li&gt;Announcements channel in Slack/Discord&lt;/li&gt;
  &lt;li&gt;Mailing list&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whichever channels you choose to use, be sure that they are listed in your README (and website, if you have one). It’s important for users to know where they can go to get the latest information about the project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action item:&lt;/strong&gt; Establish one or more communication channels and add them to your README and website.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this post, I’ve described some basic open source hygiene that all projects, regardless of sponsorship status, should follow. Doing all of these things won’t automatically result in sponsorships, but they will set your project up for success when seeking sponsors. High-quality, valuable projects are the ones that companies are more likely to sponsor, and following the suggestions in this post helps set your project apart from a hobby project that a maintainer might lose interest in after a few weeks. The more professional your project appears, the easier it will be to find sponsors.&lt;/p&gt;

&lt;p&gt;Now that your project is practicing good hygiene, it’s time to prepare for the actual sponsorships. I’ll cover that topic in my next post.&lt;/p&gt;

&lt;p&gt;&lt;i&gt;Thanks to &lt;a href=&quot;https://fredkschott.com/&quot;&gt;Fred Schott&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/benjam&quot;&gt;Ben Nickolls&lt;/a&gt; for reviewing early drafts of this post.&lt;/i&gt;&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;p&gt;)
[^7]: &lt;a href=&quot;https://www.contributor-covenant.org/&quot;&gt;Contributor Covenant&lt;/a&gt;
[^8]: &lt;a href=&quot;https://nodejs.org/en/about/releases/&quot;&gt;Node.js - Releases&lt;/a&gt;
[^9]: &lt;a href=&quot;https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes&quot;&gt;GitHub - Automatically generated release notes&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://humanwhocodes.com/blog/2021/12/making-open-source-project-sponsor-ready-companies-trust/&quot;&gt;Making your open source project sponsor-ready, Part 1: Companies and trust&lt;/a&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://opensource.org/licenses/&quot;&gt;Open Source Initiative Approved Licenses&lt;/a&gt; &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://jshint.com/relicensing-2020/&quot;&gt;JSHint - 2020 Relicensing&lt;/a&gt; &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:4&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://engineering.fb.com/2017/09/22/web/relicensing-react-jest-flow-and-immutable-js/&quot;&gt;Relicensing React, Jest, Flow, and Immutable.js&lt;/a&gt; &lt;a href=&quot;#fnref:4&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:5&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://www.apache.org/licenses/LICENSE-2.0.html&quot;&gt;Apache License Version 2.0&lt;/a&gt; &lt;a href=&quot;#fnref:5&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:6&quot;&gt;
      &lt;p&gt;[Linus Torvalds apologizes for years of being a jerk, takes time off to learn empathy](https://arstechnica.com/gadgets/2018/09/linus-torvalds-apologizes-for-years-of-being-a-jerk-takes-time-off-to-learn-empathy/ &lt;a href=&quot;#fnref:6&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Making your open source project sponsor-ready, Part 1: Companies and trust</title>
      <link>https://humanwhocodes.com/blog/2021/12/making-open-source-project-sponsor-ready-companies-trust/</link>
      <pubDate>Tue, 14 Dec 2021 00:00:00 +0000</pubDate>
      <dc:creator>Nicholas C. Zakas</dc:creator>
      
      <category>Open Source</category>
      
      <category>Fundraising</category>
      
      <category>Sponsorship</category>
      
      
      <category>Open Source</category>
      
      <guid isPermaLink="true">https://humanwhocodes.com/blog/2021/12/making-open-source-project-sponsor-ready-companies-trust/</guid>
      <description>Early on, it was a battle to get sponsorship for open source projects. What used to require phone calls and drawn-out discussions has now been streamlined thanks to efforts like Open Collective1 and GitHub Sponsors2. Companies and individuals can now know if a project accepts donations just by looking at the project page on GitHub,...</description>
      <content:encoded>&lt;p&gt;Early on, it was a battle to get sponsorship for open source projects. What used to require phone calls and drawn-out discussions has now been streamlined thanks to efforts like Open Collective&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; and GitHub Sponsors&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. Companies and individuals can now know if a project accepts donations just by looking at the project page on GitHub, and if you’re lucky, they’ll sign up without you needing to do anything. But is that really all you need to do? Just set up an Open Collective or GitHub Sponsors page and just watch the money roll in? Not quite. There’s a lot you can do to make your project attractive to potential sponsors.&lt;/p&gt;

&lt;p&gt;While it’s possible to bring in a decent amount of money through individual sponsorships, the real path to open source sustainability is to get larger donations from the companies that depend on your project. Getting $5 to $10 each month from a bunch of individuals is nice, but not as nice as getting $1,000 each month from a bunch of companies. For that reason, this post focuses on making your project attractive to companies, and to do that, you first need to understand why a company might want to sponsor your project.&lt;/p&gt;

&lt;h2 id=&quot;why-companies-sponsor-open-source-projects-or-dont&quot;&gt;Why companies sponsor open source projects (or don’t)&lt;/h2&gt;

&lt;p&gt;There’s a fairly common mantra online about why companies should sponsor open source projects: because it’s the right thing to do. Many companies, especially startups, are able to get started because they are building on top of free and open source software. It would be difficult or impossible for companies to compete without the availability of zero-cost foundational software. Once they are profitable, it makes sense for companies to contribute back to the software that helped them become successful. Right?&lt;/p&gt;

&lt;p&gt;The harsh reality is that companies don’t operate as charities. Their goal is to bring in as much money as possible, and that doesn’t include giving away money “because it’s the right thing to do.” For some, this is frustrating, but if you can move past the perceived unfairness of it all, you can come up with a strategy that works. Just because companies won’t sponsor your project out of gratitude  for your work doesn’t mean they won’t sponsor your project. You just need to stop and think about the things that companies do spend money on regularly and then align your offering with those things.&lt;/p&gt;

&lt;p&gt;And there are two things that companies readily spend money on: helping the business and publicity.&lt;/p&gt;

&lt;h3 id=&quot;companies-invest-in-things-that-help-the-business&quot;&gt;Companies invest in things that help the business&lt;/h3&gt;

&lt;p&gt;As discussed in my previous post&lt;sup id=&quot;fnref:3&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;, companies generally spend money on things that accomplish one of three goals:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Saves time&lt;/li&gt;
  &lt;li&gt;Saves money&lt;/li&gt;
  &lt;li&gt;Generates more money&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Open source projects must fulfill at least one of these goals to make it attractive to sponsor. Your project might save the company time by providing code that they would otherwise have to build and maintain themselves; your project might save them money because otherwise they’d need to buy a commercial product; your project might generate them money if it is user-facing. So the first thing to understand is what value your project is providing to companies.&lt;/p&gt;

&lt;p&gt;In the business world, this is referred to as your &lt;em&gt;value proposition&lt;/em&gt;. It’s often a one-liner that tells everyone exactly why your product is valuable. As an example, if you visit ESLint’s website, you’ll see this phrase featured prominently: “Find and fix problems in your JavaScript code.” That’s it. Simple, easy-to-understand. If you write JavaScript code, then ESLint is helpful for you. It helps by catching problems you might miss, and problems cost your company time and money; therefore, ESLint saves your company time and money.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action items:&lt;/strong&gt; Define the value proposition for your project and feature it prominently: on your README, on your website, on your social media accounts, etc. Make it a simple one-line sentence that can help explain how it saves companies time or money, or helps to generate more money.&lt;/p&gt;

&lt;h3 id=&quot;companies-pay-for-good-publicity&quot;&gt;Companies pay for good publicity&lt;/h3&gt;

&lt;p&gt;Companies are willing to pay for achieving their goals, but there is another consideration: will the sponsorship reflect well on the company? All companies care about their image in the marketplace and will not spend their money on anything that reflects badly on them. Think of the commercials you see while watching TV. Companies are buying advertising spots during specific shows because they want their brand associated with the show. Why? To generate more money by reaching the fans of that show. The opposite is also true: companies pull their ads when it becomes a risk to their reputation.&lt;/p&gt;

&lt;p&gt;Make sure to have a website in addition to your README, as open source projects frequently promote their sponsors through logo placement on both. Companies need to be okay with having their logo in these spots, which means they need to be okay with the association between their company and your project. As a simple example, if you name your open source project “hotGirlXxXparse,” it’s doubtful that a company will want their brand associated with your project.&lt;/p&gt;

&lt;p&gt;The ideal case is that the company gets good publicity for supporting your project. Companies care about publicity like this because it helps to attract new hires and retain their existing developers (similar to sponsoring tech conferences). There is a lot of enthusiasm for open source projects in the tech community, and publicly thanking a company for sponsoring your project helps to build their brand among the community. While companies may not expect much of an impact on their brand from sponsoring a project, it can result in more name recognition and even an increase in candidates for open jobs.&lt;/p&gt;

&lt;p&gt;Overall, your job is to make your project into something that companies would be proud to have their brand associated with.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action items:&lt;/strong&gt; Ensure you’ve named your project appropriately and have a professional-looking website with spaces for company logos. Make sure the website is some place a company would be excited to be displayed.&lt;/p&gt;

&lt;h2 id=&quot;the-importance-of-building-trust-with-companies&quot;&gt;The importance of building trust with companies&lt;/h2&gt;

&lt;p&gt;So you have defined your value proposition, picked a good name, and have a nice website up and running. The next step is just as important: how to signal to companies that you are trustworthy. A sponsorship is a business agreement, and you are a partner to that agreement. In order to do business with you, someone at the company must trust you. Companies do not do business with people or other companies they don’t trust.&lt;/p&gt;

&lt;p&gt;Startups are intimately familiar with this problem. How do you get customers when you have no track record and no other customers? How do they know you’ll still be around in a year? How do they know you won’t just take their money and run? With open source projects the problem is even more complicated because there often isn’t a company backing it up (if there is, they might not need sponsorship, after all).&lt;/p&gt;

&lt;p&gt;Your job from this point forward is to do everything you can to appear as trustworthy as you can. Remember, you are trying to convince a for-profit business to give you, some person who wrote some software on the weekend, a significant amount of money. This is no small task. You need to think of yourself more like a business than an individual and spend time building your reputation.&lt;/p&gt;

&lt;p&gt;In my next post, I’ll discuss some concrete ways to build the trust of both you and your project.&lt;/p&gt;

&lt;p&gt;&lt;i&gt;Thanks to &lt;a href=&quot;https://fredkschott.com/&quot;&gt;Fred Schott&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/benjam&quot;&gt;Ben Nickolls&lt;/a&gt; for reviewing early drafts of this post.&lt;/i&gt;&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://opencollective.com&quot;&gt;Open Collective&lt;/a&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://github.com/sponsors&quot;&gt;GitHub Sponsors&lt;/a&gt; &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://humanwhocodes.com/blog/2021/05/talk-to-your-company-sponsoring-open-source/&quot;&gt;How to talk to your company about sponsoring an open source project&lt;/a&gt; &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content:encoded>
    </item>
    
    <item>
      <title>How to talk to your company about sponsoring an open source project</title>
      <link>https://humanwhocodes.com/blog/2021/05/talk-to-your-company-sponsoring-open-source/</link>
      <pubDate>Tue, 18 May 2021 00:00:00 +0000</pubDate>
      <dc:creator>Nicholas C. Zakas</dc:creator>
      
      <category>Open Source</category>
      
      <category>Fundraising</category>
      
      
      <category>Open Source</category>
      
      <guid isPermaLink="true">https://humanwhocodes.com/blog/2021/05/talk-to-your-company-sponsoring-open-source/</guid>
      <description>Open source sustainability is a topic that is just starting to get the attention that it deserves. So much of the technology sector is run on software that can be used for free without any further obligation. However, as companies profit from using this software for free, the maintainers of the software often struggle to...</description>
      <content:encoded>&lt;p&gt;Open source sustainability is a topic that is just starting to get the attention that it deserves. So much of the technology sector is run on software that can be used for free without any further obligation. However, as companies profit from using this software for free, the maintainers of the software often struggle to find enough time to work on the software while making enough money to survive. Many are forced to work full-time jobs to subsidize the development of these open source projects, or else try to work full-time on the project while making less than they would in the industry. While donations from individuals help, the only real way to make open source sustainable for maintainers is for the companies who reap the outsize benefits of the software to donate towards their maintenance.&lt;/p&gt;

&lt;p&gt;This post contains everything I know about convincing a company to support open source projects. I’ve worked with many companies to donate to ESLint, the open source project that I’ve founded, and in doing so, I’ve learned a lot about how these efforts are organized. My hope is that this post will serve as a guide to help others convince their companies to donate to the open source projects that support their businesses.&lt;/p&gt;

&lt;h2 id=&quot;getting-your-story-straight&quot;&gt;Getting your story straight&lt;/h2&gt;

&lt;p&gt;Before you start talking to people about sponsoring an open source project, it’s important to take some time to craft the story you’ll tell them. A lot of developers, with their hearts in the right place, tell a story that donating to an open source project is “the right thing to do.” While that might actually be true, most companies don’t operate on the basis of altruism. Companies don’t exist to give away their hard-earned money; they exist to make more money. If you want to convince your company to donate money to an open source project, you’re going to have to make the case that doing so actually benefits the company (outside of warm fuzzy feelings).&lt;/p&gt;

&lt;p&gt;Generally speaking, a company will spend money on something that accomplishes any of these three goals:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Saves time&lt;/li&gt;
  &lt;li&gt;Saves money&lt;/li&gt;
  &lt;li&gt;Generates more money&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your job is to make the case that the open source project helps achieve one or more of these goals. In most cases, you can argue that the project saves you time because it prevents you from needing to implement the functionality yourself. In some cases, you can argue that the project bootstraps common tasks that would otherwise take you weeks or months to accomplish. If the project is a code quality tool like ESLint then you can argue that it saves you time by catching bugs or other quality issues before they happen. Really take the time to think through how the project is benefitting your workflow.&lt;/p&gt;

&lt;p&gt;Once you’ve tied the open source project to at least one of the three goals, imagine what would happen if the project was no longer maintained. How long does it takes before that absence starts to work against the three goals? How much will it slow you down or cost you money if you couldn’t use the project anymore? That is why donating to the project is valuable: your workflow is dependent on this project and removing the project now would cause harm, either through time or money. Therefore, it makes sense to donate to the project in order to maintain the advantage you’ve already realized for the foreseeable future.&lt;/p&gt;

&lt;h3 id=&quot;crafting-the-presentation&quot;&gt;Crafting the presentation&lt;/h3&gt;

&lt;p&gt;After putting together a good story of why it makes sense to donate to the project, the next step is to present that story to the correct person in your organization. It makes sense to start with your manager to get their support, though you may need to present the same information multiple times in order to get approval. The format of this presentation will be dependent upon the norms of the company, so you should be prepared to put together an email, a PowerPoint presentation, or an explainer, depending on your company’s preferences.&lt;/p&gt;

&lt;p&gt;Regardless of the format, your presentation should answer the following questions for your audience:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;What does the project do?&lt;/strong&gt; What 2-3 sentences describe the purpose of the project? Assume your audience doesn’t know anything about it and introduce both the problem the project solves and what the solution looks like.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Where is the project used in your company?&lt;/strong&gt; Is it used in a continuous integration environment? In your text editor? Is it customer facing (like a UI framework)? This gives context to your audience about how integrated the project is in your current workflow. It’s likely your audience may not even be aware the project is in use.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;What value do you get from using the project?&lt;/strong&gt; Does it lead to fewer bugs? Faster feature development? Does it generate more revenue? Again, assume your audience knows nothing about the project and how you’re using it. Remember to include at least one of the three goals mentioned earlier in this post.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Who receives the money?&lt;/strong&gt; When you donate the money, where does it end up? In a foundation? In an individual’s bank account? You’ll have better luck getting a donation to a project if the project is set up on a reputable site like Open Collective&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; or GitHub Sponsors&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. It’s difficult to convince your company to send a large amount of money to a PayPal email address. If the project isn’t already on Open Collective or GitHub Sponsors, it’s worth reaching out to the maintainers to ask them about this.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;How does the project use the money it receives?&lt;/strong&gt; Are they paying maintainers? Do they use money for infrastructure? Again, you can’t argue that it’s “nice” to pay maintainers of a project. Be explicit about how the money is used. In many cases, it will go directly to pay the maintainers, so that’s important to call out. It can help to mention if the maintainer doesn’t otherwise have a salary, as it lends to the importance of paying the maintainers to ensure the project will continue to be maintained.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;What does the company get in return for the donation?&lt;/strong&gt; A logo on the README or website? Is there a priority support channel? Make sure there is some direct benefit for the sponsorship. Companies will be looking for some sort of recognition of their donation (explained later in this post). If the project doesn’t offer anything in return, reach out to the maintainers and ask at least for a sponsorship notice in the README or website. Many maintainers will be happy to do that and it gives you a better argument.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;What commercial alternatives are available and what do they charge?&lt;/strong&gt; Would a paid alternative cost more or less than donating to support the open source project? This doesn’t apply to all projects, but if there is a commercial alternative, this can be a powerful part of your story because you can tie actual dollar amounts to the value of the project. If a commercial solution costs $10,000 and you’re asking the company to donate $1,000 to the open source version, that comes off looking like a big cost savings to the company.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;What other companies are sponsoring the project?&lt;/strong&gt; Are any big companies sponsoring the project? If you want to donate to a project that already has some corporate sponsors, it can help to call those out. A project that is sponsored by a well-known company such as Google or Facebook creates social proof that it is not just okay but expected to sponsor this project. As an added bonus, potentially getting your company’s logo next to Google or Facebook’s logo could be a selling point. If there aren’t any other sponsors, it’s best not to bring that up unless asked directly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pulling all of this information together can take some time, but ultimately, these are the details you will need to convince your company to sponsor an open source project.&lt;/p&gt;

&lt;p&gt;Hopefully, you will get approval for sponsoring a project not long after making the presentation. Your work is far from over, though. To move forward, you need to understand a little bit about how companies manage their money, and in doing so, where you can find the money to sponsor a project.&lt;/p&gt;

&lt;h2 id=&quot;finding-the-money&quot;&gt;Finding the money&lt;/h2&gt;

&lt;p&gt;Companies generally work on budgets at two levels: yearly and quarterly. The yearly budget for the next year is set towards the end of the fourth quarter of the current year and is typically when the top-level budget items (sometimes called &lt;em&gt;cost centers&lt;/em&gt;) are established. You can think of these as defining the row labels in a spreadsheet – these are the categories under which funds are assigned. The quarterly budget for the next quarter is established towards the end of the current quarter, and typically you cannot add budget items during the quarterly process, you can only adjust the amounts assigned to each item. This is important to understand because it is unlikely that you’ll be able to get funding for an open source project within three months. You’ll need to get the request into one of the windows for the yearly or quarterly budget planning process, and sometimes that can take several months.&lt;/p&gt;

&lt;p&gt;Because getting a budget item added is difficult and make take a year, it’s usually easier to work within the existing budget items to find funds that can be used for open source. Here are some budget items that are typically used to sponsor open source projects:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Dedicated Open Source Fund&lt;/li&gt;
  &lt;li&gt;IT/Software&lt;/li&gt;
  &lt;li&gt;Marketing&lt;/li&gt;
  &lt;li&gt;Recruiting&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each of these budget items have different reasons why they might be appropriate, so it’s helpful to dig in to the details.&lt;/p&gt;

&lt;h3 id=&quot;dedicated-open-source-fund&quot;&gt;Dedicated Open Source Fund&lt;/h3&gt;

&lt;p&gt;Obviously, if your company has a dedicated open source fund, then that is where the money should come from. This is money that has already been set aside from the company to sponsor open source projects and there is likely more money in there than is being used at the moment. It may be as simple as filling out a form or emailing the right person to get a new project considered for sponsorship. This is a good sign that your company is a leader when it comes to open source sustainability and the process for devoting funds to a project is already established.&lt;/p&gt;

&lt;p&gt;Your ultimate goal should be to get a dedicated open source fund budget item established at your company because that is the only way to ensure that (or at least increase the likelihood that) the funds will actually be used for open source sponsorships. Whenever you are dipping into other budget items to sponsor open source, it’s possible for those funds to be reallocated to something else each quarter.&lt;/p&gt;

&lt;h3 id=&quot;itsoftware&quot;&gt;IT/Software&lt;/h3&gt;

&lt;p&gt;Any modern company runs on a large amount of software, whether that be Gmail, GitHub, AWS, Salesforce, or any number of other software-as-a-service products. As such, there is usually a dedicated budget for this type of software, and once again, there is usually some extra dedicated for unanticipated software needs during the quarter. Because open source software is, afterall, software that your company is relying on, you can make a strong argument that funds in this budget item can be used to sponsor a project.&lt;/p&gt;

&lt;p&gt;If your company doesn’t already have a dedicated open source fund, looking at the IT/Software budget item is a good place to start. Your manager or director should have direct insight into whether this is possible because they likely receive a specific amount each quarter. The amount you receive is likely to go on in the future unless you stop using the project you want to sponsor.&lt;/p&gt;

&lt;h3 id=&quot;marketing&quot;&gt;Marketing&lt;/h3&gt;

&lt;p&gt;One of the underrated aspects of sponsoring open source projects is just how much good publicity it brings to companies. Companies spend a lot of money on something called &lt;em&gt;brand awareness&lt;/em&gt;, which is basically whether or not people know the company in the industry. Google, Facebook, and Apple all have great brand awareness: you know what they do and how they benefit you. The result is more sales, and also, an easier time recruiting new employees. So how does sponsoring an open source project increase brand awareness?&lt;/p&gt;

&lt;p&gt;Companies regularly spend thousands of dollars sponsoring tech conferences to get their logo in front of 1,000 people for three days. Many open source projects offer logo placement on their READMEs and websites, which can easily reach more eyes even for projects that aren’t well-known. Plus, open source projects are good at promoting their sponsors. ESLint, for example, tweets out a public thank you and will also publish a blog post touting the sponsorship. This type of publicity is a big win for companies, as it is a grassroots effort that shows the company believes in supporting its developers and tools they use.&lt;/p&gt;

&lt;p&gt;Getting access to these funds often takes a bit of digging. As always, you can start with your manager, but you’ll ultimately need to find someone who works in marketing to talk with and explain the opportunity. Just be careful: the marketing department thinks in terms of “campaigns,” which are fixed-length periods of spending a certain amount of money to get a certain result. In short, sometimes this ends up being equated to an ad buy and you’ll need to discuss renewing the commitment after some period of time.&lt;/p&gt;

&lt;h3 id=&quot;recruiting&quot;&gt;Recruiting&lt;/h3&gt;

&lt;p&gt;As previously mentioned, brand awareness is important to the company for a number of reasons, and one of those is the ability to attract and hire new employees. Even though a good amount of these efforts fall under marketing, some are recruiting-specific. Recruiting departments regularly spend money on sourcers (those folks who email you directly asking if you’d like to apply), software like LinkedIn and Indeed, booths at college job fairs and tech conferences, and swag like t-shirts, cups, and hats. All of this is to say: recruiting spends a lot of money to try and attract job candidates.&lt;/p&gt;

&lt;p&gt;Sponsoring an open source project is a good way to attract job candidates. As long as the project does a decent job at promoting its sponsors (and most of them do), your company is likely to get in front of exactly the people who they are looking to hire. Developers know what open source projects they prefer to use, and if they can find a company where they can no only use it but also contribute to it, that’s a big selling point for a job applicant.&lt;/p&gt;

&lt;p&gt;I put this as the last option of the four because it tends to be the hardest to get and is also prone to strict timelines after which you’ll have to discuss renewing the contribution. Your manager likely has someone who they work with in recruiting regularly, and so that person is a good place to start the conversation. Going through recruiting will likely take longer than going through other budget items, but it is an option nonetheless.&lt;/p&gt;

&lt;h2 id=&quot;sponsorship-approaches&quot;&gt;Sponsorship approaches&lt;/h2&gt;

&lt;p&gt;At this point, you have hopefully identified where in the budget the money for open source sponsorships will come from. The next step is to determine what type of sponsorship your company will make. As with finding money in the budget, there are many approaches that companies take when sponsoring open source projects. You’ll need to find the solution that best matches your company’s culture and approach to open source. Fortunately, there are many options to choose from.&lt;/p&gt;

&lt;h3 id=&quot;monthly-contributions&quot;&gt;Monthly contributions&lt;/h3&gt;

&lt;p&gt;Open source projects generally prefer monthly contributions to keep the project funded. If at all possible, getting your company to commit to donating a monthly amount for 12 months gives the project the most financial stability. There are ongoing costs to maintaining an open source project and having a reliable amount of money coming in every month helps the maintainers to plan ahead of time. Platforms like Open Collective and GitHub Sponsors allow you to pledge contributions both monthly and yearly. When specifying a yearly donation, the amount can be set up to be evenly split among the upcoming 12 months.&lt;/p&gt;

&lt;p&gt;This option works well if your open source donations are coming from your IT/Software budget item, as most software is billed on either a monthly or yearly basis.&lt;/p&gt;

&lt;h3 id=&quot;open-source-grants&quot;&gt;Open source grants&lt;/h3&gt;

&lt;p&gt;For some companies, the idea of donating monthly will not be the best option. It feels like a commitment without an end, and that can be a hard sell. Another approach is to create an open source grant, which is a one-time donation to a project based on some set of criteria. It’s up to the individual companies to determine what those criteria are, and there is no one right way to do it.&lt;/p&gt;

&lt;p&gt;Companies like Indeed&lt;sup id=&quot;fnref:3&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;, Microsoft&lt;sup id=&quot;fnref:4&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;, and Salesforce&lt;sup id=&quot;fnref:5&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; have set up Free and Open Source Software (FOSS) Contributor Funds, where the employees who have contributed to open source projects during the quarter are then allowed to nominate and vote for open source projects to receive a one-time donation (typically $10,000). FOSS Contributor Funds tend to be popular within their host companies because they allow the developers themselves to determine which projects are considered for a donation.&lt;/p&gt;

&lt;p&gt;Some companies also provide open source grants that must be applied for. Mozilla&lt;sup id=&quot;fnref:6&quot;&gt;&lt;a href=&quot;#fn:6&quot; class=&quot;footnote&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;, the Sloan Foundation&lt;sup id=&quot;fnref:7&quot;&gt;&lt;a href=&quot;#fn:7&quot; class=&quot;footnote&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;, Google&lt;sup id=&quot;fnref:8&quot;&gt;&lt;a href=&quot;#fn:8&quot; class=&quot;footnote&quot;&gt;8&lt;/a&gt;&lt;/sup&gt; &lt;sup id=&quot;fnref:9&quot;&gt;&lt;a href=&quot;#fn:9&quot; class=&quot;footnote&quot;&gt;9&lt;/a&gt;&lt;/sup&gt;, and Comcast&lt;sup id=&quot;fnref:10&quot;&gt;&lt;a href=&quot;#fn:10&quot; class=&quot;footnote&quot;&gt;10&lt;/a&gt;&lt;/sup&gt; have grant programs where anyone is welcome to apply for a one-time grant. The decisions are typically made by a committee who is dedicated to reviewing and choosing grantees.&lt;/p&gt;

&lt;p&gt;Grants are a great way to support open source projects if your company isn’t willing or able to provide ongoing monthly support. Grants are less favored by open source projects because there is far less predictability of income, but in the end, any donations are appreciated. There is also a bit of a downside for the company because there is administrative overhead associated with a grant program.&lt;/p&gt;

&lt;p&gt;Using grants is more beneficial if you end up using marketing or recruiting budget to fund open source projects because there is a set amount and duration.&lt;/p&gt;

&lt;h3 id=&quot;employee-gift-cards&quot;&gt;Employee gift cards&lt;/h3&gt;

&lt;p&gt;Another option that is available exclusively through Open Collective is to give open source gift cards&lt;sup id=&quot;fnref:11&quot;&gt;&lt;a href=&quot;#fn:11&quot; class=&quot;footnote&quot;&gt;11&lt;/a&gt;&lt;/sup&gt; to employees. The gift cards can then be used to fund any project that is on Open Collective. With this approach, the company would transfer some amount of money to Open Collective in a lump sum. Gift cards would then be issued for any number of employees, with each gift card representing a set amount of that lump sum. The employees are then free to use those gift cards to donate directly to any project they want, so long as it is capable of receiving funds through Open Collective.&lt;/p&gt;

&lt;p&gt;This can be a nice option for companies who can’t commit to a monthly or yearly donation and who don’t want the overhead of running a grant program.&lt;/p&gt;

&lt;h2 id=&quot;other-things-to-keep-in-mind&quot;&gt;Other things to keep in mind&lt;/h2&gt;

&lt;p&gt;There are just a few other notes I’d like to share as your work on convincing your company to sponsor an open source project:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Don’t expect that you can hand off this initiative to someone else. Your manager is unlikely to take this on as their own task. In every company I’ve worked with to get a donation, the process has been driven to completion by a passionate engineer or group of engineers. You will have more success with a bottom-up approach rather than expecting your manager to make time for it.&lt;/li&gt;
  &lt;li&gt;Leverage existing vendor relationships as much as possible. Getting a vendor approved at your company can require a large amount of effort on its own. Asking the company to onboard several different vendors to allow you to donate to several different projects is unlikely to succeed. Instead, focus your efforts on getting vendor approval for Open Collective and GitHub Sponsors. With Open Collective, you can set up a fund&lt;sup id=&quot;fnref:12&quot;&gt;&lt;a href=&quot;#fn:12&quot; class=&quot;footnote&quot;&gt;12&lt;/a&gt;&lt;/sup&gt; where you can transfer a lump-sum of money and then donate to individual projects from that fund; with GitHub, if your company is already paying for a GitHub organization, you can leverage that relationship by having donations appear on the monthly bill&lt;sup id=&quot;fnref:13&quot;&gt;&lt;a href=&quot;#fn:13&quot; class=&quot;footnote&quot;&gt;13&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
  &lt;li&gt;If your company already donates to an open source foundation, such as the Linux Foundation, they may not realize that those donations to not go directly to projects. For example, ESLint is part of the Open JS Foundation, however the Open JS Foundation does not provide any money directly to ESLint. Open source foundations exist to provide a legal infrastructure and technical resources to projects. These are important, but they are very different from providing funds to the projects. You may need to explain this to your company.&lt;/li&gt;
  &lt;li&gt;Be patient with your company. Especially in large companies, progress is slow and changes happen with persistence over time rather than all at once. If your company has never even considered donating to an open source project then you’ll need to provide a lot of context and get a lot of signoffs. This is completely normal. Don’t take it personally, just realize that giving away money for some unknown benefit is not high on most company’s lists of priorities.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this post, I’ve explored all of the topics I’m aware of with regards to company open source sponsorships. As I’m sure you realize if you’re read this far, it can take a lot of work to get your company started with donations to open source projects. My own experience has taught me to allow at least six months from first contact until the first donation is made, so patience is key throughout this process.&lt;/p&gt;

&lt;p&gt;Making sure you have a good story to tell, finding the budget, and the establishing the structure of the program are the three most important steps in this process. And once all of this has been established for one project, it’s easy to apply to the next project. Indeed, companies tend to increase donations after they’ve accomplished one because the infrastructure is already in place. Establishing your company’s open source sponsorship program is a fixed, upfront cost that then allows less friction for other donations going forward.&lt;/p&gt;

&lt;p&gt;My hope is that we are moving towards a time when companies, especially those large corporations, who benefit from using open source software will also start seeing open source sponsorships as part of their core business activities. It’s this software, available for free, that allows small startups to grow into massive multinational companies. It would be nice for more companies to pay it back in some way.&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://opencollective.com&quot;&gt;Open Collective&lt;/a&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://github.com/sponsors&quot;&gt;GitHub Sponsors&lt;/a&gt; &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://opensource.indeedeng.io/FOSS-Contributor-Fund/&quot;&gt;Indeed FOSS Contributor Fund&lt;/a&gt; &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:4&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://github.com/microsoft-sponsorships/microsoft-foss-fund&quot;&gt;Microsoft’s Free and Open Source Software Fund&lt;/a&gt; &lt;a href=&quot;#fnref:4&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:5&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://engineering.salesforce.com/announcing-the-first-foss-contributor-fund-recipient-60a295201497&quot;&gt;Announcing the first Salesforce FOSS Contributor Fund Recipient&lt;/a&gt; &lt;a href=&quot;#fnref:5&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:6&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://www.mozilla.org/en-US/moss/&quot;&gt;Mozilla Open Source Support Awards&lt;/a&gt; &lt;a href=&quot;#fnref:6&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:7&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://sloan.org/programs/digital-technology&quot;&gt;Sloan Foundation Digital Technology&lt;/a&gt; &lt;a href=&quot;#fnref:7&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:8&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://summerofcode.withgoogle.com/&quot;&gt;Google Summer of Code&lt;/a&gt; &lt;a href=&quot;#fnref:8&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:9&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://developers.google.com/season-of-docs&quot;&gt;Google Season of Docs&lt;/a&gt; &lt;a href=&quot;#fnref:9&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:10&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://innovationfund.comcast.com/&quot;&gt;Comcast Innovation Fund&lt;/a&gt; &lt;a href=&quot;#fnref:10&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:11&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://opencollective.com/gift-cards&quot;&gt;Open Collective Gift Cards&lt;/a&gt; &lt;a href=&quot;#fnref:11&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:12&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://docs.opencollective.com/help/financial-contributors/organizations/funds&quot;&gt;Open Collective Funds&lt;/a&gt; &lt;a href=&quot;#fnref:12&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:13&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://github.blog/2020-12-08-new-from-universe-2020-dark-mode-github-sponsors-for-companies-and-more/#companies-can-now-invest-in-open-source-with-github-sponsors&quot;&gt;Companies can now invest in open source with GitHub Sponsors&lt;/a&gt; &lt;a href=&quot;#fnref:13&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content:encoded>
    </item>
    
    <item>
      <title>The lazy-loading property pattern in JavaScript</title>
      <link>https://humanwhocodes.com/blog/2021/04/lazy-loading-property-pattern-javascript/</link>
      <pubDate>Tue, 20 Apr 2021 00:00:00 +0000</pubDate>
      <dc:creator>Nicholas C. Zakas</dc:creator>
      
      <category>JavaScript</category>
      
      <category>Design Patterns</category>
      
      
      <category>Programming</category>
      
      <guid isPermaLink="true">https://humanwhocodes.com/blog/2021/04/lazy-loading-property-pattern-javascript/</guid>
      <description>Traditionally, developers have created properties inside of JavaScript classes for any data that might be needed within an instance. This isn’t a problem for small pieces of data that are readily available inside of the constructor. However, if some data needs to be calculated before becoming available in the instance, you may not want to...</description>
      <content:encoded>&lt;p&gt;Traditionally, developers have created properties inside of JavaScript classes for any data that might be needed within an instance. This isn’t a problem for small pieces of data that are readily available inside of the constructor. However, if some data needs to be calculated before becoming available in the instance, you may not want to pay that cost upfront. For example, consider this class:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;MyClass&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;someExpensiveComputation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, the &lt;code class=&quot;highlighter-rouge&quot;&gt;data&lt;/code&gt; property is created as the result of performing some expensive computation. It may not be efficient to perform that calculation upfront if you aren’t sure the property will be used. Fortunately, there are several ways to defer these operations until later.&lt;/p&gt;

&lt;h2 id=&quot;the-on-demand-property-pattern&quot;&gt;The on-demand property pattern&lt;/h2&gt;

&lt;p&gt;The easiest way to optimize performing an expensive operation is to wait until the data is  needed before doing the computation. For example, you could use an accessor property with a getter to do the computation on demand, like this:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;MyClass&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;someExpensiveComputation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this case, your expensive computation isn’t happening until the first time someone reads the &lt;code class=&quot;highlighter-rouge&quot;&gt;data&lt;/code&gt; property, which is an improvement. However, that same expensive computation is performed every time the &lt;code class=&quot;highlighter-rouge&quot;&gt;data&lt;/code&gt; property is read, which is worse than previous example where at least the computation was performed just once. This isn’t a good solution, but you can build upon it to create a better one.&lt;/p&gt;

&lt;h2 id=&quot;the-messy-lazy-loading-property-pattern&quot;&gt;The messy lazy-loading property pattern&lt;/h2&gt;

&lt;p&gt;Only performing the computation when the property is accessed is a good start. What you really need is to cache the information after that point and just use the cached version. But where do you cache that information for easy access? The easiest approach is to define a property with the same name and set its value to the computed data, like this:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;MyClass&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;actualData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;someExpensiveComputation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;nb&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;defineProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;actualData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;writable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;configurable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;enumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;actualData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, the &lt;code class=&quot;highlighter-rouge&quot;&gt;data&lt;/code&gt; property is once again defined as a getter on the class, but this time it caches the result. The call to &lt;code class=&quot;highlighter-rouge&quot;&gt;Object.defineProperty()&lt;/code&gt; creates a new property called &lt;code class=&quot;highlighter-rouge&quot;&gt;data&lt;/code&gt; that has a fixed value of &lt;code class=&quot;highlighter-rouge&quot;&gt;actualData&lt;/code&gt;, and is set to not be writable, configurable, and enumerable (to match the getter). After that, the value itself is returned. The next time the &lt;code class=&quot;highlighter-rouge&quot;&gt;data&lt;/code&gt; property is accessed, it will be reading from the newly created property rather than calling the getter:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;MyClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// calls the getter&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// reads from the data property&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Effectively, all of the computation is done only the first time the &lt;code class=&quot;highlighter-rouge&quot;&gt;data&lt;/code&gt; property is read. Each subsequent read of the &lt;code class=&quot;highlighter-rouge&quot;&gt;data&lt;/code&gt; property is returning the cached the version.&lt;/p&gt;

&lt;p&gt;The one downside to this pattern is that the &lt;code class=&quot;highlighter-rouge&quot;&gt;data&lt;/code&gt; property starts out as a non-enumerable prototype property and ends up as a non-enumerable own property:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;MyClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hasOwnProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;// false&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hasOwnProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;While this distinction isn’t important in many cases, it is an important thing to understand about this pattern as it can cause subtle issues when the object is passed around. Fortunately, it’s easy to address this with an updated pattern.&lt;/p&gt;

&lt;h2 id=&quot;the-only-own-lazy-loading-property-pattern-for-classes&quot;&gt;The only-own lazy-loading property pattern for classes&lt;/h2&gt;

&lt;p&gt;If you have a use case where it’s important for the lazy-loaded property to always exist on the instance, then you can using &lt;code class=&quot;highlighter-rouge&quot;&gt;Object.defineProperty()&lt;/code&gt; to create the property inside of the class constructor. It’s a little bit messier than the previous example, but it will ensure that the property only ever exists on the instance. Here’s an example:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;MyClass&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;nb&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;defineProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;actualData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;someExpensiveComputation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

                &lt;span class=&quot;nb&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;defineProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;actualData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;writable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;configurable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;actualData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;configurable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;enumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, the constructor creates the &lt;code class=&quot;highlighter-rouge&quot;&gt;data&lt;/code&gt; accessor property using &lt;code class=&quot;highlighter-rouge&quot;&gt;Object.defineProperty()&lt;/code&gt;. The property is created on the instance (by using &lt;code class=&quot;highlighter-rouge&quot;&gt;this&lt;/code&gt;) and defines a getter as well as specifying the property to be enumerable and configurable (typical of own properties). It’s particularly important to set the &lt;code class=&quot;highlighter-rouge&quot;&gt;data&lt;/code&gt; property as configurable so you can call &lt;code class=&quot;highlighter-rouge&quot;&gt;Object.defineProperty()&lt;/code&gt; on it again.&lt;/p&gt;

&lt;p&gt;The getter function then does the computation and calls &lt;code class=&quot;highlighter-rouge&quot;&gt;Object.defineProperty()&lt;/code&gt; a second time. The &lt;code class=&quot;highlighter-rouge&quot;&gt;data&lt;/code&gt; property is now redefined as a data property with a specific value and is made non-writable and non-configurable to protect the final data. Then, the computed data is returned from the getter. The next time the &lt;code class=&quot;highlighter-rouge&quot;&gt;data&lt;/code&gt; property is read, it will read from the stored value. As a bonus, the &lt;code class=&quot;highlighter-rouge&quot;&gt;data&lt;/code&gt; property now only ever exists as an own property and acts the same both before and after the first read:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;MyClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hasOwnProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hasOwnProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For classes, this is most likely the pattern you want to use; object literals, on the other hand, can use a simpler approach.&lt;/p&gt;

&lt;h2 id=&quot;the-lazy-loading-property-pattern-for-object-literals&quot;&gt;The lazy-loading property pattern for object literals&lt;/h2&gt;

&lt;p&gt;If you are using an object literal instead of a class, the process is much simpler because getters defined on object literals are defined as enumerable own properties (not prototype properties) just like data properties. That means you can use the messy lazy-loading property pattern for classes without being messy:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;actualData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;someExpensiveComputation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;nb&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;defineProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;actualData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;writable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;configurable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;enumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;actualData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hasOwnProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hasOwnProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;The ability to redefine object properties in JavaScript allows a unique opportunity to cache information that may be expensive to compute. By starting out with an accessor property that is redefined as a data property, you can defer computation until the first time a property is read and then cache the result for later use. This approach works both for classes and for object literals, and is a bit simpler in object literals because you don’t have to worry about your getter ending up on the prototype.&lt;/p&gt;

&lt;p&gt;One of the best ways to improve performance is to avoid doing the same work twice, so any time you can cache a result for use later, you’ll speed up your program. Techniques like the lazy-loading property pattern allow any property to become a caching layer to improve performance.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Two approaches to win an argument as a software engineer</title>
      <link>https://humanwhocodes.com/blog/2021/03/two-approaches-win-argument-software-engineer/</link>
      <pubDate>Tue, 16 Mar 2021 00:00:00 +0000</pubDate>
      <dc:creator>Nicholas C. Zakas</dc:creator>
      
      <category>Communication</category>
      
      <category>Work</category>
      
      <category>Skills</category>
      
      
      <category>Career</category>
      
      <guid isPermaLink="true">https://humanwhocodes.com/blog/2021/03/two-approaches-win-argument-software-engineer/</guid>
      <description>If you’ve spent any time developing software professionally and then you are probably used to the spirited debates that take place between software engineers as well as between software engineers and management, design, and product. Software engineers are not known for being shy about their opinions on any particular subject, and especially when it comes...</description>
      <content:encoded>&lt;p&gt;If you’ve spent any time developing software professionally and then you are probably used to the spirited debates that take place between software engineers as well as between software engineers and management, design, and product. Software engineers are not known for being shy about their opinions on any particular subject, and especially when it comes to the company they work for or the software they work on. However, many software engineers are not good at convincing others of their position. The fundamental problem is in the approach.&lt;/p&gt;

&lt;p&gt;When trying to convince someone that their position is correct, software engineers tend to default to a data transfer methodology: I have arrived at my position due to the state of data in my brain and so to convince you I will now attempt to transfer that state to your brain. The problem is, not everyone’s brain works the same way, so attempting to transfer that state is inefficient and can result in duplicating errors. Unfortunately, there are no checksums to fall back on.&lt;/p&gt;

&lt;p&gt;Another approach software engineers try is an approach based on trust. If you trust me, then you should trust that what I say is true and should therefore agree with my recommendation. This approach is actually a power play where you believe that your clout is enough that people should have blind faith in you. This only works some of the time for some people and generally leaves everyone feeling resentful. You don’t want to steamroll your team members, you want collaboration.&lt;/p&gt;

&lt;p&gt;So if data transfer and trust won’t work, what is left? The simple answer is to create a separate pool of data that exists outside of your brain. This “outside data” needs to be presented in a way that others can understand, so you are meeting together in the middle rather than trying to project your internal state to someone else.&lt;/p&gt;

&lt;p&gt;That probably sounds very wordy, so let me break it down to two approaches: &lt;em&gt;argue with data&lt;/em&gt; and &lt;em&gt;argue with code&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;approach-1-prove-something-is-true-with-data&quot;&gt;Approach 1: Prove something is &lt;em&gt;true&lt;/em&gt; with data&lt;/h2&gt;

&lt;p&gt;If you want to prove that something is &lt;em&gt;true&lt;/em&gt;, which is to say that the thing you’re promoting is both factual and accurate, then the only way to win this argument is to gather data to illustrate the fact. For example, each of these statements may or may not be true on a project:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Switching to another framework will improve performance&lt;/li&gt;
  &lt;li&gt;Our algorithm is more error prone than others so we should change it&lt;/li&gt;
  &lt;li&gt;We waste a lot of time on repetitive tasks&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Statements like these tend to start out as opinion and often don’t go any further, which leads to arguments of opinion. Opinion arguments can’t be won because, by definition, an opinion cannot be wrong. The only way to resolve this type of dispute is with data.&lt;/p&gt;

&lt;p&gt;First, get specific and define all of your terms. What is it you are actually claiming? Taking the first statement, what does it mean to “improve performance?” Are you talking about reducing page load time? Or maybe reducing CPU utilization on a virtual machine? Or maybe using less memory? Be as specific as possible about what you are proposing as a fact.&lt;/p&gt;

&lt;p&gt;Second, ask yourself what kind of data would prove your point? Have other companies published data showing the performance improvement between the framework you’re using and the one you’re proposing? Again, are you looking for page load time, CPU utilization, memory utilization, or something else? What numbers would make your point?&lt;/p&gt;

&lt;p&gt;Third, do you already have the data you need or do you need to collect it? Some companies collect a lot of data about their software and operations; some do not. To get the data you need, you may need to add some instrumentation, or dive into your analytics system, or find tech talks or research papers. You need to find a source of data that is separate from you to make your point.&lt;/p&gt;

&lt;p&gt;Keep in mind that data can be quantitative (measured values) or qualitative (opinions). While quantitative data is always preferable, it’s not always possible. In those moments, qualitative data can still win the argument. If you’re trying to prove that the team is wasting a lot of time on repetitive tasks, your best bet may be to have everyone on the team fill out a survey asking questions about the tasks they’re doing and if they are frustrated by them.&lt;/p&gt;

&lt;p&gt;Last, present the data in a format that anyone can understand. Oftentimes, putting together a simple slide deck is still the easiest way to convince others (especially in management) that something is true. Just make sure you give people the time and space to consume the data you’ve presented before pushing for a decision.&lt;/p&gt;

&lt;p&gt;The most important thing about this approach is that you are gathering data that exists outside of your brain and can easily be shared with others.&lt;/p&gt;

&lt;h2 id=&quot;approach-2-prove-something-is-possible-with-code&quot;&gt;Approach 2: Prove something is &lt;em&gt;possible&lt;/em&gt; with code&lt;/h2&gt;

&lt;p&gt;If you want to prove that something is &lt;em&gt;possible&lt;/em&gt;, then the only way to win this argument is with code. It always surprises me when I find two software engineers arguing with each other over whether something is possible when it might take an hour to write some code and settle the issue permanently. It doesn’t make sense to argue over something that can be proven, beyond all doubt, by writing code. Here are some example statements that can probably be proved with code:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;We can make the list scroll infinitely&lt;/li&gt;
  &lt;li&gt;It’s possible to write a complex query with one request&lt;/li&gt;
  &lt;li&gt;Switching to another framework will improve performance&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Back when I was a young web developer, when people proposed an infinitely-scrolling list I thought they were crazy. It would never be smooth, it would never be fast enough, it would crash the browser due to memory limits. But then I saw the first demo of an infinitely-scrolling list that was smooth and didn’t crash the browser, and that was it. No more argument from my side. Just because I couldn’t figure out how to do it didn’t mean it wasn’t possible. If you’re in a position where you are arguing something is possible, go ahead and create it (or a reasonable prototype); if you are unwilling or unable to write the code, then it’s time to drop the argument.&lt;/p&gt;

&lt;p&gt;One last point: that last statement looks familiar, doesn’t it? Yes, sometimes the same statement needs both data and code to win the argument. In this case, you might need to gather data to prove that performance is a problem and then also write some code to show that switching to the new framework will improve those numbers. You are actually trying to prove two things: 1) the performance is a problem and 2) switching to the new framework addresses the problem. The combination approach works exceedingly well in software engineering because so much of the work is about making changes and measuring the effect of those changes.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Arguing your point is an expected part of being a software engineer. It is fine to have opinions about everything, but if you are attempting to convince someone that your position is correct in order to make some change, then you owe it to them and to yourself to make sure you are correct. You shouldn’t expect people to blindly follow your opinions; you should expect to need to produce data, code, or some combination of the two to convince others. The willingness to do the research, data crunch, write code, or otherwise dig into the problem is what will ultimately convince others of your position.&lt;/p&gt;

&lt;p&gt;No matter what kind of work-related argument you are trying to win, remember to argue with data and argue with code.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Introducing Env: a better way to read environment variables in JavaScript</title>
      <link>https://humanwhocodes.com/blog/2021/02/introducing-env-javascript-environment-variables/</link>
      <pubDate>Tue, 16 Feb 2021 00:00:00 +0000</pubDate>
      <dc:creator>Nicholas C. Zakas</dc:creator>
      
      <category>JavaScript</category>
      
      <category>Environment Variables</category>
      
      <category>Node.js</category>
      
      <category>Deno</category>
      
      
      <category>Web Development</category>
      
      <guid isPermaLink="true">https://humanwhocodes.com/blog/2021/02/introducing-env-javascript-environment-variables/</guid>
      <description>If you write server-side JavaScript, chances are you’ve need to read information from environment variables. It’s considered a best practice to share sensitive information, such as access tokens, inside of environment variables to keep them secure. However, the way environment variables are read from JavaScript is error-prone in subtle ways that might take you hours...</description>
      <content:encoded>&lt;p&gt;If you write server-side JavaScript, chances are you’ve need to read information from environment variables. It’s considered a best practice to share sensitive information, such as access tokens, inside of environment variables to keep them secure. However, the way environment variables are read from JavaScript is error-prone in subtle ways that might take you hours to figure out. When an error occurs reading an environment variable, you want to know immediately, and you don’t want to interpret cryptic error messages. That’s where Env comes in.&lt;/p&gt;

&lt;h2 id=&quot;installing-env&quot;&gt;Installing Env&lt;/h2&gt;

&lt;p&gt;Env&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; is a zero-dependency utility designed to make reading environment variables safer and less error-prone. It does this by addressing the root causes of environment variable-related errors in server-side JavaScript. It works in both Node.js and Deno, and automatically reads environment variables from the correct location based on the runtime being used.&lt;/p&gt;

&lt;p&gt;To use Env in Node.js, install it with npm:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ npm install @humanwhocodes/env
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then import the &lt;code class=&quot;highlighter-rouge&quot;&gt;Env&lt;/code&gt; constructor:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Env&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@humanwhocodes/env&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// or&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Env&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@humanwhocodes/env&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To use Env in Deno, reference it from Skypack:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Env&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;https://cdn.skypack.dev/@humanwhocodes/env?dts&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once you have the &lt;code class=&quot;highlighter-rouge&quot;&gt;Env&lt;/code&gt; constructor, you can create a new instance like this:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And now you’re ready to read environment variables safely.&lt;/p&gt;

&lt;h2 id=&quot;problem-1-missing-environment-variables&quot;&gt;Problem #1: Missing environment variables&lt;/h2&gt;

&lt;p&gt;The first problem Env addresses is how to deal with missing environment variables. It’s quite common for environment variables to go missing either because they were accidentally not set up correctly or because they only exist on some containers and not all. In any case, you want to handle missing environment variables seamlessly. In Node.js, you might do something like this:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;USERNAME&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;USERNAME&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;guest&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The intent here is to use the &lt;code class=&quot;highlighter-rouge&quot;&gt;USERNAME&lt;/code&gt; environment variable if present, and if not, default to &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;guest&quot;&lt;/code&gt;. Env streamlines this to make setting defaults clear:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;USERNAME&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;USERNAME&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;guest&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This code has the same effect but avoids any type coercion in the process. Of course, this assumes it’s okay for &lt;code class=&quot;highlighter-rouge&quot;&gt;USERNAME&lt;/code&gt; to be missing. But what if you absolutely need an environment variable present in order for your application to work? For that, you might write some code like this:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;USERNAME&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;USERNAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;USERNAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Environment variable USERNAME is missing.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s a lot of code for some simple validation, and if you have several required environment variables, you’ll end up repeating this pattern for each one. With Env, you can use the &lt;code class=&quot;highlighter-rouge&quot;&gt;require()&lt;/code&gt; method:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;USERNAME&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;USERNAME&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If the environment variable &lt;code class=&quot;highlighter-rouge&quot;&gt;USERNAME&lt;/code&gt; is missing in this example, then an error is thrown telling you so. You can also use the &lt;code class=&quot;highlighter-rouge&quot;&gt;required&lt;/code&gt; property in a similar way:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;USERNAME&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;USERNAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This syntax allows you to avoid typing a string but will still throw an error if &lt;code class=&quot;highlighter-rouge&quot;&gt;USERNAME&lt;/code&gt; is not present.&lt;/p&gt;

&lt;h2 id=&quot;problem-2-typos&quot;&gt;Problem #2: Typos&lt;/h2&gt;

&lt;p&gt;Another type of error that is common with environment variables are typos. Typos can be hard to spot when you are typing the same thing multiple times. For example, you might type something like this:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;USERNAME&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;USERRNAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Personally, I’ve spent hours tracking down bugs related to my incorrectly typing the name of the environment variable in my code. For whatever reason, I type the name of the variable correctly but not the environment variable name. If you want your JavaScript variables to have the same name as some required environment variables, you can use destructuring of the &lt;code class=&quot;highlighter-rouge&quot;&gt;required&lt;/code&gt; property to only type the name once:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;PORT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;HOST&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, two local variables, &lt;code class=&quot;highlighter-rouge&quot;&gt;PORT&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;HOST&lt;/code&gt;, are created from the environment variables of the same name. If either environment variable is missing, an error is thrown.&lt;/p&gt;

&lt;h2 id=&quot;problem-3-type-mismatches&quot;&gt;Problem #3: Type mismatches&lt;/h2&gt;

&lt;p&gt;Another subtle type of error with environment variables are type mismatches. For instance, consider the following Node.js code:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PORT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PORT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This line, or something similar, appears in a lot of Node.js applications. Most of the time it doesn’t cause an issue…but it could. Can you spot the problem?&lt;/p&gt;

&lt;p&gt;All environment variables are strings, so the JavaScript variable &lt;code class=&quot;highlighter-rouge&quot;&gt;PORT&lt;/code&gt; is a string when the environment variable is present and a number if not. Using similar code in Deno threw an error&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; that took me a while to figure out. It turned out that the Deno HTTP server required the port to be a number, so it worked fine locally but when I deployed it to Cloud Run, I received an error.&lt;/p&gt;

&lt;p&gt;To solve this problem, Env converts all default values into strings automatically:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PORT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;PORT&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PORT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;// always true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Even if you pass in a non-string value as the default, Env will convert it to a string to ensure that you only ever receive a string value when reading environment variables.&lt;/p&gt;

&lt;h2 id=&quot;problem-4-fallback-variables&quot;&gt;Problem #4: Fallback variables&lt;/h2&gt;

&lt;p&gt;Sometimes you might want to check several environment variables and only use a default if none of the environment variables are present. So you might have code that looks like this:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PORT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PORT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;HTTP_PORT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can make that a bit clearer using Env:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PORT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;PORT&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;HTTP_PORT&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Using this code, Env returns a value from the first environment variable it finds. Similar to &lt;code class=&quot;highlighter-rouge&quot;&gt;get()&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;first()&lt;/code&gt; allows you to pass in a default value to use if none of the environment variables are found, and that default value is automatically converted to a string. As an added error check, if the first argument isn’t an array or is an array with only one item, then an error is thrown.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Env is one of those utilities that has been so valuable to me that I sometimes forget to mention it. I’ve been using it in a number of personal projects for the past two years and it’s saved me a lot of time. Debugging errors related to environment variables isn’t anyone’s idea of fun, and I can’t count the times where I’ve been saved by an Env error. I hope you find it helpful, as well.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://github.com/humanwhocodes/env&quot;&gt;Env&lt;/a&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://github.com/denoland/deno/issues/7891#issuecomment-706259974&quot;&gt;serve() error: “Uncaught InvalidData”&lt;/a&gt; &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Creating a JavaScript promise from scratch, Part 7: Unhandled rejection tracking</title>
      <link>https://humanwhocodes.com/blog/2021/01/creating-javascript-promise-from-scratch-unhandled-rejection-tracking/</link>
      <pubDate>Tue, 19 Jan 2021 00:00:00 +0000</pubDate>
      <dc:creator>Nicholas C. Zakas</dc:creator>
      
      <category>JavaScript</category>
      
      <category>Promises</category>
      
      <category>ECMAScript 6</category>
      
      
      <category>Programming</category>
      
      <guid isPermaLink="true">https://humanwhocodes.com/blog/2021/01/creating-javascript-promise-from-scratch-unhandled-rejection-tracking/</guid>
      <description>When promises were introduced in ECMAScript 2015, they had an interesting flaw: if a promise didn’t have a rejection handler and was later rejected, you would have no idea. The rejection silently occurred behind the scenes and, therefore, could easily be missed. The best practice of always attaching rejection handlers to promises emerged due to...</description>
      <content:encoded>&lt;p&gt;When promises were introduced in ECMAScript 2015, they had an interesting flaw: if a promise didn’t have a rejection handler and was later rejected, you would have no idea. The rejection silently occurred behind the scenes and, therefore, could easily be missed. The best practice of always attaching rejection handlers to promises emerged due to this limitation. Eventually, a way to detect unhandled promise rejections was added to ECMA-262 and both Node.js and web browsers implemented console warnings when an unhandled rejection occurred. In this post, I’ll walk through how unhandled rejection tracking works and how to implement it in JavaScript.&lt;/p&gt;

&lt;p&gt;This is the seventh and final post in my series about creating JavaScript promises from scratch. If you haven’t already read the previous posts, I’d suggest you do before continuing on:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://humanwhocodes.com/blog/2020/09/creating-javascript-promise-from-scratch-constructor/&quot;&gt;Part 1: Constructor&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://humanwhocodes.com/blog/2020/09/creating-javascript-promise-from-scratch-resolving-to-a-promise/&quot;&gt;Part 2: Resolving to a promise&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://humanwhocodes.com/blog/2020/10/creating-javascript-promise-from-scratch-then-catch-finally/&quot;&gt;Part 3: then(), catch(), and finally()&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://humanwhocodes.com/blog/2020/10/creating-javascript-promise-from-scratch-promise-resolve-reject/&quot;&gt;Part 4: Promise.resolve() and Promise.reject()&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://humanwhocodes.com/blog/2020/11/creating-javascript-promise-from-scratch-promise-race-any/&quot;&gt;Part 5: Promise.race() and Promise.any()&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://humanwhocodes.com/blog/2020/12/creating-javascript-promise-from-scratch-promise-all-allsettled/&quot;&gt;Part 6: Promise.all() and Promise.allSettled()&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a reminder, this series is based on my promise library, &lt;a href=&quot;https://github.com/humanwhocodes/pledge&quot;&gt;Pledge&lt;/a&gt;. You can view and download all of the source code from GitHub.&lt;/p&gt;

&lt;h2 id=&quot;unhandled-rejection-tracking-in-browsers&quot;&gt;Unhandled rejection tracking in browsers&lt;/h2&gt;

&lt;p&gt;While both Node.js and web browsers have ways of dealing with unhandled rejections, I’m going to focus on the web browser implementation because it is defined in the HTML specification&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. Having a specification to work from makes it easier to understand what’s going on as opposed to the Node.js implementation which is custom (though still similar to web browsers). To start, suppose you have a promise defined like this:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;promise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;43&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This promise doesn’t have a rejection handler defined and so when it’s rejected it ends up being tracked by the browser. Periodically, the browser checks its list of unhandled rejections and fires a &lt;code class=&quot;highlighter-rouge&quot;&gt;unhandledrejection&lt;/code&gt; event on &lt;code class=&quot;highlighter-rouge&quot;&gt;globalThis&lt;/code&gt;. The event handler receives an &lt;code class=&quot;highlighter-rouge&quot;&gt;event&lt;/code&gt; object with a &lt;code class=&quot;highlighter-rouge&quot;&gt;promise&lt;/code&gt; property containing the rejected promise and a &lt;code class=&quot;highlighter-rouge&quot;&gt;reason&lt;/code&gt; property containing the rejection reason (&lt;code class=&quot;highlighter-rouge&quot;&gt;43&lt;/code&gt; in the case of this example). For example:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// called when an unhandled rejection occurs&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;globalThis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;onunhandledrejection&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;// get the promise&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reason&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;// get the rejection reason&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In addition to triggering the &lt;code class=&quot;highlighter-rouge&quot;&gt;unhandledrejection&lt;/code&gt; event, the browser will output a warning to the console indicating that an unhandled rejection occurred. You can therefore choose to track unhandled rejections programmatically or keep your console open to see them as you’re developing.&lt;/p&gt;

&lt;h3 id=&quot;late-handled-promise-rejection&quot;&gt;Late-handled promise rejection&lt;/h3&gt;

&lt;p&gt;You may be wondering, what happens if a rejection handler is added at some later point in time? After all, you can add a rejection handler anytime between creation of the promise and the time when the promise is destroyed through garbage collection. You can, for instance, do this:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;promise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;43&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reason&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reason&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, a promise is created without a rejection handler initially and then adds one later. What happens in this case depends largely on the amount of time that has passed:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If the rejection handler is added before the browser decides to trigger &lt;code class=&quot;highlighter-rouge&quot;&gt;unhandledrejection&lt;/code&gt;, then the event will not be triggered.&lt;/li&gt;
  &lt;li&gt;If the rejection handler is added after the browser has triggered &lt;code class=&quot;highlighter-rouge&quot;&gt;unhandledrejection&lt;/code&gt;, then a &lt;code class=&quot;highlighter-rouge&quot;&gt;rejectionhandled&lt;/code&gt; event is triggered to let you know that the rejection is no longer unhandled.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s a little bit confusing, but basically, any promise that triggers an &lt;code class=&quot;highlighter-rouge&quot;&gt;unhandledrejection&lt;/code&gt; event could potentially trigger a &lt;code class=&quot;highlighter-rouge&quot;&gt;rejectionhandled&lt;/code&gt; event later. Therefore, you really need to listen for both events and track which promises remain, like this:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rejections&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// called when an unhandled rejection occurs&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;globalThis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;onunhandledrejection&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reason&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;rejections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reason&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// called when an unhandled rejection occurs&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;globalThis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;onrejectionhandled&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;promise&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;rejections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This code tracks unhandled rejections using a map. When an &lt;code class=&quot;highlighter-rouge&quot;&gt;unhandledrejection&lt;/code&gt; event occurs, the promise and rejection reason are saved to the map; when a &lt;code class=&quot;highlighter-rouge&quot;&gt;rejectionhandled&lt;/code&gt; event occurs, the promise is deleted from the map. By periodically checking the contents of &lt;code class=&quot;highlighter-rouge&quot;&gt;rejections&lt;/code&gt;, you can then track which rejections occurred without handlers.&lt;/p&gt;

&lt;p&gt;Another quirk in the relationship between the &lt;code class=&quot;highlighter-rouge&quot;&gt;unhandledrejection&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;rejectionhandled&lt;/code&gt; events is that you can prevent the &lt;code class=&quot;highlighter-rouge&quot;&gt;rejectionhandled&lt;/code&gt; event from firing by adding a rejection handler inside of the &lt;code class=&quot;highlighter-rouge&quot;&gt;onunhandledrejection&lt;/code&gt; event handler, like this:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// called when an unhandled rejection occurs&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;globalThis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;onunhandledrejection&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reason&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{});&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;// make the rejection handled&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// this will never be called&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;globalThis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;onrejectionhandled&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;promise&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this case, the &lt;code class=&quot;highlighter-rouge&quot;&gt;rejectionhandled&lt;/code&gt; event isn’t triggered because a rejection handler is added before it’s time for that event. The browser assumes that you know the promise is now handled and so there is no reason to trigger the &lt;code class=&quot;highlighter-rouge&quot;&gt;rejectionhandled&lt;/code&gt; event.&lt;/p&gt;

&lt;h3 id=&quot;eliminating-the-console-warning&quot;&gt;Eliminating the console warning&lt;/h3&gt;

&lt;p&gt;As mentioned previously, the browser will output a warning to the console whenever an unhandled promise rejection occurs. This console warning occurs after the &lt;code class=&quot;highlighter-rouge&quot;&gt;unhandledrejection&lt;/code&gt; event is fired, which gives you the opportunity to prevent the warning altogether. You can cancel the console warning by calling the &lt;code class=&quot;highlighter-rouge&quot;&gt;preventDefault()&lt;/code&gt; method on the &lt;code class=&quot;highlighter-rouge&quot;&gt;event&lt;/code&gt; object, like this:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;globalThis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;onunhandledrejection&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This event handler ensures that the console warning for the unhandled rejection will not happen. Suppressing the console warning is helpful in production where you don’t want to litter the console with additional information once you already know a promise was missing a rejection handler.&lt;/p&gt;

&lt;p&gt;With that overview out of the way, it’s now time to discuss how to implement the same browser unhandled rejection tracking from scratch.&lt;/p&gt;

&lt;h2 id=&quot;implementing-unhandled-rejection-tracking&quot;&gt;Implementing unhandled rejection tracking&lt;/h2&gt;

&lt;p&gt;The design for rejection tracking in the Pledge library closely follows the web browser approach. Because I didn’t want to mess with the &lt;code class=&quot;highlighter-rouge&quot;&gt;globalThis&lt;/code&gt; object, I decided to add two static methods to the &lt;code class=&quot;highlighter-rouge&quot;&gt;Pledge&lt;/code&gt; class to act as event handlers:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Pledge&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// other methods omitted for space&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;onUnhandledRejection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// noop&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;onRejectionHandled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// noop&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// other methods omitted for space&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;event&lt;/code&gt; object is an instance of &lt;code class=&quot;highlighter-rouge&quot;&gt;PledgeRejectionEvent&lt;/code&gt;, which is defined like this:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PledgeRejectionEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reason&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reason&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reason&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;returnValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;returnValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ve included the &lt;code class=&quot;highlighter-rouge&quot;&gt;preventDefault()&lt;/code&gt; method as well as the &lt;code class=&quot;highlighter-rouge&quot;&gt;returnValue&lt;/code&gt; legacy property so either way of canceling the event will work.&lt;/p&gt;

&lt;p&gt;Last, I created a &lt;code class=&quot;highlighter-rouge&quot;&gt;RejectionTracker&lt;/code&gt; class to encapsulate most of the functionality. While this class isn’t described in any specification, I found it easier to wrap all of the functionality in this class. I then attached an instance of &lt;code class=&quot;highlighter-rouge&quot;&gt;RejectionTracker&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&gt;Pledge&lt;/code&gt; via a symbol property:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;Pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PledgeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rejectionTracker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;RejectionTracker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this way, I can always reach the rejection tracker from any instance of &lt;code class=&quot;highlighter-rouge&quot;&gt;Pledge&lt;/code&gt; through &lt;code class=&quot;highlighter-rouge&quot;&gt;this.constructor[PledgeSymbol.rejectionTracker]&lt;/code&gt;. It will become more apparent why this is important later in this post.&lt;/p&gt;

&lt;h3 id=&quot;what-does-it-mean-for-a-promise-to-be-handled&quot;&gt;What does it mean for a promise to be handled?&lt;/h3&gt;

&lt;p&gt;ECMA-262 considers a promise to be handled if the promise’s &lt;code class=&quot;highlighter-rouge&quot;&gt;then()&lt;/code&gt; method has been called (which includes &lt;code class=&quot;highlighter-rouge&quot;&gt;catch()&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;finally()&lt;/code&gt;, both of which call &lt;code class=&quot;highlighter-rouge&quot;&gt;then()&lt;/code&gt; behind the scenes). It actually doesn’t matter if you’ve attached a fulfillment handler, a rejection handler, or neither, so long as &lt;code class=&quot;highlighter-rouge&quot;&gt;then()&lt;/code&gt; was called. Each call to &lt;code class=&quot;highlighter-rouge&quot;&gt;then()&lt;/code&gt; creates a new promise which then becomes responsible for dealing with any fulfillment or rejection. Consider this example:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;promise1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;43&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;promise2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;promise1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, &lt;code class=&quot;highlighter-rouge&quot;&gt;promise1&lt;/code&gt; is considered handled because &lt;code class=&quot;highlighter-rouge&quot;&gt;then()&lt;/code&gt; is called and a fulfillment handler is attached. When &lt;code class=&quot;highlighter-rouge&quot;&gt;promise1&lt;/code&gt; is rejected, that rejection is passed on to &lt;code class=&quot;highlighter-rouge&quot;&gt;promise2&lt;/code&gt;, which is not handled. A browser would report the unhandled rejection from &lt;code class=&quot;highlighter-rouge&quot;&gt;promise2&lt;/code&gt; and disregard &lt;code class=&quot;highlighter-rouge&quot;&gt;promise1&lt;/code&gt;. So, the browser isn’t really tracking all unhandled rejections, but rather, it’s tracking whether the last promise in a chain has any handlers attached.&lt;/p&gt;

&lt;h3 id=&quot;how-do-you-know-if-a-promise-is-handled&quot;&gt;How do you know if a promise is handled?&lt;/h3&gt;

&lt;p&gt;ECMA-262 describes two key features that enable rejection tracking:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;[[PromiseIsHandled]]&lt;/code&gt; internal property&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; of every promise. This is a Boolean value indicating if the promise is handled. It starts out as &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt; and is changed to &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; after &lt;code class=&quot;highlighter-rouge&quot;&gt;then()&lt;/code&gt; is called.&lt;/li&gt;
  &lt;li&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;HostPromiseRejectionTracker()&lt;/code&gt; operation&lt;sup id=&quot;fnref:3&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; is an abstract representation of a promise rejection tracker. ECMA-262 itself does not specify an algorithm for this operation; instead, it defers that to host environments to decide (host environments meaning browsers, Node.js, Deno, etc.).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The majority of the functionality related to these two features is contained the &lt;code class=&quot;highlighter-rouge&quot;&gt;PerformPromiseThen()&lt;/code&gt; operation&lt;sup id=&quot;fnref:4&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; (discussed in part 3), which I’ve implemented as &lt;code class=&quot;highlighter-rouge&quot;&gt;performPledgeThen()&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;performPledgeThen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;onFulfilled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;onRejected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;resultCapability&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;assertIsPledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;isCallable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;onFulfilled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;onFulfilled&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;isCallable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;onRejected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;onRejected&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fulfillReaction&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PledgeReaction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;resultCapability&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;fulfill&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;onFulfilled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rejectReaction&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PledgeReaction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;resultCapability&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;onRejected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PledgeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;pending&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PledgeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fulfillReactions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fulfillReaction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PledgeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rejectReactions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rejectReaction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;fulfilled&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; 
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PledgeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
                &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fulfillJob&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PledgeReactionJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fulfillReaction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;nx&quot;&gt;hostEnqueuePledgeJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fulfillJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;rejected&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reason&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PledgeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

                &lt;span class=&quot;c1&quot;&gt;// if the pledge isn't handled, track it with the tracker&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PledgeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;isHandled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;nx&quot;&gt;hostPledgeRejectionTracker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rejectJob&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PledgeReactionJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rejectReaction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reason&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;nx&quot;&gt;hostEnqueuePledgeJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rejectJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;nl&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;TypeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`Invalid pledge state: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PledgeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// mark the pledge as handled&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PledgeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;isHandled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;resultCapability&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;resultCapability&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Regardless of what happens during the course of called &lt;code class=&quot;highlighter-rouge&quot;&gt;performPledgeThen()&lt;/code&gt;, the pledge is always marked as handled before the end of the function. If the pledge is rejected, then &lt;code class=&quot;highlighter-rouge&quot;&gt;hostPledgeRejectionTracker()&lt;/code&gt; is called with the pledge and a second argument of &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;handle&quot;&lt;/code&gt;. That second argument indicates that the rejection was handled and shouldn’t be tracked as an unhandled rejection.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;HostPromiseRejectionTracker()&lt;/code&gt; is also called by the &lt;code class=&quot;highlighter-rouge&quot;&gt;RejectPromise()&lt;/code&gt; operation&lt;sup id=&quot;fnref:5&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; (also discussed in part 3), which I’ve implemented as &lt;code class=&quot;highlighter-rouge&quot;&gt;rejectPledge()&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rejectPledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reason&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PledgeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;pending&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Pledge is already settled.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reactions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PledgeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rejectReactions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PledgeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reason&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PledgeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fulfillReactions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PledgeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rejectReactions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PledgeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;rejected&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// global rejection tracking&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PledgeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;isHandled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;hostPledgeRejectionTracker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;triggerPledgeReactions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reactions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reason&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, the &lt;code class=&quot;highlighter-rouge&quot;&gt;rejectPledge()&lt;/code&gt; function called &lt;code class=&quot;highlighter-rouge&quot;&gt;hostPledgeRejectionTracker()&lt;/code&gt; with a second argument of &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;reject&quot;&lt;/code&gt;, indicating that the pledge was rejected and not handled. Remember, &lt;code class=&quot;highlighter-rouge&quot;&gt;rejectPledge()&lt;/code&gt; is the function that is called by the &lt;code class=&quot;highlighter-rouge&quot;&gt;reject&lt;/code&gt; argument that is passed in to executor function when creating a new promise, so at that point in time, the promise hasn’t had any handlers assigned. So, &lt;code class=&quot;highlighter-rouge&quot;&gt;rejectPledge()&lt;/code&gt; is marking the pledge as unhandled, and if &lt;code class=&quot;highlighter-rouge&quot;&gt;then()&lt;/code&gt; is later called to assign a handler, then it will bemarked as handled.&lt;/p&gt;

&lt;p&gt;I’ve implemented &lt;code class=&quot;highlighter-rouge&quot;&gt;hostPledgeRejectionTracker()&lt;/code&gt; as follows:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;hostPledgeRejectionTracker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rejectionTracker&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PledgeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rejectionTracker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;rejectionTracker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is where attaching the rejection handler to the &lt;code class=&quot;highlighter-rouge&quot;&gt;Pledge&lt;/code&gt; constructor is helpful. I’m able to get to the &lt;code class=&quot;highlighter-rouge&quot;&gt;RejectionTracker&lt;/code&gt; instance and call the &lt;code class=&quot;highlighter-rouge&quot;&gt;track()&lt;/code&gt; method to keep this function simple.&lt;/p&gt;

&lt;h3 id=&quot;the-rejectiontracker-class&quot;&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;RejectionTracker&lt;/code&gt; class&lt;/h3&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;RejectionTracker&lt;/code&gt; class is designed to encapsulate all of the rejection tracking functionality described in the HTML specification:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;An environment settings object also has an outstanding rejected promises weak set and an about-to-be-notified rejected promises list, used to track unhandled promise rejections. The outstanding rejected promises weak set must not create strong references to any of its members, and implementations are free to limit its size, e.g. by removing old entries from it when new ones are added.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This description is a little bit confusing, so let me explain it. There are two different collections used to track rejections:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The &lt;em&gt;“about-to-be-notified” rejected promises list&lt;/em&gt; is a list of promises that have been rejected and will trigger the &lt;code class=&quot;highlighter-rouge&quot;&gt;unhandledrejection&lt;/code&gt; event.&lt;/li&gt;
  &lt;li&gt;The &lt;em&gt;outstanding rejected promises weak set&lt;/em&gt; is a collection of promises that had unhandled rejections and triggered the &lt;code class=&quot;highlighter-rouge&quot;&gt;unhandledrejection&lt;/code&gt; event. These promises are tracked just in case they have a rejection handler added later, in which case the &lt;code class=&quot;highlighter-rouge&quot;&gt;rejectionhandled&lt;/code&gt; event is triggered.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So these are the two collections the &lt;code class=&quot;highlighter-rouge&quot;&gt;RejectionTracker&lt;/code&gt; needs to manage. Additionally, it manages a logger (typically &lt;code class=&quot;highlighter-rouge&quot;&gt;console&lt;/code&gt; but can be overwritten for testing) and a timeout ID (which I’ll explain later in this post). Here’s what the class and constructor look like:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;RejectionTracker&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;aboutToBeNotified&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;outstandingRejections&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;WeakSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timeoutId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// TODO&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I chose to use a set for the “about-to-be-notified” promises list because it will prevent duplicates while allowing me to iterate through all of the promises contained within it. The outstanding rejections collection is implemented as a weak set, per the specification, which means there’s no way to iterate over the contents. That’s not a problem for how this collection is used in algorithm, however.&lt;/p&gt;

&lt;h4 id=&quot;implementing-hostpromiserejectiontracker&quot;&gt;Implementing &lt;code class=&quot;highlighter-rouge&quot;&gt;HostPromiseRejectionTracker()&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;The primary method is &lt;code class=&quot;highlighter-rouge&quot;&gt;track()&lt;/code&gt;, and that implements the functionality described in the HTML specification for &lt;code class=&quot;highlighter-rouge&quot;&gt;HostPromiseRejectionTracker()&lt;/code&gt;&lt;sup id=&quot;fnref:6&quot;&gt;&lt;a href=&quot;#fn:6&quot; class=&quot;footnote&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;, which is as follows:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Let &lt;em&gt;script&lt;/em&gt; be the running script.&lt;/li&gt;
  &lt;li&gt;If &lt;em&gt;script&lt;/em&gt;’s muted errors is true, terminate these steps.&lt;/li&gt;
  &lt;li&gt;Let &lt;em&gt;settings object&lt;/em&gt; be &lt;em&gt;script&lt;/em&gt;’s settings object.&lt;/li&gt;
  &lt;li&gt;If &lt;em&gt;operation&lt;/em&gt; is &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;reject&quot;&lt;/code&gt;,
    &lt;ol&gt;
      &lt;li&gt;Add &lt;em&gt;promise&lt;/em&gt; to &lt;em&gt;settings object&lt;/em&gt;’s about-to-be-notified rejected promises list.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;If &lt;em&gt;operation&lt;/em&gt; is &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;handle&quot;&lt;/code&gt;,
    &lt;ol&gt;
      &lt;li&gt;If &lt;em&gt;settings object&lt;/em&gt;’s about-to-be-notified rejected promises list contains &lt;em&gt;promise&lt;/em&gt;, then remove &lt;em&gt;promise&lt;/em&gt; from that list and return.&lt;/li&gt;
      &lt;li&gt;If &lt;em&gt;settings object&lt;/em&gt;’s outstanding rejected promises weak set does not contain &lt;em&gt;promise&lt;/em&gt;, then return.&lt;/li&gt;
      &lt;li&gt;Remove &lt;em&gt;promise&lt;/em&gt; from settings object’s outstanding rejected promises weak set.&lt;/li&gt;
      &lt;li&gt;Let &lt;em&gt;global&lt;/em&gt; be &lt;em&gt;settings object&lt;/em&gt;’s global object.&lt;/li&gt;
      &lt;li&gt;Queue a global task on the DOM manipulation task source given &lt;em&gt;global&lt;/em&gt; to fire an event named &lt;code class=&quot;highlighter-rouge&quot;&gt;rejectionhandled&lt;/code&gt; at &lt;em&gt;global&lt;/em&gt;, using &lt;code class=&quot;highlighter-rouge&quot;&gt;PromiseRejectionEvent&lt;/code&gt;, with the &lt;code class=&quot;highlighter-rouge&quot;&gt;promise&lt;/code&gt; attribute initialized to &lt;em&gt;promise&lt;/em&gt;, and the &lt;code class=&quot;highlighter-rouge&quot;&gt;reason&lt;/code&gt; attribute initialized to the value of &lt;em&gt;promise&lt;/em&gt;’s &lt;code class=&quot;highlighter-rouge&quot;&gt;[[PromiseResult]]&lt;/code&gt; internal slot.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first three steps can be ignored for our purposes because they are just setting up variables. The fourth steps occurs when &lt;code class=&quot;highlighter-rouge&quot;&gt;operation&lt;/code&gt; is &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;reject&quot;&lt;/code&gt;, at which point the promise that was rejected is added to the about-to-be-notified rejected promises list. That’s all that needs to happen at this point because a recurring check will later read that list to determine if any events need to be fired. The more interesting part is what happens when &lt;code class=&quot;highlighter-rouge&quot;&gt;operation&lt;/code&gt; is &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;handle&quot;&lt;/code&gt;, meaning that a previously rejected promise now has a rejection handler added. Here are the steps using clearer language:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;If &lt;code class=&quot;highlighter-rouge&quot;&gt;promise&lt;/code&gt; is in the about-to-be-notified rejected promises list, that means the promise was rejected without a rejection handler but the &lt;code class=&quot;highlighter-rouge&quot;&gt;unhandledrejection&lt;/code&gt; event has not yet been fired for that promise. Because of that, you can just remove &lt;code class=&quot;highlighter-rouge&quot;&gt;promise&lt;/code&gt; from the list to ensure the event is never fired, and therefore, you’ll never need to fire a &lt;code class=&quot;highlighter-rouge&quot;&gt;rejectionhandled&lt;/code&gt; event. Your work here is done.&lt;/li&gt;
  &lt;li&gt;If the outstanding rejected promises weak set doesn’t contain &lt;code class=&quot;highlighter-rouge&quot;&gt;promise&lt;/code&gt;, then there’s also nothing else to do here. The &lt;code class=&quot;highlighter-rouge&quot;&gt;unhandledrejection&lt;/code&gt; event was never fired for &lt;code class=&quot;highlighter-rouge&quot;&gt;promise&lt;/code&gt; so the &lt;code class=&quot;highlighter-rouge&quot;&gt;rejectionhandled&lt;/code&gt; event should also never fire. There’s no more tracking necessary.&lt;/li&gt;
  &lt;li&gt;If &lt;code class=&quot;highlighter-rouge&quot;&gt;promise&lt;/code&gt; is in the outstanding rejected promises weak set, that means it has previously triggered the &lt;code class=&quot;highlighter-rouge&quot;&gt;unhandledrejection&lt;/code&gt; event and you are now being notified that it is handled. That means you need to trigger the &lt;code class=&quot;highlighter-rouge&quot;&gt;rejectionhandled&lt;/code&gt; event. For simplicity, you can read “queue a global task” as “run this code with &lt;code class=&quot;highlighter-rouge&quot;&gt;setTimeout()&lt;/code&gt;.”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After all of that explanation, here’s what it looks like in code:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;RejectionTracker&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;aboutToBeNotified&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;outstandingRejections&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;WeakSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timeoutId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;operation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;aboutToBeNotified&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;operation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;aboutToBeNotified&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;has&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;aboutToBeNotified&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;outstandingRejections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;has&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            
            &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;outstandingRejections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;nx&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PledgeRejectionEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PledgeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
                &lt;span class=&quot;nx&quot;&gt;pledge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;onRejectionHandled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;            
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// not part of spec, need to toggle monitoring&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;aboutToBeNotified&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;startMonitor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stopMonitor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// other methods omitted for space&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The code closely mirrors the specification algorithm, ultimately resulting in the &lt;code class=&quot;highlighter-rouge&quot;&gt;onRejectionHandled&lt;/code&gt; method being called on the &lt;code class=&quot;highlighter-rouge&quot;&gt;Pledge&lt;/code&gt; constructor with an instance of &lt;code class=&quot;highlighter-rouge&quot;&gt;PledgeReactionEvent&lt;/code&gt;. This event can’t be cancelled, so there’s no reason to check the &lt;code class=&quot;highlighter-rouge&quot;&gt;returnValue&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;I did need to add a little bit of extra code at the end to toggle the monitoring of rejected promises. You only need to monitor the about-to-be-notified rejected promises list to know when to trigger the &lt;code class=&quot;highlighter-rouge&quot;&gt;unhandledrejection&lt;/code&gt; event. (The outstanding promise rejections weak set doesn’t need to be monitored.) To account for that, and to save resources, I turn on the monitor when there is at least one item in the about-to-be-notified rejected promises list and turn it off otherwise.&lt;/p&gt;

&lt;p&gt;The actual monitoring process is described in the HTML specification, as well, and is implemented as the &lt;code class=&quot;highlighter-rouge&quot;&gt;startMonitor()&lt;/code&gt; method.&lt;/p&gt;

&lt;h4 id=&quot;monitoring-for-promise-rejections&quot;&gt;Monitoring for promise rejections&lt;/h4&gt;

&lt;p&gt;The HTML specification&lt;sup id=&quot;fnref:1:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; says that the following steps should be taken to notify users of unhandled promise rejections:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Let &lt;em&gt;list&lt;/em&gt; be a copy of &lt;em&gt;settings object&lt;/em&gt;’s about-to-be-notified rejected promises &lt;em&gt;list&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;If &lt;em&gt;list&lt;/em&gt; is empty, return.&lt;/li&gt;
  &lt;li&gt;Clear &lt;em&gt;settings object&lt;/em&gt;’s about-to-be-notified rejected promises list.&lt;/li&gt;
  &lt;li&gt;Let &lt;em&gt;global&lt;/em&gt; be &lt;em&gt;settings object&lt;/em&gt;’s global object.&lt;/li&gt;
  &lt;li&gt;Queue a global task on the DOM manipulation task source given &lt;em&gt;global&lt;/em&gt; to run the following substep:
    &lt;ol&gt;
      &lt;li&gt;For each promise &lt;em&gt;p&lt;/em&gt; in &lt;em&gt;list&lt;/em&gt;:
        &lt;ol&gt;
          &lt;li&gt;If &lt;em&gt;p&lt;/em&gt;’s &lt;code class=&quot;highlighter-rouge&quot;&gt;[[PromiseIsHandled]]&lt;/code&gt; internal slot is true, continue to the next iteration of the loop.&lt;/li&gt;
          &lt;li&gt;Let &lt;em&gt;notHandled&lt;/em&gt; be the result of firing an event named &lt;code class=&quot;highlighter-rouge&quot;&gt;unhandledrejection&lt;/code&gt; at &lt;em&gt;global&lt;/em&gt;, using &lt;code class=&quot;highlighter-rouge&quot;&gt;PromiseRejectionEvent&lt;/code&gt;, with the &lt;code class=&quot;highlighter-rouge&quot;&gt;cancelable&lt;/code&gt; attribute initialized to true, the &lt;code class=&quot;highlighter-rouge&quot;&gt;promise&lt;/code&gt; attribute initialized to &lt;em&gt;p&lt;/em&gt;, and the &lt;code class=&quot;highlighter-rouge&quot;&gt;reason&lt;/code&gt; attribute initialized to the value of &lt;em&gt;p&lt;/em&gt;’s &lt;code class=&quot;highlighter-rouge&quot;&gt;[[PromiseResult]]&lt;/code&gt; internal slot.&lt;/li&gt;
          &lt;li&gt;If &lt;em&gt;notHandled&lt;/em&gt; is false, then the promise rejection is handled. Otherwise, the promise rejection is not handled.&lt;/li&gt;
          &lt;li&gt;If &lt;em&gt;p&lt;/em&gt;’s &lt;code class=&quot;highlighter-rouge&quot;&gt;[[PromiseIsHandled]]&lt;/code&gt; internal slot is false, add &lt;em&gt;p&lt;/em&gt; to &lt;em&gt;settings object&lt;/em&gt;’s outstanding rejected promises weak set.&lt;/li&gt;
        &lt;/ol&gt;
      &lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The specification further says:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This algorithm results in promise rejections being marked as &lt;strong&gt;handled&lt;/strong&gt; or &lt;strong&gt;not handled&lt;/strong&gt;. These concepts parallel handled and not handled script errors. If a rejection is still &lt;strong&gt;not handled&lt;/strong&gt; after this, then the rejection may be reported to a developer console.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So this part of the specification describes exactly how to determine when an &lt;code class=&quot;highlighter-rouge&quot;&gt;unhandledrejection&lt;/code&gt; event should be fired and what effect, if any, it has on a warning being output to the console. However, the specification doesn’t say when this should take place, so browsers are free to implement it in the way they want. For the purposes of this post, I decided to use &lt;code class=&quot;highlighter-rouge&quot;&gt;setInterval()&lt;/code&gt; to periodically check the about-to-be-notified rejected promises list. This code is encapsulated in the &lt;code class=&quot;highlighter-rouge&quot;&gt;startMonitor()&lt;/code&gt; method, which you can see here:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;RejectionTracker&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// other methods omitted for space&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;startMonitor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// only start monitor once&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timeoutId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timeoutId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setInterval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

            &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;aboutToBeNotified&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;aboutToBeNotified&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stopMonitor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PledgeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;isHandled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PledgeRejectionEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PledgeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
                &lt;span class=&quot;nx&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;onUnhandledRejection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;notHandled&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;returnValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PledgeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;isHandled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;outstandingRejections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;notHandled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`Pledge rejection was not caught: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PledgeSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;stopMonitor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;clearInterval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timeoutId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timeoutId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first step in &lt;code class=&quot;highlighter-rouge&quot;&gt;stopMonitor()&lt;/code&gt; is to ensure that only one timer is ever used, so I check to make sure that &lt;code class=&quot;highlighter-rouge&quot;&gt;timeoutId&lt;/code&gt; is &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt; before proceeding. Next, &lt;code class=&quot;highlighter-rouge&quot;&gt;list&lt;/code&gt; stores a reference to the current about-to-be-notified rejected promises list and then the property is overwritten with a new instance of &lt;code class=&quot;highlighter-rouge&quot;&gt;Set&lt;/code&gt; to ensure that the same promises aren’t processed by this check more than once. If there are no promises to process then the monitor is stopped and the function exits (this is not a part of the specification).&lt;/p&gt;

&lt;p&gt;Next, each pledge in &lt;code class=&quot;highlighter-rouge&quot;&gt;list&lt;/code&gt; is evaluated. Remember that the &lt;code class=&quot;highlighter-rouge&quot;&gt;PledgeSymbol.isHandled&lt;/code&gt; property indicates if there’s a rejection handler attached to the pledge, so if that is &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;, then you can safely skip processing that pledge. Otherwise, the &lt;code class=&quot;highlighter-rouge&quot;&gt;Pledge.onUnhandledRejection()&lt;/code&gt; method is called with an event object. Unlike with &lt;code class=&quot;highlighter-rouge&quot;&gt;Pledge.onRejectionHandled()&lt;/code&gt;, in this case you care about whether or not the event was cancelled, so &lt;code class=&quot;highlighter-rouge&quot;&gt;notHandled&lt;/code&gt; is set to the event’s return value.&lt;/p&gt;

&lt;p&gt;After that, the function checks &lt;code class=&quot;highlighter-rouge&quot;&gt;PledgeSymbol.isHandled&lt;/code&gt; again because it’s possible that the code inside of &lt;code class=&quot;highlighter-rouge&quot;&gt;Pledge.onUnhandledRejection()&lt;/code&gt; might have added a rejection handler. If this property is still &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;, then the pledge is added to the outstanding rejections weak set to track for any future rejection handler additions.&lt;/p&gt;

&lt;p&gt;To finish up the algorithm, if &lt;code class=&quot;highlighter-rouge&quot;&gt;notHandled&lt;/code&gt; is &lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt;, that’s when an error is output to the console. Keep in mind that the &lt;code class=&quot;highlighter-rouge&quot;&gt;notHandled&lt;/code&gt; variable is the sole determinant of whether or not a console error is output; the &lt;code class=&quot;highlighter-rouge&quot;&gt;PledgeSymbol.isHandled&lt;/code&gt; property is a completely separate value that only indicates if a rejection handler is present.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;stopMonitor()&lt;/code&gt; method simply cancels the timer and resets the &lt;code class=&quot;highlighter-rouge&quot;&gt;timeoutId&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With that, the &lt;code class=&quot;highlighter-rouge&quot;&gt;RejectionTracker&lt;/code&gt; class is complete and all of the unhandled rejection tracking from browser implementations are now part of the Pledge library.&lt;/p&gt;

&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h2&gt;

&lt;p&gt;This post covered how browsers track unhandled promise rejections, which is a bit different than how Node.js tracks them. The browser triggers an &lt;code class=&quot;highlighter-rouge&quot;&gt;unhandledrejection&lt;/code&gt; event when a rejected promise is missing a rejection handler as well as outputting a message to the console. If the promise later has a rejection handler assigned, then a &lt;code class=&quot;highlighter-rouge&quot;&gt;rejectionhandled&lt;/code&gt; event is triggered.&lt;/p&gt;

&lt;p&gt;The description of how this functionality works is spread across both the ECMA-262 and HTML specifications, with the former defining only a small, abstract API while the latter provides explicit instructions to browsers on how to track unhandled rejections.&lt;/p&gt;

&lt;p&gt;All of the code from this series is available in the &lt;a href=&quot;https://github.com/humanwhocodes/pledge&quot;&gt;Pledge&lt;/a&gt; on GitHub. I hope you’ll download it and try it out to get a better understanding of promises.&lt;/p&gt;

&lt;p&gt;And thank you to my &lt;a href=&quot;https://github.com/sponsors/nzakas&quot;&gt;sponsors&lt;/a&gt;, whose donations supported parts 5 through 7 of this series. If you enjoyed this series and would like to see more in-depth blog posts, please consider &lt;a href=&quot;https://github.com/sponsors/nzakas&quot;&gt;sponsoring me&lt;/a&gt;. Your support allows independent software developers like me to continue our work.&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://html.spec.whatwg.org/multipage/webappapis.html#unhandled-promise-rejections&quot;&gt;Unhandled promise rejections&lt;/a&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt; &lt;a href=&quot;#fnref:1:1&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://tc39.es/ecma262/#sec-properties-of-promise-instances&quot;&gt;Properties of Promise Instances&lt;/a&gt; &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://tc39.es/ecma262/#sec-host-promise-rejection-tracker&quot;&gt;HostPromiseRejectionTracker ( promise, operation )&lt;/a&gt; &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:4&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://tc39.es/ecma262/#sec-performpromisethen&quot;&gt;PerformPromiseThen ( promise, onFulfilled, onRejected [ , resultCapability ] )&lt;/a&gt; &lt;a href=&quot;#fnref:4&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:5&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://tc39.es/ecma262/#sec-rejectpromise&quot;&gt;RejectPromise ( promise, reason )&lt;/a&gt; &lt;a href=&quot;#fnref:5&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:6&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://html.spec.whatwg.org/multipage/webappapis.html#the-hostpromiserejectiontracker-implementation&quot;&gt;HostPromiseRejectionTracker(promise, operation)&lt;/a&gt; &lt;a href=&quot;#fnref:6&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content:encoded>
    </item>
    
  </channel>
</rss>
