JavaScript Concurrency - Adam Boduch - E-Book

JavaScript Concurrency E-Book

Adam Boduch

0,0
34,79 €

-100%
Sammeln Sie Punkte in unserem Gutscheinprogramm und kaufen Sie E-Books und Hörbücher mit bis zu 100% Rabatt.

Mehr erfahren.
Beschreibung

Concurrent programming may sound abstract and complex, but it helps to deliver a better user experience. With single threaded JavaScript, applications lack dynamism. This means that when JavaScript code is running, nothing else can happen. The DOM can’t update, which means the UI freezes. In a world where users expect speed and responsiveness – in all senses of the word – this is something no developer can afford.

Fortunately, JavaScript has evolved to adopt concurrent capabilities – one of the reasons why it is still at the forefront of modern web development. This book helps you dive into concurrent JavaScript, and demonstrates how to apply its core principles and key techniques and tools to a range of complex development challenges. Built around the three core principles of concurrency – parallelism, synchronization, and conservation – you’ll learn everything you need to unlock a more efficient and dynamic JavaScript, to lay the foundations of even better user experiences.

Throughout the book you’ll learn how to put these principles into action by using a range of development approaches. Covering everything from JavaScript promises, web workers, generators and functional programming techniques, everything you learn will have a real impact on the performance of your applications. You’ll also learn how to move between client and server, for a more frictionless and fully realized approach to development. With further guidance on concurrent programming with Node.js, JavaScript Concurrency is committed to making you a better web developer.

The best developers know that great design is about more than the UI – with concurrency, you can be confident every your project will be expertly designed to guarantee its dynamism and power.

Das E-Book können Sie in Legimi-Apps oder einer beliebigen App lesen, die das folgende Format unterstützen:

EPUB
MOBI

Seitenzahl: 348

Veröffentlichungsjahr: 2015

Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



Table of Contents

JavaScript Concurrency
Credits
About the Author
About the Reviewer
www.PacktPub.com
Support files, eBooks, discount offers, and more
Why subscribe?
Free access for Packt account holders
Preface
What this book covers
What you need for this book
Who this book is for
Conventions
Reader feedback
Customer support
Downloading the example code
Errata
Piracy
Questions
1. Why JavaScript Concurrency?
Synchronous JavaScript
Synchronicity is easy to understand
Asynchronous is inevitable
Asynchronous browsers
Types of concurrency
Asynchronous actions
Parallel actions
JavaScript concurrency principles: Parallelize, Synchronize, Conserve
Parallelize
Synchronize
The Promise API
Conserve
Summary
2. The JavaScript Execution Model
Everything is a task
Meet the players
The Execution environment
Event loops
Task queues
Execution contexts
Maintaining execution state
Job queues
Creating tasks using timers
Using setTimeout()
Using setInterval()
Responding to DOM events
Event targets
Managing event frequency
Responding to network events
Making requests
Coordinating requests
Concurrency challenges with this model
Limited opportunity for parallelism
Synchronization through callbacks
Summary
3. Synchronizing with Promises
Promise terminology
Promise
State
Executor
Resolver
Rejector
Thenable
Resolving and rejecting promises
Resolving promises
Rejecting promises
Empty promises
Reacting to promises
Resolution job queues
Using promised data
Error callbacks
Always reacting
Resolving other promises
Promise–like objects
Building callback chains
Promises only change state once
Immutable promises
Many then callbacks, many promises
Passing promises around
Synchronizing several promises
Waiting on promises
Cancelling promises
Promises without executors
Summary
4. Lazy Evaluation with Generators
Call stacks and memory allocation
Bookmarking function contexts
Sequences instead of arrays
Creating generators and yielding values
Generator function syntax
Yielding values
Iterating over generators
Infinite sequences
No end in sight
Alternating sequences
Deferring to other generators
Selecting a strategy
Interweaving generators
Passing data to generators
Reusing generators
Lightweight map/reduce
Coroutines
Creating coroutine functions
Handling DOM events
Handling promised values
Summary
5. Working with Workers
What are workers?
OS threads
Event targets
True parallelism
Types of workers
Dedicated workers
Sub-workers
Shared workers
Worker environments
What's available, what isn't?
Loading scripts
Communicating with workers
Posting messages
Message serialization
Receiving messages from workers
Sharing application state
Sharing memory
Fetching resources
Communicating between pages
Performing sub-tasks with sub-workers
Dividing work into tasks
A word of caution
Error handling in web workers
Error condition checking
Exception handling
Summary
6. Practical Parallelism
Functional programming
Data in, data out
Immutability
Referential transparency and time
Do we need to go parallel?
How big is the data?
Hardware concurrency capabilities
Creating tasks and assigning work
Candidate problems
Embarrassingly parallel
Searching collections
Mapping and reducing
Keeping the DOM responsive
Bottom halves
Translating DOM manipulation
Translating DOM events
Summary
7. Abstracting Concurrency
Writing concurrent code
Hiding the concurrency mechanism
Without concurrency
Worker communication with promises
Helper functions
Extending postMessage()
Synchronizing worker results
Lazy workers
Reducing overhead
Generating values in workers
Lazy worker chains
Using Parallel.js
How it works
Spawning workers
Mapping and reducing
Worker pools
Allocating pools
Scheduling jobs
Summary
8. Evented IO with NodeJS
Single threaded IO
IO is slow
IO events
Multi-threading challenges
More connections, more problems
Deploying to the Internet
The C10K problem
Lightweight event handlers
Evented network IO
Handling HTTP requests
Streaming responses
Proxy network requests
Evented file IO
Reading from files
Writing to files
Streaming reads and writes
Summary
9. Advanced NodeJS Concurrency
Coroutines with Co
Generating promises
Awaiting values
Resolving values
Asynchronous dependencies
Wrapping coroutines
Child Processes
Blocking the event loop
Forking processes
Spawning external processes
Inter-process communication
Process Clusters
Challenges with process management
Abstracting process pools
Server clusters
Proxying requests
Facilitating micro-services
Informed load balancing
Summary
10. Building a Concurrent Application
Getting started
Concurrency first
Retrofitting concurrency
Application types
Requirements
The overall goal
The API
The UI
Building the API
The HTTP server and routing
Co-routines as handlers
The create chat handler
The join chat handler
The load chat handler
The send message handler.
Static handlers
Building the UI
Talking to the API
Implementing the HTML
DOM events and manipulation
Adding an API worker
Additions and improvements
Clustering the API
Cleaning up chats
Asynchronous entry points
Who's typing?
Leaving chats
Polling timeouts
Summary
Index

JavaScript Concurrency

JavaScript Concurrency

Copyright © 2015 Packt Publishing

All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.

Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the author, nor Packt Publishing, and its dealers and distributors will be held liable for any damages caused or alleged to be caused directly or indirectly by this book.

Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.

First published: December 2015

Production reference: 1181215

Published by Packt Publishing Ltd.

Livery Place

35 Livery Street

Birmingham B3 2PB, UK.

ISBN 978-1-78588-923-3

www.packtpub.com

Credits

Author

Adam Boduch

Reviewer

August Marcello III

Commissioning Editor

Edward Gordon

Acquisition Editor

Ruchita Bhansali

Content Development Editor

Divij Kotian

Technical Editor

Gebin George

Copy Editor

Yesha Gangani

Project Coordinator

Nikhil Nair

Proofreader

Safis Editing

Indexer

Tejal Daruwale Soni

Graphics

Jason Monteiro

Production Coordinator

Melwyn Dsa

Cover Work

Melwyn Dsa

About the Author

Adam Boduch has been involved with large-scale JavaScript development for nearly 10 years. Before moving to the front-end, he worked on several large-scale cloud computing products, using Python and Linux. No stranger to complexity, Adam has practical experience with real-world software systems, and the scaling challenges they pose. He is the author of several JavaScript books, including JavaScript at Scale, Packt Publishing, and is passionate about innovative user experiences and high performance.

About the Reviewer

August Marcello III is a highly passionate software engineer with nearly two decades of experience in the design, implementation, and deployment of modern client-side web application architectures in enterprise. An exclusive focus on delivering compelling SaaS-based User Experiences throughout the Web ecosystem has proven to be rewarding both personally and professionally. His passion for emerging technologies in general, combined with a particular focus on forward-thinking JavaScript platforms, have been a primary driver in his pursuit of technical excellence. When he's not coding, he can be found trail running, mountain biking, and spending time with his family and friends.

Many thanks to Chuck, Mark, Eric, and Adam, who I have had the privilege to work with and learn from. With gratitude to my family, friends, and the experiences I have been blessed to be a part of.

www.PacktPub.com

Support files, eBooks, discount offers, and more

For support files and downloads related to your book, please visit www.PacktPub.com.

Did you know that Packt offers eBook versions of every book published, with PDF and ePub files available? You can upgrade to the eBook version at www.PacktPub.com and as a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at <[email protected]> for more details.

At www.PacktPub.com, you can also read a collection of free technical articles, sign up for a range of free newsletters and receive exclusive discounts and offers on Packt books and eBooks.

https://www2.packtpub.com/books/subscription/packtlib

Do you need instant solutions to your IT questions? PacktLib is Packt's online digital book library. Here, you can search, access, and readPackt's entire library of books.

Why subscribe?

Fully searchable across every book published by PacktCopy and paste, print, and bookmark contentOn demand and accessible via a web browser

Free access for Packt account holders

If you have an account with Packt at www.PacktPub.com, you can use this to access PacktLib today and view 9 entirely free books. Simply use your login credentials for immediate access.

For Melissa, Jason, and Simon, thanks for all your love and support.

Preface

It wasn't long ago when I would dread using many of the web applications I had come to depend on. When they worked, they were fantastic; when they didn't, it was a nightmare. Especially frustrating was the clear fact that there weren't any bugs in the JavaScript code driving the application. No, the problem was that often there was too much code running, perhaps because it was processing a large data set. The end result was always the same: the UI would freeze, and I would helplessly curse the Web.

Nowadays, this doesn't happen as frequently. We've fixed many of the common JavaScript problems from years ago. Something that hasn't caught on as fast as I had hoped is concurrency in JavaScript. There are little bits of concurrency sprinkled all throughout our applications, but seldom do we see truly concurrent JavaScript code.

Let's change the status quo.

What this book covers

Chapter 1, Why JavaScript Concurrency?, is an introduction to concurrency in JavaScript.

Chapter 2, The JavaScript Execution Model, takes you through the mechanisms that run our JavaScript code.

Chapter 3, Synchronizing with Promises, looks at synchronization techniques using promises.

Chapter 4, Lazy Evaluation with Generators, will get your grips to conserving resources by computing lazily.

Chapter 5, Working with Workers, looks at achieving true parallelism in JavaScript.

Chapter 6, Practical Parallelism, will help you in identifying the right parallelization problems to solve.

Chapter 7, Abstracting Concurrency, will get your hands dirty writing concurrent code that reads like regular code.

Chapter 8, Evented IO with NodeJS, will show you how concurrency semantics work in this environment.

Chapter 9, Advanced NodeJS Concurrency, is learning about specific Node concurrency issues.

Chapter 10, Building a Concurrent Application, is all about putting it all together.

What you need for this book

Requirements for this book are as follows:

A recent version of any major browserNodeJS (at least 4.0)A code editor

Who this book is for

JavaScript Concurrency is written for any JavaScript developer who wants to learn how to write more efficient, powerful, and maintainable applications that utilize the latest developments in the JavaScript language.

All aspects of concurrent, asynchronous, and parallel programming are covered from first principles, and by the end of the book you'll be able to create a fully-worked application that leverages all the topics covered in the book.

Reader feedback

Feedback from our readers is always welcome. Let us know what you think about this book—what you liked or disliked. Reader feedback is important for us as it helps us develop titles that you will really get the most out of.

To send us general feedback, simply e-mail <[email protected]>, and mention the book's title in the subject of your message.

If there is a topic that you have expertise in and you are interested in either writing or contributing to a book, see our author guide at www.packtpub.com/authors.

Customer support

Now that you are the proud owner of a Packt book, we have a number of things to help you to get the most from your purchase.

Downloading the example code

You can download the example code files from your account at http://www.packtpub.com for all the Packt Publishing books you have purchased. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

Errata

Although we have taken every care to ensure the accuracy of our content, mistakes do happen. If you find a mistake in one of our books—maybe a mistake in the text or the code—we would be grateful if you could report this to us. By doing so, you can save other readers from frustration and help us improve subsequent versions of this book. If you find any errata, please report them by visiting http://www.packtpub.com/submit-errata, selecting your book, clicking on the Errata Submission Form link, and entering the details of your errata. Once your errata are verified, your submission will be accepted and the errata will be uploaded to our website or added to any list of existing errata under the Errata section of that title.

To view the previously submitted errata, go to https://www.packtpub.com/books/content/support and enter the name of the book in the search field. The required information will appear under the Errata section.

Piracy

Piracy of copyrighted material on the Internet is an ongoing problem across all media. At Packt, we take the protection of our copyright and licenses very seriously. If you come across any illegal copies of our works in any form on the Internet, please provide us with the location address or website name immediately so that we can pursue a remedy.

Please contact us at <[email protected]> with a link to the suspected pirated material.

We appreciate your help in protecting our authors and our ability to bring you valuable content.

Questions

If you have a problem with any aspect of this book, you can contact us at <[email protected]>, and we will do our best to address the problem.

JavaScript concurrency principles: Parallelize, Synchronize, Conserve

Now that we've been through the basics of what concurrency is, and its role in front-end web development, let's look at some fundamental concurrency principles of JavaScript development. These principles are merely tools that inform our design choices when we write concurrent JavaScript code.

When we apply these principles, they force us to step back and ask the appropriate questions before we move forward with implementation. In particular, they're the why and how questions:

Why are we implementing this concurrent design?What do we hope to get out of it that we couldn't otherwise get out of a simpler synchronous approach?How do we implement concurrency in a way that's unobtrusive to the features of our applications?

Here's a reference visualization of each concurrency principle, feeding on one another during the development process. And with that, we'll turn our attention to each principle for further exploration:

Parallelize

The parallelize principle means taking advantage of modern CPU capabilities to compute results in less time. This is now possible in any modern browser or NodeJS environment. In the browser, we can achieve true parallelism using web workers. In Node, we can achieve true parallelism by spawning new processes. Here's what the CPU looks like from the browser's perspective:

With the goal being more computations in less time, we must now ask ourselves why we want to do this? Besides the fact that raw performance is super cool in it's own right, there has to be some tangible impact for the user. This principle makes us look at our parallel code and ask—what does the user get out of this? The answer is that we can compute using larger data sets as input, and have a smaller opportunity of an unresponsive user experience due to long-running JavaScript.

It's important to scrutinize the tangible benefit of going parallel because when we do so, we add complexity to our code that wouldn't otherwise be there. So if the user sees the same result no matter what we do, the parallelize principle probably isn't applicable. On the other hand, if scalability is important and there's a strong possibility of growing data set sizes, the trade off of code simplicity for parallelism is probably worthwhile. Here's a checklist to follow when thinking about the parallelize principle:

Does our application perform expensive computations against large data sets?As our data sets grow in size, is there potential for processing bottlenecks that negatively impact the user experience?Do our users currently experience bottlenecks in our application's performance?How feasible is parallelism in our design, given other constraints? What are the trade-offs?Do the benefits of our concurrency implementation outweigh the overhead costs, either in terms of user-perceived latency or in terms of code maintainability?

Synchronize

The synchronize principle is about the mechanisms used to coordinate concurrent actions and the abstractions of those mechanisms. Callback functions are a JavaScript notion with deep roots. It's the obvious tool of choice when we need to run some code, but we don't want to run it now. We want to run it when some condition becomes true. By and large, there's nothing inherently wrong with this approach. Used in isolation, the callback pattern is probably the most succinct, readable concurrency pattern that we can use. Callbacks fall apart when there are plenty them, and lots of dependencies between them.

The Promise API

The Promise API is the core JavaScript language construct, introduced in ECMAScript 6 to address the synchronization woes faced by every application on the planet. It's a simple API that actually makes use of callbacks (yes, we're fighting callbacks with callbacks). The aim of promises isn't to eliminate callbacks, it's to remove the unnecessary callbacks. Here's what a promise that's used to synchronize two network fetch calls looks like:

What's crucial about promises is that they're a generic synchronization mechanism. This means that they're not specifically made for network requests, web workers, or DOM events. We, the programmers, have to wrap our asynchronous actions with promises and resolve them as necessary. The reason why this is a good thing is because the callers that rely on the promise interface don't care about what's going on inside the promise. As the name implies, it's a promise to resolve a value at some point. This could be in 5 seconds or immediately. The data can come from a network resource or a web worker. The caller doesn't care, because it makes an assumption of concurrency, which means we can fulfill it any in way we like without breaking the application. Here's a modified version of the preceding diagram, which will give us a taste of what promises make possible:

When we learn to treat values as values at some point in the future, concurrent code is suddenly much more approachable. Promises, and similar mechanisms, can be used to synchronize just network requests, or just web worker events. But they're real power is using them to write concurrent applications, where concurrency is the default. Here's a checklist to reference when thinking about the synchronize principle:

Does our application heavily rely on callback functions as a synchronization mechanism?Do we often have to synchronize more than one asynchronous event such as network requests?Do our callback functions contain more synchronization boilerplate code than application code?What kind of assumptions does our code make about the concurrency mechanisms that drive asynchronous events?If we had a magic kill concurrency button, would our application still behave as expected?

Conserve

The conserve principle is about saving on compute and memory resources. This is done by using lazy evaluation techniques. The name lazy stems from the idea that we don't actually compute a new value until we're sure we actually need it. Imagine an application component that renders page elements. We can pass this component the exact data that it needs to render. This means that several computations take place before the component actually needs it. It also means that the data that's used needs to be allocated into memory, so that we can pass it to the component. There's nothing wrong with this approach. In fact, it's the standard way to pass data around in our JavaScript components.

The alternative approach uses lazy evaluation to achieve the same result. Rather than computing the values to be rendered, then allocating them in a structure to be passed, we compute one item, and then render it. Think of this as a kind of cooperative multi-tasking, where the larger action is broken down into smaller tasks that pass the focus of control back and forth.

Here's an eager approach to compute data and pass it to the component that renders UI elements:

There's two undesirable aspects to this approach. First, the transformation happens up-front, which could be a costly computation. What happens if the component is unable to render it for whatever reason—due to some constraint? Then we've performed this computation to transform data that wasn't needed. As a corollary, we've allocated a new data structure for the transformed data so that we could pass it to our component. This transient memory structure doesn't really serve any purpose, as it's garbage-collected immediately. Let's take a look at what the lazy approach might look like:

Using the lazy approach, we're able to remove the expensive transform computation that happens up-front. Instead, we transform only one item at a time. We're also able to remove the up-front allocation of the transformed data structure. Instead, only the transformed item is passed into the component. Then, the component can ask for another item or stop. The conserve principle uses concurrency as a means to only compute what's needed and only allocate memory that's needed.

The following checklist will help us think about the conserve principle when writing concurrent code:

Are we computing values that are never used?Do we only allocate data structures as a means to pass them from one component to the next?Do we chain-together data transformation actions?

Summary

In this chapter, we introduced some motivations for concurrency in JavaScript. While synchronous JavaScript is easy to maintain and understand, asynchronous JavaScript code is inevitable on the web. So it's important to make concurrency our default assumption when writing JavaScript applications.

There's two main types of concurrency we're interested in—asynchronous actions and parallel actions. Asynchronicity is about the time ordering of actions, which gives the impression that things are happening at the same time. Without this type of concurrency, the user experience would suffer greatly, because it would constantly be waiting on other actions to complete. Parallelism is another type of concurrency that solves a different type of problem, where we want to increase performance by computing results faster.

Finally, we looked at the three principles of concurrency in JavaScript programming. The parallelize principle is about leveraging the multi-core CPUs found in modern systems. The synchronize principle is about creating abstractions that enable us to write concurrent code, hiding the concurrency mechanisms from our feature code. The conserve principle uses lazy evaluation to only compute what is needed and to avoid unnecessary memory allocations.

In the next chapter, we'll turn our attention to the JavaScript execution environment. To be effective with JavaScript concurrency, we need a sound understanding of what's actually happening when our code is run.

Chapter 2. The JavaScript Execution Model

The first chapter of this book explored the state of JavaScript concurrency. Generally speaking, dealing with concurrency in JavaScript applications is anything but a trivial matter. There's a lot to think about when writing concurrent JavaScript code, and the kind of solutions that we come up with are often unorthodox. There's a lot of callbacks, and wading through all of them is enough to drive a person insane. We also caught a glimpse of how our pattern of writing concurrent JavaScript code has started to change with existing concurrency components. Web workers have started to mature, and JavaScript language concurrency constructs have only just been introduced.

The language and the runtime environment only get us partway there. We need to think about concurrency at the design level, rather than after the fact. Concurrency should be the default. This is easy to say and very difficult to do. Throughout this book, we're going to explore all that the JavaScript concurrency features have to offer, and how we can best use them to our advantage as design tools. But, before we do this, we need to go into depth on what's really happening when our JavaScript runs. This knowledge is an essential input to designing concurrent applications, because we'll know exactly what to expect when choosing one concurrency mechanism over another.

In this chapter, we'll start with the browser environment, by looking at all the subsystems that our code touches—such as the JavaScript interpreter, the task queue, and the DOM itself. Then we'll walk through some code that will shed some light on what's really happening behind the scenes to orchestrate our code. We'll close the chapter with a discussion on the challenges that we face with this model.

Everything is a task

When we visit a web page, a whole environment is created for us within the browser. This environment has several subsystems that enable the webpage we're looking at to look and behave as it should according toWorld Wide Web Consortium (W3C) specs. Tasks are the fundamental abstraction inside a web browser. Anything that happens is either a task itself, or a smaller part of a larger task.

Note

If you're reading any of the W3C specifications, the term "user agent" is used instead of "web browser". In 99.9% of cases, the major browser vendors are what we're reading about.

In this section, we'll look at the major components of these environments, and how task queues and event loops facilitate the communication between these components, to realize the overall appearance and behavior of the web page.

Meet the players

Let's introduce some terminology that will help us throughout the various sections in this chapter:

Execution environment: This container gets created whenever a new web page is opened. It's the all-encompassing environment, which has everything that our JavaScript code will interact with. It also serves as a sandbox—our JavaScript code can't reach outside of this environment.JavaScript interpreter: This is the component that's responsible for parsing and executing our JavaScript source code. It's the browser's job to augment the interpreter with globals, such as window, and XMLHttpRequest.Task queue: Tasks are queued whenever something needs to happen. An execution environment has at least one of these queues, but typically, it has several of them.Event loop: An execution environment has a single event loop that's responsible for servicing all task queues. There's only one event loop, because there's only one thread.