Web Api Cookbook Level Up Your Javascript Applications Converted Joe Attardi download https://ebookbell.com/product/web-api-cookbook-level-up-your- javascript-applications-converted-joe-attardi-56376958 Explore and download more ebooks at ebookbell.com
Here are some recommended products that we believe you will be interested in. You can click the link to download. Web Api Cookbook Level Up Your Javascript Applications 1st Edition Joe Attardi https://ebookbell.com/product/web-api-cookbook-level-up-your- javascript-applications-1st-edition-joe-attardi-56785342 Web Api Cookbook Level Up Your Javascript Applications Joe Attardi https://ebookbell.com/product/web-api-cookbook-level-up-your- javascript-applications-joe-attardi-56409582 Restful Web Api Patterns And Practices Cookbook Connecting And Orchestrating Microservices And Distributed Data 1st Edition Mike Amundsen https://ebookbell.com/product/restful-web-api-patterns-and-practices- cookbook-connecting-and-orchestrating-microservices-and-distributed- data-1st-edition-mike-amundsen-53003204 Restful Web Api Patterns And Practices Cookbook Author Names Here https://ebookbell.com/product/restful-web-api-patterns-and-practices- cookbook-author-names-here-232358360
Restful Web Api Patterns And Practices Cookbook Mike Amundsen https://ebookbell.com/product/restful-web-api-patterns-and-practices- cookbook-mike-amundsen-232358362 Web Api Development For The Absolute Beginner A Stepbystep Approach To Learning The Fundamentals Of Web Api Development With Net 7 1st Edition Irina Dominte https://ebookbell.com/product/web-api-development-for-the-absolute- beginner-a-stepbystep-approach-to-learning-the-fundamentals-of-web- api-development-with-net-7-1st-edition-irina-dominte-51638020 Web Api Development With Aspnet Core 8 Learn Techniques Patterns And Tools For Building Highperformance Robust And Scalable Web Apis 1st Edition Xiaodi Yan https://ebookbell.com/product/web-api-development-with-aspnet- core-8-learn-techniques-patterns-and-tools-for-building- highperformance-robust-and-scalable-web-apis-1st-edition-xiaodi- yan-56790244 Web Api Development With Aspnet Core 8 Learn Techniques Patterns And Tools For Building Highperformance Robust And Scalable Web Apis Xiaodi Yan https://ebookbell.com/product/web-api-development-with-aspnet- core-8-learn-techniques-patterns-and-tools-for-building- highperformance-robust-and-scalable-web-apis-xiaodi-yan-56548330 Restful Web Api Design With Nodejs Design And Implement Comprehensive Restful Solutions In Nodejs 1st Ed Valentin Bojinov https://ebookbell.com/product/restful-web-api-design-with-nodejs- design-and-implement-comprehensive-restful-solutions-in-nodejs-1st-ed- valentin-bojinov-50194976
Web API Cookbook Level Up Your JavaScript Applications Joe Attardi
Web API Cookbook by Joe Attardi Copyright © 2024 Joseph Attardi. All rights reserved. Printed in the United States of America. Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472. O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://oreilly.com). For more information, contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com. Acquisitions Editor: Amanda Quinn Development Editor: Virginia Wilson Production Editor: Beth Kelly Copyeditor: Piper Editorial Consulting, LLC Proofreader: Tove Innis Indexer: WordCo Indexing Services, Inc. Interior Designer: David Futato Cover Designer: Karen Montgomery Illustrator: Kate Dullea April 2024: First Edition
Revision History for the First Edition 2024-03-21: First Release See http://oreilly.com/catalog/errata.csp?isbn=9781098150693 for release details. The O’Reilly logo is a registered trademark of O’Reilly Media, Inc. Web API Cookbook, the cover image, and related trade dress are trademarks of O’Reilly Media, Inc. The views expressed in this work are those of the author and do not represent the publisher’s views. While the publisher and the author have used good faith efforts to ensure that the information and instructions contained in this work are accurate, the publisher and the author disclaim all responsibility for errors or omissions, including without limitation responsibility for damages resulting from the use of or reliance on this work. Use of the information and instructions contained in this work is at your own risk. If any code samples or other technology this work contains or describes is subject to open source licenses or the intellectual property rights of others, it is your responsibility to ensure that your use thereof complies with such licenses and/or rights. 978-1-098-15069-3 [LSI]
Preface JavaScript has come a long way since its introduction in late 1995. In the early days, the core APIs built into web browsers were limited. More advanced functionality typically required third-party JavaScript libraries, or in some cases even browser plug-ins. A web API is a series of global objects and functions exposed by the browser. Your JavaScript code can use these to interact with the Document Object Model (DOM), perform network communication, integrate with native device features, and much more. POLYFILLS Older browsers can take advantage of some of these APIs by using polyfills. A polyfill is a JavaScript library that implements missing functionality. Polyfills are typically used for web API features that aren’t implemented in older browsers. While beneficial, polyfills do have some drawbacks: They are loaded as third-party libraries, which add to your bundle size. They generally aren’t maintained by the browser teams, so there may be bugs or inconsistencies. Some advanced functionality can’t be polyfilled in a performant way, or at all. The Power of Modern Browsers Modern web APIs have two big advantages for the web platform:
No more plug-ins In the past, much of this functionality was only available to native applications or clunky browser plug-ins. (Remember ActiveX and Flash?) Fewer third-party dependencies Modern browsers provide considerable functionality that used to require third-party JavaScript libraries. Popular libraries such as jQuery, Lodash, and Moment are usually not needed anymore. Drawbacks of Third-Party Libraries Third-party libraries can be helpful with older browsers or newer functionality, but they have some costs: More code to download Using libraries increases the amount of JavaScript the browser has to load. Whether it’s bundled with your app or loaded separately from a content delivery network (CDN), your browser still has to download it. This translates into potentially longer loading times and higher battery usage on mobile devices. Increased risk Open source libraries, even popular ones, can be abandoned. When bugs or security vulnerabilities are found, there’s no guarantee of an update. Browsers, in general, are supported by large companies (the major browsers are from Google, Mozilla, Apple, and Microsoft), and it’s more likely that these issues will be fixed.
This isn’t to say that third-party libraries are bad. There are many benefits as well, especially if you need to support older browsers. Like everything in software development, library use is a balancing act.
Who This Book Is For This book is intended for software developers with some experience with JavaScript who want to get the most out of the web platform. It assumes that you have a good knowledge of the JavaScript language itself: syntax, language features, and standard library functions. You should also have a working knowledge of the DOM APIs used for building interactive, browser-based JavaScript applications. There is a wide range of recipes in this book; there’s something for developers of all skill and experience levels. What’s in This Book Each chapter contains a set of recipes—code examples for accomplishing a specific task. Each recipe has three sections: Problem Describes the problem the recipe solves. Solution Contains code and explanation that implements the recipe solution. Discussion A deeper discussion of the topic. This section may contain additional code examples and comparisons with other techniques. Code samples and live demos are on the companion website, https://WebAPIs.info.
Additional Resources By its nature, the web is changing all the time. There are many great resources available online to help clarify any questions that might come up. CanIUse.com At the time of writing, some APIs in this book are still in development or an “experimental” phase. Watch for compatibility notes in recipes that use these APIs. For most features, you can check the latest compatibility data at https://CanIUse.com. You can search by the name of a feature and see the latest information about which browser versions support the API and any limitations or caveats for particular browser versions. MDN Web Docs MDN Web Docs is the de facto API documentation for all things web. It covers all the APIs from this book in great detail, as well as other topics such as CSS and HTML. It contains in-depth articles and tutorials as well as API specifications. Specifications When in doubt, the specification of a feature or API is the definitive resource. They aren’t the most exciting reads, but they are a good place to look for details about edge cases or expected behavior. Different APIs have different standards, but most can be found either from the Web Hypertext Application Technology Working Group (WHATWG) or the World Wide Web Consortium (W3C). The standards for ECMAScript (which specifies features in the JavaScript Language) are maintained and developed by the Ecma International Technical Committee 39, better known as TC39.
Conventions Used in This Book The following typographical conventions are used in this book: Italic Indicates new terms, URLs, email addresses, filenames, and file extensions. Constant width Used for program listings, as well as within paragraphs to refer to program elements such as variable or function names, databases, data types, environment variables, statements, and keywords. Constant width bold Shows commands or other text that should be typed literally by the user. Constant width italic Shows text that should be replaced with user-supplied values or by values determined by context. TIP This element signifies a tip or suggestion. NOTE This element signifies a general note.
WARNING This element indicates a warning or caution. Using Code Examples Supplemental material (code examples, exercises, etc.) is available for download at https://github.com/joeattardi/web-api-cookbook. Also check out the companion website, where many of the code samples and recipes in this book are expanded into full, live, working examples. If you have a technical question or a problem using the code examples, please send email to bookquestions@oreilly.com. This book is here to help you get your job done. In general, if example code is offered with this book, you may use it in your programs and documentation. You do not need to contact us for permission unless you’re reproducing a significant portion of the code. For example, writing a program that uses several chunks of code from this book does not require permission. Selling or distributing examples from O’Reilly books does require permission. Answering a question by citing this book and quoting example code does not require permission. Incorporating a significant amount of example code from this book into your product’s documentation does require permission. We appreciate, but generally do not require, attribution. An attribution usually includes the title, author, publisher, and ISBN. For example: “Web API Cookbook by Joseph Attardi (O’Reilly). Copyright 2024 Joe Attardi, 978-1-098-15069-3.” If you feel your use of code examples falls outside fair use or the permission given above, feel free to contact us at permissions@oreilly.com.
O’Reilly Online Learning NOTE For more than 40 years, O’Reilly Media has provided technology and business training, knowledge, and insight to help companies succeed. Our unique network of experts and innovators share their knowledge and expertise through books, articles, and our online learning platform. O’Reilly’s online learning platform gives you on-demand access to live training courses, in-depth learning paths, interactive coding environments, and a vast collection of text and video from O’Reilly and 200+ other publishers. For more information, visit https://oreilly.com. How to Contact Us Please address comments and questions concerning this book to the publisher: O’Reilly Media, Inc. 1005 Gravenstein Highway North Sebastopol, CA 95472 800-889-8969 (in the United States or Canada) 707-827-7019 (international or local) 707-829-0104 (fax) support@oreilly.com
https://www.oreilly.com/about/contact.html We have a web page for this book, where we list errata, examples, and any additional information. You can access this page at https://oreil.ly/web-api- cookbook. For news and information about our books and courses, visit https://oreilly.com. Find us on LinkedIn: https://linkedin.com/company/oreilly-media Watch us on YouTube: https://youtube.com/oreillymedia Acknowledgments First of all, a heartfelt thanks to my family and friends for supporting me, especially to my wife, Liz, and son, Benjamin, for putting up with listening to my incessant typing. When I am in the zone, I tend to type very quickly and loudly. Thank you to Amanda Quinn, Senior Content Acquisitions Editor, for bringing me on as an O’Reilly author. I’ve read countless O’Reilly books over the years and never thought I’d be writing one of my own one day. Thanks also to Louise Corrigan for making the introduction to Amanda and getting the process started (and who worked with me some years back publishing my very first book!). Special thanks to Virginia Wilson, Senior Development Editor, for guiding me throughout the process of writing the book and meeting regularly to keep things on track. I’d also like to thank the fantastic technical reviewers on this book: Martine Dowden, Schalk Neethling, Sarah Shook, and Adam Scott. The book turned out far better with their helpful feedback. Lastly, I’d like to give a shout out to the teams designing and developing these modern web APIs. Without them, this book would not exist!
Chapter 1. Asynchronous APIs Introduction A lot of the APIs covered in this book are asynchronous. When you call one of these functions or methods, you might not get the result back right away. Different APIs have different mechanisms to get the result back to you when it’s ready. Callback Functions The most basic asynchronous pattern is a callback function. This is a function that you pass to an asynchronous API. When the work is complete, it calls your callback with the result. Callbacks can be used on their own or as part of other asynchronous patterns. Events Many browser APIs are event based. An event is something that happens asynchronously. Some examples of events are: A button was clicked. The mouse was moved. A network request was completed. An error occurred. An event has a name, such as click or mouseover, and an object with data about the event that occurred. This might include information such as what element was clicked or an HTTP status code. When you listen for an event, you provide a callback function that receives the event object as an argument.
Objects that emit events implement the EventTarget interface, which provides the addEventListener and removeEventListener methods. To listen for an event on an element or other object, you can call addEventListener on it, passing the name of the event and a handler function. The callback is called every time the event is triggered until it is removed. A listener can be removed manually by calling removeEventListener, or in many cases listeners are automatically removed by the browser when objects are destroyed or removed from the DOM. Promises Many newer APIs use Promises. A Promise is an object, returned from a function, that is a placeholder for the eventual result of the asynchronous action. Instead of listening for an event, you call then on a Promise object. You pass a callback function to then that is eventually called with the result as its argument. To handle errors, you pass another callback function to the Promise’s catch method. A Promise is fulfilled when the operation completes successfully, and it is rejected when there’s an error. The fulfilled value is passed as an argument to the then callback, or the rejected value is passed as an argument to the catch callback. There are a few key differences between events and Promises: Event handlers are fired multiple times, whereas a then callback is executed only once. You can think of a Promise as a one-time operation. If you call then on a Promise, you’ll always get the result (if there is one). This is different from events where, if an event occurs before you add a listener, the event is lost. Promises have a built-in error-handling mechanism. With events, you typically need to listen for an error event to handle error
conditions. Working with Promises Problem You want to call an API that uses Promises and retrieve the result. Solution Call then on the Promise object to handle the result in a callback function. To handle potential errors, add a call to catch. Imagine you have a function getUsers that makes a network request to load a list of users. This function returns a Promise that eventually resolves to the user list (see Example 1-1). Example 1-1. Using a Promise-based API getUsers() .then( // This function is called when the user list has been loaded. userList => { console.log('User List:'); userList.forEach(user => { console.log(user.name); }); } ).catch(error => { console.error('Failed to load the user list:', error); }); Discussion The Promise returned from getUsers is an object with a then method. When the user list is loaded, the callback passed to then is executed with the user list as its argument.
This Promise also has a catch method for handling errors. If an error occurs while loading the user list, the callback passed to catch is called with the error object. Only one of these callbacks is called, depending on the outcome. ALWAYS HANDLE ERRORS It’s important to always handle the error case of a Promise. If you don’t, and a Promise is rejected, the browser throws an exception for the unhandled rejection and could crash your app. To prevent an unhandled rejection from taking down your app, add a listener to the window object for the unhandledrejection event. If any Promise is rejected and you don’t handle it with a catch, this event fires. Here you can take action such as logging the error. Loading an Image with a Fallback Problem You want to load an image to display on the page. If there’s an error loading the image, you want to use a known good image URL as a fallback. Solution Create an Image element programmatically, and listen for its load and error events. If the error event triggers, replace it with the fallback image. Once either the requested image or the placeholder image loads, add it to the DOM when desired. For a cleaner API, you can wrap this in a Promise. The Promise either resolves with an Image to be added or rejects with an error if neither the image nor the fallback can be loaded (see Example 1-2).
Example 1-2. Loading an image with a fallback /** * Loads an image. If there's an error loading the image, uses a fallback * image URL instead. * * @param url The image URL to load * @param fallbackUrl The fallback image to load if there's an error * @returns a Promise that resolves to an Image element to insert into the DOM */ function loadImage(url, fallbackUrl) { return new Promise((resolve, reject) => { const image = new Image(); // Attempt to load the image from the given URL image.src = url; // The image triggers the 'load' event when it is successfully loaded. image.addEventListener('load', () => { // The now-loaded image is used to resolve the Promise resolve(image); }); // If an image failed to load, it triggers the 'error' event. image.addEventListener('error', error => { // Reject the Promise in one of two scenarios: // (1) There is no fallback URL. // (2) The fallback URL is the one that failed. if (!fallbackUrl || image.src === fallbackUrl) { reject(error); } else { // If this is executed, it means the original image failed to load. // Try to load the fallback. image.src = fallbackUrl; } }); }); } Discussion The loadImage function takes a URL and a fallback URL and returns a Promise. Then it creates a new Image and sets its src attribute to the given URL. The browser attempts to load the image. There are three possible outcomes:
Success case If the image loads successfully, the load event is triggered. The event handler resolves the Promise with the Image, which can then be inserted into the DOM. Fallback case If the image fails to load, the error event is triggered. The error handler sets the src attribute to the fallback URL, and the browser attempts to load the fallback image. If that is successful, the load event fires and resolves the Promise with the fallback Image. Failure case If neither the image nor the fallback image could be loaded, the error handler rejects the Promise with the error event. The error event is triggered every time there’s a load error. The handler first checks if it’s the fallback URL that failed. If so, this means that the original URL and fallback URL both failed to load. This is the failure case, so the Promise is rejected. If it’s not the fallback URL, this means the requested URL failed to load. Now it sets the fallback URL and tries to load that. The order of checks here is important. Without that first check, if the fallback fails to load, the error handler would trigger an infinite loop of setting the (invalid) fallback URL, requesting it, and firing the error event again. Example 1-3 shows how to use this loadImage function. Example 1-3. Using the loadImage function loadImage('https://example.com/profile.jpg', 'https://example.com/fallback.jpg') .then(image => { // container is an element in the DOM where the image will go
container.appendChild(image); }).catch(error => { console.error('Image load failed'); }); Chaining Promises Problem You want to call several Promise-based APIs in sequence. Each operation depends on the result of the previous one. Solution Use a chain of Promises to run the asynchronous tasks in sequence. Imagine a blog application with two APIs, both of which return Promises: getUser(id) Loads a user with the given user ID getPosts(user) Loads all the blog posts for a given user If you want to load the posts for a user, you first need to load the user object—you can’t call getPosts until the user details are loaded. You can do this by chaining the two Promises together, as shown in Example 1-4. Example 1-4. Using a Promise chain /** * Loads the post titles for a given user ID. * @param userId is the ID of the user whose posts you want to load * @returns a Promise that resolves to an array of post titles */ function getPostTitles(userId) { return getUser(userId) // Callback is called with the loaded user object .then(user => {
console.log(`Getting posts for ${user.name}`); // This Promise is also returned from .then return getPosts(user); }) // Calling then on the getPosts' Promise .then(posts => { // Returns another Promise that will resolve to an array of post titles return posts.map(post => post.title); }) // Called if either getUser or getPosts are rejected .catch(error => { console.error('Error loading data:', error); }); } Discussion The value returned from a Promise’s then handler is wrapped in a new Promise. This Promise is returned from the then method itself. This means the return value of then is also a Promise, so you can chain another then onto it. This is how you create a chain of Promises. getUser returns a Promise that resolves to the user object. The then handler calls getPosts and returns the resulting Promise, which is returned again from then, so you can call then once more to get the final result, the array of posts. At the end of the chain is a call to catch to handle any errors. This works like a try/catch block. If an error occurs at any point within the chain, the catch handler is called with that error and the rest of the chain does not get executed. Using the async and await Keywords Problem You are working with an API that returns a Promise, but you want the code to read in a more linear, or synchronous, fashion.
Solution Use the await keyword with the Promise instead of calling then on it (see Example 1-5). Consider again the getUsers function from “Working with Promises”. This function returns a Promise that resolves to a list of users. Example 1-5. Using the await keyword // A function must be declared with the async keyword // in order to use await in its body. async function listUsers() { try { // Equivalent to getUsers().then(...) const userList = await getUsers(); console.log('User List:'); userList.forEach(user => { console.log(user.name); }); } catch (error) { // Equivalent to .catch(...) console.error('Failed to load the user list:', error); } } Discussion await is an alternative syntax for working with Promises. Instead of calling then with a callback that takes the result as its argument, the expression effectively “pauses” execution of the rest of the function and returns the result when the Promise is fulfilled. If the Promise is rejected, the await expression throws the rejected value. This is handled with a standard try/catch block. Using Promises in Parallel Problem You want to execute a series of asynchronous tasks in parallel using Promises.
Solution Collect all the Promises, and pass them to Promise.all. This function takes an array of Promises and waits for them all to complete. It returns a new Promise that is fulfilled once all the given Promises are fulfilled, or rejects if any of the given Promises are rejected (see Example 1-6). Example 1-6. Loading multiple users with Promise.all // Loading three users at once Promise.all([ getUser(1), getUser(2), getUser(3) ]).then(users => { // users is an array of user objects—the values returned from // the parallel getUser calls }).catch(error => { // If any of the above Promises are rejected console.error('One of the users failed to load:', error); }); Discussion If you have multiple tasks that don’t depend on one another, Promise.all is a good choice. Example 1-6 calls getUser three times, passing a different user ID each time. It collects these Promises into an array that is passed to Promise.all. All three requests run in parallel. Promise.all returns another Promise. Once all three users have loaded successfully, this new Promise becomes fulfilled with an array containing the loaded users. The index of each result corresponds to the index of the Promise in the input array. In this case, it returns an array with users 1, 2, and 3, in that order. What if one or more of these users failed to load? Maybe one of the user IDs doesn’t exist or there was a temporary network error. If any of the Promises passed to Promise.all are rejected, the new Promise immediately rejects as well. The rejection value is the same as that of the rejected Promise.
If one of the users fails to load, the Promise returned by Promise.all is rejected with the error that occurred. The results of the other Promises are lost. If you still want to get the results of any resolved Promises (or errors from other rejected ones), you can instead use Promise.allSettled. With Promise.allSettled, a new Promise is returned just like with Promise.all. However, this Promise is always fulfilled, once all of the Promises are settled (either fulfilled or rejected). As shown in Example 1-7, the resolved value is an array whose elements each have a status property. This is either fulfilled or rejected, depending on the result of that Promise. If the status is fulfilled, the object also has a value property that is the resolved value. On the other hand, if the status is rejected, it instead has a reason property, which is the rejected value. Example 1-7. Using Promise.allSettled Promise.allSettled([ getUser(1), getUser(2), getUser(3) ]).then(results => { results.forEach(result => { if (result.status === 'fulfilled') { console.log('- User:', result.value.name); } else { console.log('- Error:', result.reason); } }); }); // No catch necessary here because allSettled is always fulfilled. Animating an Element with
requestAnimationFrame Problem You want to animate an element in a performant way using JavaScript. Solution Use the requestAnimationFrame function to schedule your animation updates to run at regular intervals. Imagine you have a div element that you want to hide with a fade animation. This is done by adjusting the opacity at regular intervals, using a callback passed to request​ A⁠ nimationFrame (see Example 1-8). The duration of each interval depends on the desired frames per second (FPS) of the animation. Example 1-8. Fade-out animation using requestAnimationFrame const animationSeconds = 2; // Animate over 2 seconds const fps = 60; // A nice, smooth animation // The time interval between each frame const frameInterval = 1000 / fps; // The total number of frames for the animation const frameCount = animationSeconds * fps; // The amount to adjust the opacity by in each frame const opacityIncrement = 1 / frameCount; // The timestamp of the last frame let lastTimestamp; // The starting opacity value let opacity = 1; function fade(timestamp) { // Set the last timestamp to now if there isn't an existing one. if (!lastTimestamp) { lastTimestamp = timestamp; }
// Calculate how much time has elapsed since the last frame. // If not enough time has passed yet, schedule another call of this // function and return. const elapsed = timestamp - lastTimestamp; if (elapsed < frameInterval) { requestAnimationFrame(animate); return; } // Time for a new animation frame. Remember this timestamp. lastTimestamp = timestamp; // Adjust the opacity value and make sure it doesn't go below 0. opacity = Math.max(0, opacity - opacityIncrement) box.style.opacity = opacity; // If the opacity hasn't reached the target value of 0, schedule another // call to this function. if (opacity > 0) { requestAnimationFrame(animate); } } // Schedule the first call to the animation function. requestAnimationFrame(fade); Discussion This is a good, performant way to animate elements using JavaScript that has good browser support. Because it’s done asynchronously, this animation won’t block the browser’s main thread. If the user switches to another tab, the animation is paused and requestAnimationFrame isn’t called unnecessarily. When you schedule a function to run with requestAnimationFrame, the function is called before the next repaint operation. How often this happens depends on the browser and screen refresh rate. Before animating, Example 1-8 does some calculations based on a given animation duration (2 seconds) and frame rate (60 frames per second). It calculates the total number of frames, and uses the duration to calculate how long each frame runs. If you want a different frame rate that doesn’t
match the system refresh rate, this keeps track of when the last animation update was performed to maintain your target frame rate. Then, based on the number of frames, it calculates the opacity adjustment made in each frame. The fade function is scheduled by passing it to a requestAnimationFrame call. Each time the browser calls this function, it passes a timestamp. The fade function calculates how much time has elapsed since the last frame. If not enough time has passed yet, it doesn’t do anything and asks the browser to call again next time around. Once enough time has passed, it performs an animation step. It takes the calculated opacity adjustment and applies it to the element’s style. Depending on the exact timing, this could result in an opacity less than 0, which is invalid. This is fixed by using Math.max to set a minimum value of 0. If the opacity hasn’t reached 0 yet, more animation frames need to be performed. It calls requestAnimationFrame again to schedule the next execution. As an alternative to this method, newer browsers support the Web Animations API, which you’ll learn about in Chapter 8. This API lets you specify keyframes with CSS properties, and the browser handles updating the intermediate values for you. Wrapping an Event API in a Promise Problem You want to wrap an event-based API to return a Promise.
Solution Create a new Promise object and register event listeners within its constructor. When you receive the event you’re waiting for, resolve the Promise with the value. Similarly, reject the Promise if an error event occurs. Sometimes this is called “promisifying” a function. Example 1-9 demonstrates promisifying the XMLHttpRequest API. Example 1-9. Promisifying the XMLHttpRequest API /** * Sends a GET request to the specified URL. Returns a Promise that will resolve to * the JSON body parsed as an object, or will reject if there is an error or the * response is not valid JSON. * * @param url The URL to request * @returns a Promise that resolves to the response body */ function loadJSON(url) { // Create a new Promise object, performing the async work inside the // constructor function. return new Promise((resolve, reject) => { const request = new XMLHttpRequest(); // If the request is successful, parse the JSON response and // resolve the Promise with the resulting object. request.addEventListener('load', event => { // Wrap the JSON.parse call in a try/catch block just in case // the response body is not valid JSON. try { resolve(JSON.parse(event.target.responseText)); } catch (error) { // There was an error parsing the response body. // Reject the Promise with this error. reject(error); } }); // If the request fails, reject the Promise with the // error that was emitted. request.addEventListener('error', error => { reject(error); });
// Set the target URL and send the request. request.open('GET', url); request.send(); }); } Example 1-10 shows how to use the promisified loadJSON function. Example 1-10. Using the loadJSON helper // Using .then loadJSON('/api/users/1').then(user => { console.log('Got user:', user); }) // Using await const user = await loadJSON('/api/users/1'); console.log('Got user:', user); Discussion You create a Promise by calling the Promise constructor function with the new operator. This function receives two arguments, a resolve and reject function. The resolve and reject functions are supplied by the JavaScript engine. Within the Promise constructor, you do your asynchronous work and listen for events. When the resolve function is called, the Promise immediately resolves to that value. Calling reject works the same way—it rejects the Promise with the error. Creating your own Promise can help these types of situations, but in general you usually don’t need to create them manually like this. If an API already returns a Promise, you don’t need to wrap that in your own Promise—just use it directly.
Chapter 2. Simple Persistence with the Web Storage API Introduction The Web Storage API persists simple data locally, in the user’s browser. You can retrieve this data later, even after closing and reopening the browser. This API has a Storage interface that provides data access and persistence. You don’t create instances of Storage directly; there are two global instances: window.localStorage and window.sessionStorage. The only difference between these is how long they retain the data. sessionStorage data is associated with a specific browser session. It retains the data if the page is reloaded, but closing the browser completely loses the data. Different tabs for the same origin do not share the same persisted data. On the other hand, localStorage shares the same storage space across all tabs and sessions for the same origin. The browser retains this data even after you close the browser. In general, session storage is a good choice if you want to store something ephemeral or sensitive that you want to be destroyed once the browser is closed. In both cases, storage space is specific to a given origin.
WHAT IS AN ORIGIN? A page’s origin is a string combining the protocol (http or https), host, and port of a URL. For example, the URLs https://example.com/path/to/index.html and https://example.com/profile/index.html both have the same origin: https://example.com. Getting and Setting Items Web Storage can only store string values. Each value has a key that you can use to look it up. The API is simple: getItem(key) Returns the string bound to a key, or null if the key doesn’t exist. setItem(key, value) Stores a string value under the given key. If the key already exists, you’ll overwrite it. clear() Deletes all stored data for the current origin. Disadvantages Web Storage can be really useful, but it does have a few disadvantages: Data storage limitations Web Storage can only store string data. You can store simple objects, but not directly—you’ll need to convert them to a JavaScript Object Notation (JSON) string first. Size limitations
Each origin has a limited amount of space available for storage. In most browsers this is 5 megabytes. If an origin’s storage becomes full, the browser will throw an exception if you attempt to add more data. Security concerns Even though the browser stores each origin’s data separately, it’s still vulnerable to cross-site scripting (XSS) attacks. An attacker can inject code via an XSS attack that steals locally persisted data. Be mindful of what sensitive data you store here. NOTE The recipes in this chapter all use local storage, but they all apply to session storage as well, since both objects implement the same Storage interface. Checking for Web Storage Support Problem You want to check if local storage is available before using it to avoid crashing your app. You also want to handle the situation where local storage is available but blocked by user settings. Solution Check the global window object for the localStorage property to verify that the browser supports local storage. If the check passes, local storage is available (see Example 2-1). Example 2-1. Checking if local storage is available /** * Determines if local storage is available.
* @returns true if the browser can use local storage, false if not */ function isLocalStorageAvailable() { try { // Local storage is available if the property exists. return typeof window.localStorage !== 'undefined'; } catch (error) { // If window.localStorage exists but the user is blocking local // storage, the attempt to read the property throws an exception. // If this happens, consider local storage not available. return false; } } Discussion The function in Example 2-1 handles both cases: if local storage is supported at all, and if it exists and is not blocked by user settings. It checks to see if the window.localStorage property is not undefined. If this check passes, this means the browser supports local storage. If the user has blocked local storage, just the act of referencing the window.localStorage property throws an exception with a message saying access is denied. By surrounding the property check with a try/catch block, you can also handle this case. When catching the exception, it considers local storage not available and returns false. Persisting String Data Problem You want to persist a string value to local storage and read it back later. Solution Use localStorage.getItem and localStorage.setItem to read and write the data. Example 2-2 shows how we can use local storage to
remember the value of a color picker. Example 2-2. Persisting data to local storage // A reference to the color picker input element const colorPicker = document.querySelector('#colorPicker'); // Load the saved color, if any, and set it on the color picker. const storedValue = localStorage.getItem('savedColor'); if (storedValue) { console.log('Found saved color:', storedValue); colorPicker.value = storedValue; } // Update the saved color whenever the value changes. colorPicker.addEventListener('change', event => { localStorage.setItem('savedColor', event.target.value); console.log('Saving new color:', colorPicker.value); }); Discussion When the page first loads, local storage is checked for a previously saved color. If you call getItem with a key that doesn’t exist, it returns null. The return value is only set in the color picker if it is not null or empty. When the color picker’s value changes, the event handler saves the new value to local storage. If there’s already a saved color, this overwrites it. Persisting Simple Objects Problem You have a JavaScript object, such as a user profile, that you want to persist to local storage. You can’t do this directly because local storage only supports string values.
Solution Use JSON.stringify to convert the object to a JSON string before saving it. When loading the value later, use JSON.parse to turn it back into an object, as shown in Example 2-3. Example 2-3. Using JSON.parse and JSON.stringify /** * Given a user profile object, serialize it to JSON and store it in local storage. * @param userProfile the profile object to save */ function saveProfile(userProfile) { localStorage.setItem('userProfile', JSON.stringify(userProfile)); } /** * Loads the user profile from local storage and deserializes the JSON back to * an object. If there is no stored profile, an empty object is returned. * @returns the stored user profile or an empty object. */ function loadProfile() { // If there is no stored userProfile value, this will return null. In this case, // use the default value of an empty object. return JSON.parse(localStorage.getItem('userProfile')) || {}; } Discussion Passing the profile object directly to localStorage.setItem won’t have the desired effect, as shown in Example 2-4. Example 2-4. Attempting to persist an array const userProfile = { firstName: 'Ava', lastName: 'Johnson' }; localStorage.setItem('userProfile', userProfile); // Prints [object Object] console.log(localStorage.getItem('userProfile'));
The saved value is [object Object]. This is the result of calling toString on the profile object. JSON.stringify takes an object and returns a JSON string representing the object. Passing the user profile object to JSON.stringify results in this JSON string (whitespace added for readability): { "firstName": "Ava", "lastName": "Johnson" } This approach works for objects like the user profile, but the JSON specification limits what can be serialized to a string. Generally speaking, these are objects, arrays, strings, numbers, booleans, and null. Other values, like class instances or functions, can’t be serialized in this way. Persisting Complex Objects Problem You want to persist an object that can’t be directly serialized to a JSON string, to local storage. For example, the user profile object might have a Date object in it specifying when it was last updated. Solution Use replacer and reviver functions with JSON.stringify and JSON.parse to provide custom serialization for the complex data. Consider the following profile object: const userProfile = { firstName: 'Ava', lastName: 'Johnson', // This date represents June 2, 2025.
// Months start with zero but days start with 1. lastUpdated: new Date(2025, 5, 2); } If you serialize this object with JSON.stringify, the resulting string has the lastUpdated date as an ISO date string (see Example 2-5). Example 2-5. Attempting to serialize an object with a Date object const json = JSON.stringify(userProfile); The resulting JSON string looks like this: { "firstName": "Ava", "lastName": "Johnson", "lastUpdated": '2025-06-02T04:00:00.000Z' } Now you have a JSON string that you can save to local storage. However, if you call JSON.parse with this JSON string, the resulting object differs slightly from the original. The lastUpdated property is still a string, not a Date, because JSON.parse doesn’t know that this should be a Date object. To handle these situations, JSON.stringify and JSON.parse accept special functions called replacer and reviver, respectively. These functions provide custom logic to convert nonprimitive values to and from JSON. Serializing with a replacer function The replacer argument to JSON.stringify can work in several different ways. MDN has some comprehensive documentation on the replacer function. The replacer function takes two arguments: key and value (see Example 2-6). JSON.stringify first calls this function with an empty string as the key, and the object being stringified as the value. You can transform the lastUpdated field here to a serializable representation of the
Date object by calling getTime(), which gives the date as the number of milliseconds since the epoch (midnight UTC on January 1, 1970). Example 2-6. The replacer function function replacer(key, value) { if (key === '') { // First replacer call, "value" is the object itself. // Return all properties of the object, but transform lastUpdated. // This uses object spread syntax to make a copy of "value" before // adding the lastUpdated property. return { ...value, lastUpdated: value.lastUpdated.getTime() }; } // After the initial transformation, the replacer is called once // for each key/value pair. // No more replacements are necessary, so return these as is. return value; } You can pass this replacer function to JSON.stringify to serialize the object to JSON, as shown in Example 2-7. Example 2-7. Stringifying with the replacer const json = JSON.stringify(userProfile, replacer); This generates the following JSON string: { "firstName": "Ava", "lastName": "Johnson", "lastUpdated": 1748836800000 } The number in the lastUpdated property is the timestamp for June 2, 2025.
Deserializing with the reviver function Later, when you pass this JSON string to JSON.parse, the lastUpdated property remains as a number (the timestamp). You can use a reviver function to transform this serialized number value back into a Date object. JSON.parse calls the reviver function for each property in the JSON string. For each key, the value returned from the function is the value that is set in the final object (see Example 2-8). Example 2-8. The reviver function function reviver(key, value) { // JSON.parse calls the reviver once for each key/value pair. // Watch for the lastUpdated key. // Only proceed if there's actually a value for lastUpdated. if (key === 'lastUpdated' && value) { // Here, the value is the timestamp. You can pass this to the Date constructor // to create a Date object referring to the proper time. return new Date(value); } // Restore all other values as is. return value; } To use the reviver, pass it as the second argument to JSON.parse, as shown in Example 2-9. Example 2-9. Parsing with the reviver const object = JSON.parse(userProfile, reviver); This returns an object that is equal to the user profile object we started with: { firstName: 'Ava', lastName: 'Johnson', lastUpdated: [Date object representing June 2, 2025] }
Discussion With this reliable method to convert this object to and from JSON, keeping the Date property intact, you can persist these values in local storage. The approach shown here is just one way to work with a replacer function. Instead of a replacer function, you could also define a toJSON function on the object being stringified. Combined with a factory function, no replacer function is necessary. FACTORY FUNCTIONS Example 2-10 uses a factory function to create user profile objects. It takes some arguments and returns a new object containing data based on those arguments. A factory function is similar to a class’s constructor function. The main difference is that you use a constructor function with the new operator, but a factory is called directly like any other function. Example 2-10. Using a factory that adds a toJSON function /** * A factory function to create a user profile object, * with the lastUpdated property set to today and a toJSON method * * @param firstName The user's first name * @param lastName The user's last name */ function createUser(firstName, lastName) { return { firstName, lastName, lastUpdated: new Date(), toJSON() { return { firstName: this.firstName, lastName: this.lastName, lastUpdated: this.lastUpdated.getTime(); } } } }
const userProfile = createUser('Ava', 'Johnson'); Calling JSON.stringify with the object in Example 2-10 returns the same JSON string as before, with lastUpdated properly converted to a timestamp. NOTE There isn’t any mechanism like this for parsing a string back to an object with JSON.parse. If you use the toJSON approach shown here, you’ll still need to write a reviver function to properly deserialize a user profile string. Since functions can’t be serialized, the resulting JSON string won’t have a toJSON property. Whatever method you choose, the resulting JSON is the same. Listening for Storage Changes Problem You want to receive a notification when another tab on the same origin makes changes to local storage. Solution Listen for the storage event on the window object. This event fires when other tabs or sessions in the same browser, on the same origin, make changes to any data in local storage (see Example 2-11). Example 2-11. Listening for storage changes from another tab // Listen for the 'storage' event. If another tab changes the // 'savedColor' item, update this page's color picker with the new value. window.addEventListener('storage', event => { if (event.key === 'savedColor') { console.log('New color was chosen in another tab:', event.newValue);
colorPicker.value = event.newValue; } }); Consider the persistent color picker from “Persisting String Data”. If the user has multiple tabs open and changes the color in another tab, you can get notified and update the local in-memory copy of the data to keep everything in sync. NOTE The storage event is not triggered on the tab or page that made the storage change. It’s meant to listen for changes that other pages have made to local storage. A storage event specifies which key was changed and what the new value is. It also includes the old value, in case you need it for comparison. Discussion The main use case for the storage event is to keep multiple sessions in sync with each other in real time. NOTE The storage event is only triggered for other tabs and sessions in the same browser on the same device. Even if you don’t listen for the storage event, all sessions on the same origin still share the same local storage data. If you call localStorage.getItem at any point, you’ll still get the latest value. The storage event just provides a real-time notification when such a change happens so the app can update the local data.
Finding All Known Keys Problem You want to know all the keys that are currently in local storage for the current origin. Solution Use the length property with the key function to build a list of all the known keys. Storage objects don’t have a function to return the list of keys directly, but you can build such a list by using the following: The length property returns the number of keys. The key function, given an index, returns the key at that index. You can combine these with a for loop to build an array of all the keys, as shown Example 2-12. Example 2-12. Building a list of keys /** * Generates an array of all keys found in the local storage area * @returns an array of keys */ function getAllKeys() { const keys = []; for (let i = 0; i < localStorage.length; i++) { keys.push(localStorage.key(i)); } return keys; } Discussion You can combine the length property and the key function to perform other types of queries, too. This could be, for example, a function that takes
an array of keys and returns an object containing just those key/value pairs (see Example 2-13). Example 2-13. Querying for a subset of key/value pairs function getAll(keys) { const results = {}; // Check each key in local storage. for (let i = 0; i < localStorage.length; i++) { // Get the ith key. If the keys array includes this key, add it and its value // to the results object. const key = localStorage.key(i); if (keys.includes(key)) { results[key] = localStorage.getItem(key); } } // results now has all key/value pairs that exist in local storage. return results; } NOTE The ordering of the keys, as referenced with the key function, may not be the same across different browsers. Removing Data Problem You want to remove some, or all, data from local storage. Solution Use the removeItem and clear methods as appropriate. To remove a particular key/value pair from local storage, call localStorage.remove​ I⁠ tem with the key (see Example 2-14).
Example 2-14. Removing an item from local storage // This is a safe operation. If the key doesn't exist, // no exception is thrown. localStorage.removeItem('my-key'); Call localStorage.clear to remove all data from local storage for the current origin, as shown in Example 2-15. Example 2-15. Removing all items from local storage localStorage.clear(); Discussion Browsers limit the amount of data that you can store in Web Storage. Typically, the limit is about 5 MB. To avoid running out of space and throwing an error, you should remove items once they are no longer needed. Depending on what you’re using Web Storage for, you can also provide a way for your users to clear stored data. Consider an emoji picker that stores recently selected emojis in local storage. You might add a Clear Recents button that removes these items.
Chapter 3. URLs and Routing Introduction Most web pages and applications deal with URLs in some way. This could be an action like crafting a link with certain query parameters, or URL- based routing in a single-page application (SPA). A URL is just a string that complies with some syntax rules as defined in RFC 3986, “Uniform Resource Identifier (URI): Generic Syntax”. There are several component parts of a URL that you may need to parse or manipulate. Doing so with techniques like regular expressions or string concatenation isn’t always reliable. Today, browsers support the URL API. This API provides a URL constructor that can create, derive, and manipulate URLs. This API was somewhat limited at first, but later updates added utilities like the URLSearchParams interface that simplified building and reading query strings. Parts of a URL When you call the URL constructor with a string representing a valid URL, the resulting object contains properties representing the URL’s different component parts. Figure 3-1 shows the most commonly used of these: protocol (1) For web URLs, this is typically http: or https: (note that the colon is included, but not the slashes). Other protocols are possible such as file: (for a local file not hosted on a server) or ftp: (a resource on an FTP server). hostname (2) The domain or host name (example.com).
pathname (3) The path of the resource relative to the root, with leading slash (/admin/login). search (4) Any query parameters. The ? character is included (? username=sysadmin). Figure 3-1. An example URL with its component parts highlighted Some other parts of the URL include: hash If the URL contains a hash, returns the hash portion (including the hash symbol, #). This is sometimes used for internal navigation for older SPAs. For the URL https://example.com/app#profile, the value of hash would be #profile. host Similar to hostname, but also includes the port number (if specified), for example localhost:8443. origin The origin of the URL. This usually includes the protocol, hostname, and port (if specified). You can get the entire URL string by calling toString on it, or by accessing its href property.
If an invalid URL string is passed to the URL constructor, it throws an exception. Resolving a Relative URL Problem You have a partial or relative URL like /api/users that you want to resolve to a full, absolute URL like https://example.com/api/users. Solution Create a URL object, passing the relative URL and the desired base URL, as shown in Example 3-1. Example 3-1. Creating relative URLs /** * Given a relative path and a base URL, resolves a full absolute URL. * @param relativePath The relative path for the URL * @param baseUrl A valid URL to use as the base */ function resolveUrl(relativePath, baseUrl) { return new URL(relativePath, baseUrl).href; } // https://example.com/api/users console.log(resolveUrl('/api/users', 'https://example.com')); Without the second argument, the URL constructor would throw an error because /api/users is not a valid URL. The second argument is the base for constructing a new URL. It constructs the URL by assuming the given path is relative to the base URL. Discussion The second argument must be a valid URL. To construct the final URL, the typical rules for a valid relative URL are applied depending on the first argument.
If the first argument starts with a leading slash, the pathname of the base URL is ignored and the new URL is relative to the root of the base URL: // https://example.com/api/v1/users console.log(resolveUrl('/api/v1/users', 'https://example.com')); // https://example.com/api/v1/users // Note that /api/v2 is discarded due to the leading slash in /api/v1/users console.log(resolveUrl('/api/v1/users', 'https://example.com/api/v2')); Otherwise, the URL is calculated relative to the base URL: // https://example.com/api/v1/users console.log(resolveUrl('../v1/users/', 'https://example.com/api/v2')); // https://example.com/api/v1/users console.log(resolveUrl('users', 'https://example.com/api/v1/groups')); If the first argument is a valid URL on its own, the base URL is ignored. If the constructor’s second argument is not a string, toString is called on it and the resulting string is used. This means you can pass other URL objects, or even other objects that are similar to URL. You can even pass window.location (a Location object, which has similar properties to a URL) to generate a new URL on the current origin (see Example 3-2). Example 3-2. Creating a relative URL on the same origin const usersApiUrl = new URL('/api/users', window.location); Removing Query Parameters From a URL Problem You want to remove all query parameters from a URL.
Random documents with unrelated content Scribd suggests to you:
an ellipse, a parabola, or a hyperbola, is moving under the influence of gravity. [Transcriber's Note: In Newton's equation above, (SK)2 means to group S and K together and square their product. In the original book, instead of using parentheses, there was a vinculum, a horizontal bar, drawn over the S and the K to express the same grouping.]
Fig. 63. 53. The Force that draws the Different Planets to the Sun Varies inversely as the Squares of the Distances of the Planets from the Sun.—Newton compared the distances jK and eF, over which two planets are drawn towards the sun in the same time, and found these distances to vary inversely as the squares of the distances of the planets from the sun: hence he concluded that all the planets are held in their orbits by gravity. He also showed that this would be true of any two bodies that were revolving around the sun's centre, according to Kepler's Third Law. 54. The Copernican System.—The theory of the solar system which originated with Copernicus, and which was developed and completed by Kepler and Newton, is commonly known as the Copernican System. This system is shown in Fig. 64.
Fig. 64.
II. THE SUN AND PLANETS. I. THE EARTH. Form and Size. 55. Form of the Earth.—In ordinary language the term horizon denotes the line that bounds the portion of the earth's surface that is visible at any point. (1) It is well known that the horizon of a plain presents the form of a circle surrounding the observer. If the latter moves, the circle moves also; but its form remains the same, and is modified only when mountains or other obstacles limit the view. Out at sea, the circular form of the horizon is still more decided, and changes only near the coasts, the outline of which breaks the regularity. Here, then, we obtain a first notion of the rotundity of the earth, since a sphere is the only body which is presented always to us under the form of a circle, from whatever point on its surface it is viewed. (2) Moreover, it cannot be maintained that the horizon is the vanishing point of distinct vision, and that it is this which causes the appearance of a circular boundary, because the horizon is enlarged when we mount above the surface of the plain. This will be evident from Fig. 65, in which a mountain is depicted in the middle of a plain, whose uniform curvature is that of a sphere. From the foot of the mountain the spectator will have but a very limited horizon. Let him ascend half way, his visual radius extends, is inclined below the first horizon, and reveals a more extended circular area. At the summit of the mountain the horizon still increases; and, if the
atmosphere is pure, the spectator will see numerous objects where from the lower stations the sky alone was visible. Fig. 65. This extension of the horizon would be inexplicable if the earth had the form of an extended plane. (3) The curvature of the surface of the sea manifests itself in a still more striking manner. If we are on the coast at the summit of a hill, and a vessel appears on the horizon (Fig. 66), we see only the tops of the masts and the highest sails; the lower sails and the hull are invisible. As the vessel approaches, its lower part comes into view above the horizon, and soon it appears entire.
Fig. 66. In the same manner the sailors from the ship see the different parts of objects on the land appear successively, beginning with the highest. The reason of this will be evident from Fig. 67, where the course of a vessel, seen in profile, is figured on the convex surface of the sea. Fig. 67.
As the curvature of the ocean is the same in every direction, it follows that the surface of the ocean is spherical. The same is true of the surface of the land, allowance being made for the various inequalities of the surface. From these and various other indications, we conclude that the earth is a sphere. 56. Size of the Earth.—The size of the earth is ascertained by measuring the length of a degree of a meridian, and multiplying this by three hundred and sixty. This gives the circumference of the earth as about twenty-five thousand miles, and its diameter as about eight thousand miles. We know that the two stations between which we measure are one degree apart when the elevation of the pole at one station is one degree greater than at the other. 57. The Earth Flattened at the Poles.—Degrees on the meridian have been measured in various parts of the earth, and it has been found that they invariably increase in length as we proceed from the equator towards the pole: hence the earth must curve less and less rapidly as we approach the poles; for the less the curvature of a circle, the larger the degrees on it.
Fig. 68. 58. The Earth in Space.—In Fig. 68 we have a view of the earth suspended in space. The side of the earth turned towards the sun is illumined, and the other side is in darkness. As the planet rotates on its axis, successive portions of it will be turned towards the sun. As viewed from a point in space between it and the sun, it will present
light and dark portions, which will assume different forms according to the portion which is illumined. These different appearances are shown in Fig. 69. Fig. 69. Day and Night.
59. Day and Night.—The succession of day and night is due to the rotation of the earth on its axis, by which a place on the surface of the earth is carried alternately into the sunshine and out of it. As the sun moves around the heavens on the ecliptic, it will be on the celestial equator when at the equinoxes, and 23-1/2° north of the equator when at the summer solstice, and 23-1/2° south of the equator when at the winter solstice. 60. Day and Night when the Sun is at the Equinoxes.—When the sun is at either equinox, the diurnal circle described by the sun will coincide with the celestial equator; and therefore half of this diurnal circle will be above the horizon at every point on the surface of the globe. At these times day and night will be equal in every part of the earth. Fig. 70.
Fig. 71. The equality of days and nights when the sun is on the celestial equator is also evident from the following considerations: one-half of the earth is in sunshine all of the time; when the sun is on the celestial equator, it is directly over the equator of the earth, and the illumination extends from pole to pole, as is evident from Figs. 70 and 71, in the former of which the sun is represented as on the eastern horizon at a place along the central line of the figure, and in the latter as on the meridian along the same line. In each diagram it is seen that the illumination extends from pole to pole: hence, as the earth rotates on its axis, every place on the surface will be in the sunshine and out of it just half of the time. 61. Day and Night when the Sun is at the Summer Solstice.—When the sun is at the summer solstice, it will be 23-1/2° north of the celestial equator. The diurnal circle described by the sun will then be 23-1/2° north of the celestial equator; and more than half of this diurnal circle will be above the horizon at all places north of the equator, and less than half of it at places south of the equator:
hence the days will be longer than the nights at places north of the equator, and shorter than the nights at places south of the equator. At places within 23-1/2° of the north pole, the entire diurnal circle described by the sun will be above the horizon, so that the sun will not set. At places within 23-1/2° of the south pole of the earth, the entire diurnal circle will be below the horizon, so that the sun will not rise. Fig. 72.
Fig. 73. The illumination of the earth at this time is shown in Figs. 72 and 73. In Fig. 72 the sun is represented as on the western horizon along the middle line of the figure, and in Fig. 73 as on the meridian. It is seen at once that the illumination extends 23-1/2° beyond the north pole, and falls 23-1/2° short of the south pole. As the earth rotates on its axis, places near the north pole will be in the sunshine all the time, while places near the south pole will be out of the sunshine all the time. All places north of the equator will be in the sunshine longer than they are out of it, while all places south of the equator will be out of the sunshine longer than they are in it. 62. Day and Night when the Sun is at the Winter Solstice.—When the sun is at the winter solstice, it is 23-1/2° south of the celestial equator. The diurnal circle described by the sun is then 23-1/2° south of the celestial equator. More than half of this diurnal circle will therefore be above the horizon at all places south of the equator, and less than half of it at all places north of the equator: hence the days will be longer than the nights south of the equator, and shorter
than the nights at places north of the equator. At places within 23- 1/2° of the south pole, the diurnal circle described by the sun will be entirely above the horizon, and the sun will therefore not set. At places within 23-1/2° of the north pole, the diurnal circle described by the sun will be wholly below the horizon, and therefore the sun will not rise. The illumination of the earth at this time is shown in Figs. 74 and 75, and is seen to be the reverse of that shown in Figs. 72 and 73. Fig. 74.
Fig. 75. 63. Variation in the Length of Day and Night.—As long as the sun is north of the equinoctial, the nights will be longer than the days south of the equator, and shorter than the days north of the equator. It is just the reverse when the sun is south of the equator. The farther the sun is from the equator, the greater is the inequality of the days and nights. The farther the place is from the equator, the greater the inequality of its days and nights. When the distance of a place from the north pole is less than the distance of the sun north of the equinoctial, it will have continuous day without night, since the whole of the sun's diurnal circle will be above the horizon. A place within the same distance of the south pole will have continuous night.
When the distance of a place from the north pole is less than the distance of the sun south of the equinoctial, it will have continuous night, since the whole of the sun's diurnal circle will then be below the horizon. A place within the same distance of the south pole will then have continuous day. At the equator the days and nights are always equal; since, no matter where the sun is in the heavens, half of all the diurnal circles described by it will be above the horizon, and half of them below it. 64. The Zones.—It will be seen, from what has been stated above, that the sun will at some time during the year be directly overhead at every place within 23-1/2° of the equator on either side. This belt of the earth is called the torrid zone. The torrid zone is bounded by circles called the tropics; that of Cancer on the north, and that of Capricorn on the south. It will also be seen, that, at every place within 23-1/2° of either pole, there will be, some time during the year, a day during which the sun will not rise, or on which it will not set. These two belts of the earth's surface are called the frigid zones. These zones are bounded by the arctic circles. The nearer a place is to the poles, the greater the number of days on which the sun does not rise or set. Between the frigid zones and the torrid zones, there are two belts on the earth which are called the temperate zones. The sun is never overhead at any place in these two zones, but it rises and sets every day at every place within their limits. 65. The Width of the Zones.—The distance the frigid zones extend from the poles, and the torrid zones from the equator, is exactly equal to the obliquity of the ecliptic, or the deviation of the axis of the earth from the perpendicular to the plane of its orbit. Were this deviation forty-five degrees, the obliquity of the ecliptic would be forty-five degrees, the torrid zone would extend forty-five degrees from the equator, and the frigid zones forty-five degrees from the poles. In this case there would be no temperate zones. Were this deviation fifty degrees, the torrid and frigid zones would overlap ten
degrees, and there would be two belts of ten degrees on the earth, which would experience alternately during the year a torrid and a frigid climate. Were the axis of the earth perpendicular to the plane of the earth's orbit, there would be no zones on the earth, and no variation in the length of day and night. 66. Twilight.—Were it not for the atmosphere, the darkness of midnight would begin the moment the sun sank below the horizon, and would continue till he rose again above the horizon in the east, when the darkness of the night would be suddenly succeeded by the full light of day. The gradual transition from the light of day to the darkness of the night, and from the darkness of the night to the light of day, is called twilight, and is due to the diffusion of light from the upper layers of the atmosphere after the sun has ceased to shine on the lower layers at night, or before it has begun to shine on them in the morning. Fig. 76. Let ABCD (Fig. 76) represent a portion of the earth, A a point on its surface where the sun S is setting; and let SAH be a ray of light just grazing the earth at A, and leaving the atmosphere at the point H. The point A is illuminated by the whole reflective atmosphere HGFE.
The point B, to which the sun has set, receives no direct solar light, nor any reflected from that part of the atmosphere which is below ALH; but it receives a twilight from the portion HLF, which lies above the visible horizon BF. The point C receives a twilight only from the small portion of the atmosphere; while at D the twilight has ceased altogether. 67. Duration of Twilight.—The astronomical limit of twilight is generally understood to be the instant when stars of the sixth magnitude begin to be visible in the zenith at evening, or disappear in the morning. Twilight is usually reckoned to last until the sun's depression below the horizon amounts to eighteen degrees: this, however, varies; in the tropics a depression of sixteen or seventeen degrees being sufficient to put an end to the phenomenon, while in England a depression of seventeen to twenty-one degrees is required. The duration of twilight differs in different latitudes; it varies also in the same latitude at different seasons of the year, and depends, in some measure, on the meteorological condition of the atmosphere. When the sky is of a pale color, indicating the presence of an unusual amount of condensed vapor, twilight is of longer duration. This happens habitually in the polar regions. On the contrary, within the tropics, where the air is pure and dry, twilight sometimes lasts only fifteen minutes. Strictly speaking, in the latitude of Greenwich there is no true night from May 22 to July 21, but constant twilight from sunset to sunrise. Twilight reaches its minimum three weeks before the vernal equinox, and three weeks after the autumnal equinox, when its duration is an hour and fifty minutes. At midwinter it is longer by about seventeen minutes; but the augmentation is frequently not perceptible, owing to the greater prevalence of clouds and haze at that season of the year, which intercept the light, and hinder it from reaching the earth. The duration is least at the equator (an hour and twelve minutes), and increases as we approach the poles; for at the former there are two twilights every twenty-four hours, but at the latter only two in a year, each lasting about fifty days. At the north pole the sun is below the horizon for six months, but from Jan. 29 to the vernal equinox, and from the autumnal equinox to Nov. 12, the sun is less than eighteen degrees below the horizon; so that there is twilight during the whole of these intervals, and thus the length of the actual night is reduced to two months and a half. The length of the day in these regions is about six months, during the whole of which time the sun is constantly above the horizon. The general rule is, that to the inhabitants of an oblique sphere the twilight is longer in proportion as the place is nearer the
elevated pole, and the sun is farther from the equator on the side of the elevated pole. The Seasons. 68. The Seasons.—While the sun is north of the celestial equator, places north of the equator are receiving heat from the sun by day longer than they are losing it by radiation at night, while places south of the equator are losing heat by radiation at night longer than they are receiving it from the sun by day. When, therefore, the sun passes north of the equator, the temperature begins to rise at places north of the equator, and to fall at places south of it. The rise of temperature is most rapid north of the equator when the sun is at the summer solstice; but, for some time after this, the earth continues to receive more heat by day than it loses by night, and therefore the temperature continues to rise. For this reason, the heat is more excessive after the sun passes the summer solstice than before it reaches it. 69. The Duration of the Seasons.—Summer is counted as beginning in June, when the sun is at the summer solstice, and as continuing until the sun reaches the autumnal equinox, in September. Autumn then begins, and continues until the sun is at the winter solstice, in December. Winter follows, continuing until the sun comes to the vernal equinox, in March, when spring begins, and continues to the summer solstice. In popular reckoning the seasons begin with the first day of June, September, December, and March. The reason why winter is counted as occurring after the winter solstice is similar to the reason why the summer is placed after the summer solstice. The earth north of the equator is losing heat most rapidly at the time of the winter solstice; but for some time after this it loses more heat by night than it receives by day: hence for some time the temperature continues to fall, and the cold is more intense after the winter solstice than before it.
Fig. 77. Of course, when it is summer in the northern hemisphere, it is winter in the southern hemisphere, and the reverse. Fig. 77 shows the portion of the earth's orbit included in each season. It will be seen that the earth is at perihelion in the winter season for places north of the equator, and at aphelion in the summer season. This tends to mitigate somewhat the extreme temperatures of our winters and summers.
Fig. 78. 70. The Illumination of the Earth at the different Seasons.—Fig. 78 shows the earth as it would appear to an observer at the sun during each of the four seasons; that is to say, the portion of the earth that is receiving the sun's rays. Figs. 79, 80, 81, and 82 are enlarged views of the earth, as seen from the sun at the time of the summer solstice, of the autumnal equinox, of the winter solstice, and of the vernal equinox.
Fig. 79.
Fig. 80.
Fig. 81.
Fig. 82. Fig. 83.
Fig. 83 is, so to speak, a side view of the earth, showing the limit of sunshine on the earth when the sun is at the summer solstice; and Fig. 84, showing the limit of sunshine when the sun is at the autumnal equinox. Fig. 84. 71. Cause of the Change of Seasons.—Variety in the length of day and night, and diversity in the seasons, depend upon the obliquity of the ecliptic. Were there no obliquity of the ecliptic, there would be no inequality in the length of day and night, and but slight diversity of seasons. The greater the obliquity of the ecliptic, the greater would be the variation in the length of the days and nights, and the more extreme the changes of the seasons. Tides. 72. Tides.—The alternate rise and fall of the surface of the sea twice in the course of a lunar day, or of twenty-four hours and fifty-one
Welcome to our website – the perfect destination for book lovers and knowledge seekers. We believe that every book holds a new world, offering opportunities for learning, discovery, and personal growth. That’s why we are dedicated to bringing you a diverse collection of books, ranging from classic literature and specialized publications to self-development guides and children's books. More than just a book-buying platform, we strive to be a bridge connecting you with timeless cultural and intellectual values. With an elegant, user-friendly interface and a smart search system, you can quickly find the books that best suit your interests. Additionally, our special promotions and home delivery services help you save time and fully enjoy the joy of reading. Join us on a journey of knowledge exploration, passion nurturing, and personal growth every day! ebookbell.com

Web Api Cookbook Level Up Your Javascript Applications Converted Joe Attardi

  • 1.
    Web Api CookbookLevel Up Your Javascript Applications Converted Joe Attardi download https://ebookbell.com/product/web-api-cookbook-level-up-your- javascript-applications-converted-joe-attardi-56376958 Explore and download more ebooks at ebookbell.com
  • 2.
    Here are somerecommended products that we believe you will be interested in. You can click the link to download. Web Api Cookbook Level Up Your Javascript Applications 1st Edition Joe Attardi https://ebookbell.com/product/web-api-cookbook-level-up-your- javascript-applications-1st-edition-joe-attardi-56785342 Web Api Cookbook Level Up Your Javascript Applications Joe Attardi https://ebookbell.com/product/web-api-cookbook-level-up-your- javascript-applications-joe-attardi-56409582 Restful Web Api Patterns And Practices Cookbook Connecting And Orchestrating Microservices And Distributed Data 1st Edition Mike Amundsen https://ebookbell.com/product/restful-web-api-patterns-and-practices- cookbook-connecting-and-orchestrating-microservices-and-distributed- data-1st-edition-mike-amundsen-53003204 Restful Web Api Patterns And Practices Cookbook Author Names Here https://ebookbell.com/product/restful-web-api-patterns-and-practices- cookbook-author-names-here-232358360
  • 3.
    Restful Web ApiPatterns And Practices Cookbook Mike Amundsen https://ebookbell.com/product/restful-web-api-patterns-and-practices- cookbook-mike-amundsen-232358362 Web Api Development For The Absolute Beginner A Stepbystep Approach To Learning The Fundamentals Of Web Api Development With Net 7 1st Edition Irina Dominte https://ebookbell.com/product/web-api-development-for-the-absolute- beginner-a-stepbystep-approach-to-learning-the-fundamentals-of-web- api-development-with-net-7-1st-edition-irina-dominte-51638020 Web Api Development With Aspnet Core 8 Learn Techniques Patterns And Tools For Building Highperformance Robust And Scalable Web Apis 1st Edition Xiaodi Yan https://ebookbell.com/product/web-api-development-with-aspnet- core-8-learn-techniques-patterns-and-tools-for-building- highperformance-robust-and-scalable-web-apis-1st-edition-xiaodi- yan-56790244 Web Api Development With Aspnet Core 8 Learn Techniques Patterns And Tools For Building Highperformance Robust And Scalable Web Apis Xiaodi Yan https://ebookbell.com/product/web-api-development-with-aspnet- core-8-learn-techniques-patterns-and-tools-for-building- highperformance-robust-and-scalable-web-apis-xiaodi-yan-56548330 Restful Web Api Design With Nodejs Design And Implement Comprehensive Restful Solutions In Nodejs 1st Ed Valentin Bojinov https://ebookbell.com/product/restful-web-api-design-with-nodejs- design-and-implement-comprehensive-restful-solutions-in-nodejs-1st-ed- valentin-bojinov-50194976
  • 7.
    Web API Cookbook LevelUp Your JavaScript Applications Joe Attardi
  • 8.
    Web API Cookbook byJoe Attardi Copyright © 2024 Joseph Attardi. All rights reserved. Printed in the United States of America. Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472. O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://oreilly.com). For more information, contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com. Acquisitions Editor: Amanda Quinn Development Editor: Virginia Wilson Production Editor: Beth Kelly Copyeditor: Piper Editorial Consulting, LLC Proofreader: Tove Innis Indexer: WordCo Indexing Services, Inc. Interior Designer: David Futato Cover Designer: Karen Montgomery Illustrator: Kate Dullea April 2024: First Edition
  • 9.
    Revision History forthe First Edition 2024-03-21: First Release See http://oreilly.com/catalog/errata.csp?isbn=9781098150693 for release details. The O’Reilly logo is a registered trademark of O’Reilly Media, Inc. Web API Cookbook, the cover image, and related trade dress are trademarks of O’Reilly Media, Inc. The views expressed in this work are those of the author and do not represent the publisher’s views. While the publisher and the author have used good faith efforts to ensure that the information and instructions contained in this work are accurate, the publisher and the author disclaim all responsibility for errors or omissions, including without limitation responsibility for damages resulting from the use of or reliance on this work. Use of the information and instructions contained in this work is at your own risk. If any code samples or other technology this work contains or describes is subject to open source licenses or the intellectual property rights of others, it is your responsibility to ensure that your use thereof complies with such licenses and/or rights. 978-1-098-15069-3 [LSI]
  • 10.
    Preface JavaScript has comea long way since its introduction in late 1995. In the early days, the core APIs built into web browsers were limited. More advanced functionality typically required third-party JavaScript libraries, or in some cases even browser plug-ins. A web API is a series of global objects and functions exposed by the browser. Your JavaScript code can use these to interact with the Document Object Model (DOM), perform network communication, integrate with native device features, and much more. POLYFILLS Older browsers can take advantage of some of these APIs by using polyfills. A polyfill is a JavaScript library that implements missing functionality. Polyfills are typically used for web API features that aren’t implemented in older browsers. While beneficial, polyfills do have some drawbacks: They are loaded as third-party libraries, which add to your bundle size. They generally aren’t maintained by the browser teams, so there may be bugs or inconsistencies. Some advanced functionality can’t be polyfilled in a performant way, or at all. The Power of Modern Browsers Modern web APIs have two big advantages for the web platform:
  • 11.
    No more plug-ins Inthe past, much of this functionality was only available to native applications or clunky browser plug-ins. (Remember ActiveX and Flash?) Fewer third-party dependencies Modern browsers provide considerable functionality that used to require third-party JavaScript libraries. Popular libraries such as jQuery, Lodash, and Moment are usually not needed anymore. Drawbacks of Third-Party Libraries Third-party libraries can be helpful with older browsers or newer functionality, but they have some costs: More code to download Using libraries increases the amount of JavaScript the browser has to load. Whether it’s bundled with your app or loaded separately from a content delivery network (CDN), your browser still has to download it. This translates into potentially longer loading times and higher battery usage on mobile devices. Increased risk Open source libraries, even popular ones, can be abandoned. When bugs or security vulnerabilities are found, there’s no guarantee of an update. Browsers, in general, are supported by large companies (the major browsers are from Google, Mozilla, Apple, and Microsoft), and it’s more likely that these issues will be fixed.
  • 12.
    This isn’t tosay that third-party libraries are bad. There are many benefits as well, especially if you need to support older browsers. Like everything in software development, library use is a balancing act.
  • 13.
    Who This BookIs For This book is intended for software developers with some experience with JavaScript who want to get the most out of the web platform. It assumes that you have a good knowledge of the JavaScript language itself: syntax, language features, and standard library functions. You should also have a working knowledge of the DOM APIs used for building interactive, browser-based JavaScript applications. There is a wide range of recipes in this book; there’s something for developers of all skill and experience levels. What’s in This Book Each chapter contains a set of recipes—code examples for accomplishing a specific task. Each recipe has three sections: Problem Describes the problem the recipe solves. Solution Contains code and explanation that implements the recipe solution. Discussion A deeper discussion of the topic. This section may contain additional code examples and comparisons with other techniques. Code samples and live demos are on the companion website, https://WebAPIs.info.
  • 14.
    Additional Resources By itsnature, the web is changing all the time. There are many great resources available online to help clarify any questions that might come up. CanIUse.com At the time of writing, some APIs in this book are still in development or an “experimental” phase. Watch for compatibility notes in recipes that use these APIs. For most features, you can check the latest compatibility data at https://CanIUse.com. You can search by the name of a feature and see the latest information about which browser versions support the API and any limitations or caveats for particular browser versions. MDN Web Docs MDN Web Docs is the de facto API documentation for all things web. It covers all the APIs from this book in great detail, as well as other topics such as CSS and HTML. It contains in-depth articles and tutorials as well as API specifications. Specifications When in doubt, the specification of a feature or API is the definitive resource. They aren’t the most exciting reads, but they are a good place to look for details about edge cases or expected behavior. Different APIs have different standards, but most can be found either from the Web Hypertext Application Technology Working Group (WHATWG) or the World Wide Web Consortium (W3C). The standards for ECMAScript (which specifies features in the JavaScript Language) are maintained and developed by the Ecma International Technical Committee 39, better known as TC39.
  • 15.
    Conventions Used inThis Book The following typographical conventions are used in this book: Italic Indicates new terms, URLs, email addresses, filenames, and file extensions. Constant width Used for program listings, as well as within paragraphs to refer to program elements such as variable or function names, databases, data types, environment variables, statements, and keywords. Constant width bold Shows commands or other text that should be typed literally by the user. Constant width italic Shows text that should be replaced with user-supplied values or by values determined by context. TIP This element signifies a tip or suggestion. NOTE This element signifies a general note.
  • 16.
    WARNING This element indicatesa warning or caution. Using Code Examples Supplemental material (code examples, exercises, etc.) is available for download at https://github.com/joeattardi/web-api-cookbook. Also check out the companion website, where many of the code samples and recipes in this book are expanded into full, live, working examples. If you have a technical question or a problem using the code examples, please send email to bookquestions@oreilly.com. This book is here to help you get your job done. In general, if example code is offered with this book, you may use it in your programs and documentation. You do not need to contact us for permission unless you’re reproducing a significant portion of the code. For example, writing a program that uses several chunks of code from this book does not require permission. Selling or distributing examples from O’Reilly books does require permission. Answering a question by citing this book and quoting example code does not require permission. Incorporating a significant amount of example code from this book into your product’s documentation does require permission. We appreciate, but generally do not require, attribution. An attribution usually includes the title, author, publisher, and ISBN. For example: “Web API Cookbook by Joseph Attardi (O’Reilly). Copyright 2024 Joe Attardi, 978-1-098-15069-3.” If you feel your use of code examples falls outside fair use or the permission given above, feel free to contact us at permissions@oreilly.com.
  • 17.
    O’Reilly Online Learning NOTE Formore than 40 years, O’Reilly Media has provided technology and business training, knowledge, and insight to help companies succeed. Our unique network of experts and innovators share their knowledge and expertise through books, articles, and our online learning platform. O’Reilly’s online learning platform gives you on-demand access to live training courses, in-depth learning paths, interactive coding environments, and a vast collection of text and video from O’Reilly and 200+ other publishers. For more information, visit https://oreilly.com. How to Contact Us Please address comments and questions concerning this book to the publisher: O’Reilly Media, Inc. 1005 Gravenstein Highway North Sebastopol, CA 95472 800-889-8969 (in the United States or Canada) 707-827-7019 (international or local) 707-829-0104 (fax) support@oreilly.com
  • 18.
    https://www.oreilly.com/about/contact.html We have aweb page for this book, where we list errata, examples, and any additional information. You can access this page at https://oreil.ly/web-api- cookbook. For news and information about our books and courses, visit https://oreilly.com. Find us on LinkedIn: https://linkedin.com/company/oreilly-media Watch us on YouTube: https://youtube.com/oreillymedia Acknowledgments First of all, a heartfelt thanks to my family and friends for supporting me, especially to my wife, Liz, and son, Benjamin, for putting up with listening to my incessant typing. When I am in the zone, I tend to type very quickly and loudly. Thank you to Amanda Quinn, Senior Content Acquisitions Editor, for bringing me on as an O’Reilly author. I’ve read countless O’Reilly books over the years and never thought I’d be writing one of my own one day. Thanks also to Louise Corrigan for making the introduction to Amanda and getting the process started (and who worked with me some years back publishing my very first book!). Special thanks to Virginia Wilson, Senior Development Editor, for guiding me throughout the process of writing the book and meeting regularly to keep things on track. I’d also like to thank the fantastic technical reviewers on this book: Martine Dowden, Schalk Neethling, Sarah Shook, and Adam Scott. The book turned out far better with their helpful feedback. Lastly, I’d like to give a shout out to the teams designing and developing these modern web APIs. Without them, this book would not exist!
  • 19.
    Chapter 1. AsynchronousAPIs Introduction A lot of the APIs covered in this book are asynchronous. When you call one of these functions or methods, you might not get the result back right away. Different APIs have different mechanisms to get the result back to you when it’s ready. Callback Functions The most basic asynchronous pattern is a callback function. This is a function that you pass to an asynchronous API. When the work is complete, it calls your callback with the result. Callbacks can be used on their own or as part of other asynchronous patterns. Events Many browser APIs are event based. An event is something that happens asynchronously. Some examples of events are: A button was clicked. The mouse was moved. A network request was completed. An error occurred. An event has a name, such as click or mouseover, and an object with data about the event that occurred. This might include information such as what element was clicked or an HTTP status code. When you listen for an event, you provide a callback function that receives the event object as an argument.
  • 20.
    Objects that emitevents implement the EventTarget interface, which provides the addEventListener and removeEventListener methods. To listen for an event on an element or other object, you can call addEventListener on it, passing the name of the event and a handler function. The callback is called every time the event is triggered until it is removed. A listener can be removed manually by calling removeEventListener, or in many cases listeners are automatically removed by the browser when objects are destroyed or removed from the DOM. Promises Many newer APIs use Promises. A Promise is an object, returned from a function, that is a placeholder for the eventual result of the asynchronous action. Instead of listening for an event, you call then on a Promise object. You pass a callback function to then that is eventually called with the result as its argument. To handle errors, you pass another callback function to the Promise’s catch method. A Promise is fulfilled when the operation completes successfully, and it is rejected when there’s an error. The fulfilled value is passed as an argument to the then callback, or the rejected value is passed as an argument to the catch callback. There are a few key differences between events and Promises: Event handlers are fired multiple times, whereas a then callback is executed only once. You can think of a Promise as a one-time operation. If you call then on a Promise, you’ll always get the result (if there is one). This is different from events where, if an event occurs before you add a listener, the event is lost. Promises have a built-in error-handling mechanism. With events, you typically need to listen for an error event to handle error
  • 21.
    conditions. Working with Promises Problem Youwant to call an API that uses Promises and retrieve the result. Solution Call then on the Promise object to handle the result in a callback function. To handle potential errors, add a call to catch. Imagine you have a function getUsers that makes a network request to load a list of users. This function returns a Promise that eventually resolves to the user list (see Example 1-1). Example 1-1. Using a Promise-based API getUsers() .then( // This function is called when the user list has been loaded. userList => { console.log('User List:'); userList.forEach(user => { console.log(user.name); }); } ).catch(error => { console.error('Failed to load the user list:', error); }); Discussion The Promise returned from getUsers is an object with a then method. When the user list is loaded, the callback passed to then is executed with the user list as its argument.
  • 22.
    This Promise alsohas a catch method for handling errors. If an error occurs while loading the user list, the callback passed to catch is called with the error object. Only one of these callbacks is called, depending on the outcome. ALWAYS HANDLE ERRORS It’s important to always handle the error case of a Promise. If you don’t, and a Promise is rejected, the browser throws an exception for the unhandled rejection and could crash your app. To prevent an unhandled rejection from taking down your app, add a listener to the window object for the unhandledrejection event. If any Promise is rejected and you don’t handle it with a catch, this event fires. Here you can take action such as logging the error. Loading an Image with a Fallback Problem You want to load an image to display on the page. If there’s an error loading the image, you want to use a known good image URL as a fallback. Solution Create an Image element programmatically, and listen for its load and error events. If the error event triggers, replace it with the fallback image. Once either the requested image or the placeholder image loads, add it to the DOM when desired. For a cleaner API, you can wrap this in a Promise. The Promise either resolves with an Image to be added or rejects with an error if neither the image nor the fallback can be loaded (see Example 1-2).
  • 23.
    Example 1-2. Loadingan image with a fallback /** * Loads an image. If there's an error loading the image, uses a fallback * image URL instead. * * @param url The image URL to load * @param fallbackUrl The fallback image to load if there's an error * @returns a Promise that resolves to an Image element to insert into the DOM */ function loadImage(url, fallbackUrl) { return new Promise((resolve, reject) => { const image = new Image(); // Attempt to load the image from the given URL image.src = url; // The image triggers the 'load' event when it is successfully loaded. image.addEventListener('load', () => { // The now-loaded image is used to resolve the Promise resolve(image); }); // If an image failed to load, it triggers the 'error' event. image.addEventListener('error', error => { // Reject the Promise in one of two scenarios: // (1) There is no fallback URL. // (2) The fallback URL is the one that failed. if (!fallbackUrl || image.src === fallbackUrl) { reject(error); } else { // If this is executed, it means the original image failed to load. // Try to load the fallback. image.src = fallbackUrl; } }); }); } Discussion The loadImage function takes a URL and a fallback URL and returns a Promise. Then it creates a new Image and sets its src attribute to the given URL. The browser attempts to load the image. There are three possible outcomes:
  • 24.
    Success case If theimage loads successfully, the load event is triggered. The event handler resolves the Promise with the Image, which can then be inserted into the DOM. Fallback case If the image fails to load, the error event is triggered. The error handler sets the src attribute to the fallback URL, and the browser attempts to load the fallback image. If that is successful, the load event fires and resolves the Promise with the fallback Image. Failure case If neither the image nor the fallback image could be loaded, the error handler rejects the Promise with the error event. The error event is triggered every time there’s a load error. The handler first checks if it’s the fallback URL that failed. If so, this means that the original URL and fallback URL both failed to load. This is the failure case, so the Promise is rejected. If it’s not the fallback URL, this means the requested URL failed to load. Now it sets the fallback URL and tries to load that. The order of checks here is important. Without that first check, if the fallback fails to load, the error handler would trigger an infinite loop of setting the (invalid) fallback URL, requesting it, and firing the error event again. Example 1-3 shows how to use this loadImage function. Example 1-3. Using the loadImage function loadImage('https://example.com/profile.jpg', 'https://example.com/fallback.jpg') .then(image => { // container is an element in the DOM where the image will go
  • 25.
    container.appendChild(image); }).catch(error => { console.error('Imageload failed'); }); Chaining Promises Problem You want to call several Promise-based APIs in sequence. Each operation depends on the result of the previous one. Solution Use a chain of Promises to run the asynchronous tasks in sequence. Imagine a blog application with two APIs, both of which return Promises: getUser(id) Loads a user with the given user ID getPosts(user) Loads all the blog posts for a given user If you want to load the posts for a user, you first need to load the user object—you can’t call getPosts until the user details are loaded. You can do this by chaining the two Promises together, as shown in Example 1-4. Example 1-4. Using a Promise chain /** * Loads the post titles for a given user ID. * @param userId is the ID of the user whose posts you want to load * @returns a Promise that resolves to an array of post titles */ function getPostTitles(userId) { return getUser(userId) // Callback is called with the loaded user object .then(user => {
  • 26.
    console.log(`Getting posts for${user.name}`); // This Promise is also returned from .then return getPosts(user); }) // Calling then on the getPosts' Promise .then(posts => { // Returns another Promise that will resolve to an array of post titles return posts.map(post => post.title); }) // Called if either getUser or getPosts are rejected .catch(error => { console.error('Error loading data:', error); }); } Discussion The value returned from a Promise’s then handler is wrapped in a new Promise. This Promise is returned from the then method itself. This means the return value of then is also a Promise, so you can chain another then onto it. This is how you create a chain of Promises. getUser returns a Promise that resolves to the user object. The then handler calls getPosts and returns the resulting Promise, which is returned again from then, so you can call then once more to get the final result, the array of posts. At the end of the chain is a call to catch to handle any errors. This works like a try/catch block. If an error occurs at any point within the chain, the catch handler is called with that error and the rest of the chain does not get executed. Using the async and await Keywords Problem You are working with an API that returns a Promise, but you want the code to read in a more linear, or synchronous, fashion.
  • 27.
    Solution Use the awaitkeyword with the Promise instead of calling then on it (see Example 1-5). Consider again the getUsers function from “Working with Promises”. This function returns a Promise that resolves to a list of users. Example 1-5. Using the await keyword // A function must be declared with the async keyword // in order to use await in its body. async function listUsers() { try { // Equivalent to getUsers().then(...) const userList = await getUsers(); console.log('User List:'); userList.forEach(user => { console.log(user.name); }); } catch (error) { // Equivalent to .catch(...) console.error('Failed to load the user list:', error); } } Discussion await is an alternative syntax for working with Promises. Instead of calling then with a callback that takes the result as its argument, the expression effectively “pauses” execution of the rest of the function and returns the result when the Promise is fulfilled. If the Promise is rejected, the await expression throws the rejected value. This is handled with a standard try/catch block. Using Promises in Parallel Problem You want to execute a series of asynchronous tasks in parallel using Promises.
  • 28.
    Solution Collect all thePromises, and pass them to Promise.all. This function takes an array of Promises and waits for them all to complete. It returns a new Promise that is fulfilled once all the given Promises are fulfilled, or rejects if any of the given Promises are rejected (see Example 1-6). Example 1-6. Loading multiple users with Promise.all // Loading three users at once Promise.all([ getUser(1), getUser(2), getUser(3) ]).then(users => { // users is an array of user objects—the values returned from // the parallel getUser calls }).catch(error => { // If any of the above Promises are rejected console.error('One of the users failed to load:', error); }); Discussion If you have multiple tasks that don’t depend on one another, Promise.all is a good choice. Example 1-6 calls getUser three times, passing a different user ID each time. It collects these Promises into an array that is passed to Promise.all. All three requests run in parallel. Promise.all returns another Promise. Once all three users have loaded successfully, this new Promise becomes fulfilled with an array containing the loaded users. The index of each result corresponds to the index of the Promise in the input array. In this case, it returns an array with users 1, 2, and 3, in that order. What if one or more of these users failed to load? Maybe one of the user IDs doesn’t exist or there was a temporary network error. If any of the Promises passed to Promise.all are rejected, the new Promise immediately rejects as well. The rejection value is the same as that of the rejected Promise.
  • 29.
    If one ofthe users fails to load, the Promise returned by Promise.all is rejected with the error that occurred. The results of the other Promises are lost. If you still want to get the results of any resolved Promises (or errors from other rejected ones), you can instead use Promise.allSettled. With Promise.allSettled, a new Promise is returned just like with Promise.all. However, this Promise is always fulfilled, once all of the Promises are settled (either fulfilled or rejected). As shown in Example 1-7, the resolved value is an array whose elements each have a status property. This is either fulfilled or rejected, depending on the result of that Promise. If the status is fulfilled, the object also has a value property that is the resolved value. On the other hand, if the status is rejected, it instead has a reason property, which is the rejected value. Example 1-7. Using Promise.allSettled Promise.allSettled([ getUser(1), getUser(2), getUser(3) ]).then(results => { results.forEach(result => { if (result.status === 'fulfilled') { console.log('- User:', result.value.name); } else { console.log('- Error:', result.reason); } }); }); // No catch necessary here because allSettled is always fulfilled. Animating an Element with
  • 30.
    requestAnimationFrame Problem You want toanimate an element in a performant way using JavaScript. Solution Use the requestAnimationFrame function to schedule your animation updates to run at regular intervals. Imagine you have a div element that you want to hide with a fade animation. This is done by adjusting the opacity at regular intervals, using a callback passed to request​ A⁠ nimationFrame (see Example 1-8). The duration of each interval depends on the desired frames per second (FPS) of the animation. Example 1-8. Fade-out animation using requestAnimationFrame const animationSeconds = 2; // Animate over 2 seconds const fps = 60; // A nice, smooth animation // The time interval between each frame const frameInterval = 1000 / fps; // The total number of frames for the animation const frameCount = animationSeconds * fps; // The amount to adjust the opacity by in each frame const opacityIncrement = 1 / frameCount; // The timestamp of the last frame let lastTimestamp; // The starting opacity value let opacity = 1; function fade(timestamp) { // Set the last timestamp to now if there isn't an existing one. if (!lastTimestamp) { lastTimestamp = timestamp; }
  • 31.
    // Calculate howmuch time has elapsed since the last frame. // If not enough time has passed yet, schedule another call of this // function and return. const elapsed = timestamp - lastTimestamp; if (elapsed < frameInterval) { requestAnimationFrame(animate); return; } // Time for a new animation frame. Remember this timestamp. lastTimestamp = timestamp; // Adjust the opacity value and make sure it doesn't go below 0. opacity = Math.max(0, opacity - opacityIncrement) box.style.opacity = opacity; // If the opacity hasn't reached the target value of 0, schedule another // call to this function. if (opacity > 0) { requestAnimationFrame(animate); } } // Schedule the first call to the animation function. requestAnimationFrame(fade); Discussion This is a good, performant way to animate elements using JavaScript that has good browser support. Because it’s done asynchronously, this animation won’t block the browser’s main thread. If the user switches to another tab, the animation is paused and requestAnimationFrame isn’t called unnecessarily. When you schedule a function to run with requestAnimationFrame, the function is called before the next repaint operation. How often this happens depends on the browser and screen refresh rate. Before animating, Example 1-8 does some calculations based on a given animation duration (2 seconds) and frame rate (60 frames per second). It calculates the total number of frames, and uses the duration to calculate how long each frame runs. If you want a different frame rate that doesn’t
  • 32.
    match the systemrefresh rate, this keeps track of when the last animation update was performed to maintain your target frame rate. Then, based on the number of frames, it calculates the opacity adjustment made in each frame. The fade function is scheduled by passing it to a requestAnimationFrame call. Each time the browser calls this function, it passes a timestamp. The fade function calculates how much time has elapsed since the last frame. If not enough time has passed yet, it doesn’t do anything and asks the browser to call again next time around. Once enough time has passed, it performs an animation step. It takes the calculated opacity adjustment and applies it to the element’s style. Depending on the exact timing, this could result in an opacity less than 0, which is invalid. This is fixed by using Math.max to set a minimum value of 0. If the opacity hasn’t reached 0 yet, more animation frames need to be performed. It calls requestAnimationFrame again to schedule the next execution. As an alternative to this method, newer browsers support the Web Animations API, which you’ll learn about in Chapter 8. This API lets you specify keyframes with CSS properties, and the browser handles updating the intermediate values for you. Wrapping an Event API in a Promise Problem You want to wrap an event-based API to return a Promise.
  • 33.
    Solution Create a newPromise object and register event listeners within its constructor. When you receive the event you’re waiting for, resolve the Promise with the value. Similarly, reject the Promise if an error event occurs. Sometimes this is called “promisifying” a function. Example 1-9 demonstrates promisifying the XMLHttpRequest API. Example 1-9. Promisifying the XMLHttpRequest API /** * Sends a GET request to the specified URL. Returns a Promise that will resolve to * the JSON body parsed as an object, or will reject if there is an error or the * response is not valid JSON. * * @param url The URL to request * @returns a Promise that resolves to the response body */ function loadJSON(url) { // Create a new Promise object, performing the async work inside the // constructor function. return new Promise((resolve, reject) => { const request = new XMLHttpRequest(); // If the request is successful, parse the JSON response and // resolve the Promise with the resulting object. request.addEventListener('load', event => { // Wrap the JSON.parse call in a try/catch block just in case // the response body is not valid JSON. try { resolve(JSON.parse(event.target.responseText)); } catch (error) { // There was an error parsing the response body. // Reject the Promise with this error. reject(error); } }); // If the request fails, reject the Promise with the // error that was emitted. request.addEventListener('error', error => { reject(error); });
  • 34.
    // Set thetarget URL and send the request. request.open('GET', url); request.send(); }); } Example 1-10 shows how to use the promisified loadJSON function. Example 1-10. Using the loadJSON helper // Using .then loadJSON('/api/users/1').then(user => { console.log('Got user:', user); }) // Using await const user = await loadJSON('/api/users/1'); console.log('Got user:', user); Discussion You create a Promise by calling the Promise constructor function with the new operator. This function receives two arguments, a resolve and reject function. The resolve and reject functions are supplied by the JavaScript engine. Within the Promise constructor, you do your asynchronous work and listen for events. When the resolve function is called, the Promise immediately resolves to that value. Calling reject works the same way—it rejects the Promise with the error. Creating your own Promise can help these types of situations, but in general you usually don’t need to create them manually like this. If an API already returns a Promise, you don’t need to wrap that in your own Promise—just use it directly.
  • 35.
    Chapter 2. SimplePersistence with the Web Storage API Introduction The Web Storage API persists simple data locally, in the user’s browser. You can retrieve this data later, even after closing and reopening the browser. This API has a Storage interface that provides data access and persistence. You don’t create instances of Storage directly; there are two global instances: window.localStorage and window.sessionStorage. The only difference between these is how long they retain the data. sessionStorage data is associated with a specific browser session. It retains the data if the page is reloaded, but closing the browser completely loses the data. Different tabs for the same origin do not share the same persisted data. On the other hand, localStorage shares the same storage space across all tabs and sessions for the same origin. The browser retains this data even after you close the browser. In general, session storage is a good choice if you want to store something ephemeral or sensitive that you want to be destroyed once the browser is closed. In both cases, storage space is specific to a given origin.
  • 36.
    WHAT IS ANORIGIN? A page’s origin is a string combining the protocol (http or https), host, and port of a URL. For example, the URLs https://example.com/path/to/index.html and https://example.com/profile/index.html both have the same origin: https://example.com. Getting and Setting Items Web Storage can only store string values. Each value has a key that you can use to look it up. The API is simple: getItem(key) Returns the string bound to a key, or null if the key doesn’t exist. setItem(key, value) Stores a string value under the given key. If the key already exists, you’ll overwrite it. clear() Deletes all stored data for the current origin. Disadvantages Web Storage can be really useful, but it does have a few disadvantages: Data storage limitations Web Storage can only store string data. You can store simple objects, but not directly—you’ll need to convert them to a JavaScript Object Notation (JSON) string first. Size limitations
  • 37.
    Each origin hasa limited amount of space available for storage. In most browsers this is 5 megabytes. If an origin’s storage becomes full, the browser will throw an exception if you attempt to add more data. Security concerns Even though the browser stores each origin’s data separately, it’s still vulnerable to cross-site scripting (XSS) attacks. An attacker can inject code via an XSS attack that steals locally persisted data. Be mindful of what sensitive data you store here. NOTE The recipes in this chapter all use local storage, but they all apply to session storage as well, since both objects implement the same Storage interface. Checking for Web Storage Support Problem You want to check if local storage is available before using it to avoid crashing your app. You also want to handle the situation where local storage is available but blocked by user settings. Solution Check the global window object for the localStorage property to verify that the browser supports local storage. If the check passes, local storage is available (see Example 2-1). Example 2-1. Checking if local storage is available /** * Determines if local storage is available.
  • 38.
    * @returns trueif the browser can use local storage, false if not */ function isLocalStorageAvailable() { try { // Local storage is available if the property exists. return typeof window.localStorage !== 'undefined'; } catch (error) { // If window.localStorage exists but the user is blocking local // storage, the attempt to read the property throws an exception. // If this happens, consider local storage not available. return false; } } Discussion The function in Example 2-1 handles both cases: if local storage is supported at all, and if it exists and is not blocked by user settings. It checks to see if the window.localStorage property is not undefined. If this check passes, this means the browser supports local storage. If the user has blocked local storage, just the act of referencing the window.localStorage property throws an exception with a message saying access is denied. By surrounding the property check with a try/catch block, you can also handle this case. When catching the exception, it considers local storage not available and returns false. Persisting String Data Problem You want to persist a string value to local storage and read it back later. Solution Use localStorage.getItem and localStorage.setItem to read and write the data. Example 2-2 shows how we can use local storage to
  • 39.
    remember the valueof a color picker. Example 2-2. Persisting data to local storage // A reference to the color picker input element const colorPicker = document.querySelector('#colorPicker'); // Load the saved color, if any, and set it on the color picker. const storedValue = localStorage.getItem('savedColor'); if (storedValue) { console.log('Found saved color:', storedValue); colorPicker.value = storedValue; } // Update the saved color whenever the value changes. colorPicker.addEventListener('change', event => { localStorage.setItem('savedColor', event.target.value); console.log('Saving new color:', colorPicker.value); }); Discussion When the page first loads, local storage is checked for a previously saved color. If you call getItem with a key that doesn’t exist, it returns null. The return value is only set in the color picker if it is not null or empty. When the color picker’s value changes, the event handler saves the new value to local storage. If there’s already a saved color, this overwrites it. Persisting Simple Objects Problem You have a JavaScript object, such as a user profile, that you want to persist to local storage. You can’t do this directly because local storage only supports string values.
  • 40.
    Solution Use JSON.stringify toconvert the object to a JSON string before saving it. When loading the value later, use JSON.parse to turn it back into an object, as shown in Example 2-3. Example 2-3. Using JSON.parse and JSON.stringify /** * Given a user profile object, serialize it to JSON and store it in local storage. * @param userProfile the profile object to save */ function saveProfile(userProfile) { localStorage.setItem('userProfile', JSON.stringify(userProfile)); } /** * Loads the user profile from local storage and deserializes the JSON back to * an object. If there is no stored profile, an empty object is returned. * @returns the stored user profile or an empty object. */ function loadProfile() { // If there is no stored userProfile value, this will return null. In this case, // use the default value of an empty object. return JSON.parse(localStorage.getItem('userProfile')) || {}; } Discussion Passing the profile object directly to localStorage.setItem won’t have the desired effect, as shown in Example 2-4. Example 2-4. Attempting to persist an array const userProfile = { firstName: 'Ava', lastName: 'Johnson' }; localStorage.setItem('userProfile', userProfile); // Prints [object Object] console.log(localStorage.getItem('userProfile'));
  • 41.
    The saved valueis [object Object]. This is the result of calling toString on the profile object. JSON.stringify takes an object and returns a JSON string representing the object. Passing the user profile object to JSON.stringify results in this JSON string (whitespace added for readability): { "firstName": "Ava", "lastName": "Johnson" } This approach works for objects like the user profile, but the JSON specification limits what can be serialized to a string. Generally speaking, these are objects, arrays, strings, numbers, booleans, and null. Other values, like class instances or functions, can’t be serialized in this way. Persisting Complex Objects Problem You want to persist an object that can’t be directly serialized to a JSON string, to local storage. For example, the user profile object might have a Date object in it specifying when it was last updated. Solution Use replacer and reviver functions with JSON.stringify and JSON.parse to provide custom serialization for the complex data. Consider the following profile object: const userProfile = { firstName: 'Ava', lastName: 'Johnson', // This date represents June 2, 2025.
  • 42.
    // Months startwith zero but days start with 1. lastUpdated: new Date(2025, 5, 2); } If you serialize this object with JSON.stringify, the resulting string has the lastUpdated date as an ISO date string (see Example 2-5). Example 2-5. Attempting to serialize an object with a Date object const json = JSON.stringify(userProfile); The resulting JSON string looks like this: { "firstName": "Ava", "lastName": "Johnson", "lastUpdated": '2025-06-02T04:00:00.000Z' } Now you have a JSON string that you can save to local storage. However, if you call JSON.parse with this JSON string, the resulting object differs slightly from the original. The lastUpdated property is still a string, not a Date, because JSON.parse doesn’t know that this should be a Date object. To handle these situations, JSON.stringify and JSON.parse accept special functions called replacer and reviver, respectively. These functions provide custom logic to convert nonprimitive values to and from JSON. Serializing with a replacer function The replacer argument to JSON.stringify can work in several different ways. MDN has some comprehensive documentation on the replacer function. The replacer function takes two arguments: key and value (see Example 2-6). JSON.stringify first calls this function with an empty string as the key, and the object being stringified as the value. You can transform the lastUpdated field here to a serializable representation of the
  • 43.
    Date object bycalling getTime(), which gives the date as the number of milliseconds since the epoch (midnight UTC on January 1, 1970). Example 2-6. The replacer function function replacer(key, value) { if (key === '') { // First replacer call, "value" is the object itself. // Return all properties of the object, but transform lastUpdated. // This uses object spread syntax to make a copy of "value" before // adding the lastUpdated property. return { ...value, lastUpdated: value.lastUpdated.getTime() }; } // After the initial transformation, the replacer is called once // for each key/value pair. // No more replacements are necessary, so return these as is. return value; } You can pass this replacer function to JSON.stringify to serialize the object to JSON, as shown in Example 2-7. Example 2-7. Stringifying with the replacer const json = JSON.stringify(userProfile, replacer); This generates the following JSON string: { "firstName": "Ava", "lastName": "Johnson", "lastUpdated": 1748836800000 } The number in the lastUpdated property is the timestamp for June 2, 2025.
  • 44.
    Deserializing with thereviver function Later, when you pass this JSON string to JSON.parse, the lastUpdated property remains as a number (the timestamp). You can use a reviver function to transform this serialized number value back into a Date object. JSON.parse calls the reviver function for each property in the JSON string. For each key, the value returned from the function is the value that is set in the final object (see Example 2-8). Example 2-8. The reviver function function reviver(key, value) { // JSON.parse calls the reviver once for each key/value pair. // Watch for the lastUpdated key. // Only proceed if there's actually a value for lastUpdated. if (key === 'lastUpdated' && value) { // Here, the value is the timestamp. You can pass this to the Date constructor // to create a Date object referring to the proper time. return new Date(value); } // Restore all other values as is. return value; } To use the reviver, pass it as the second argument to JSON.parse, as shown in Example 2-9. Example 2-9. Parsing with the reviver const object = JSON.parse(userProfile, reviver); This returns an object that is equal to the user profile object we started with: { firstName: 'Ava', lastName: 'Johnson', lastUpdated: [Date object representing June 2, 2025] }
  • 45.
    Discussion With this reliablemethod to convert this object to and from JSON, keeping the Date property intact, you can persist these values in local storage. The approach shown here is just one way to work with a replacer function. Instead of a replacer function, you could also define a toJSON function on the object being stringified. Combined with a factory function, no replacer function is necessary. FACTORY FUNCTIONS Example 2-10 uses a factory function to create user profile objects. It takes some arguments and returns a new object containing data based on those arguments. A factory function is similar to a class’s constructor function. The main difference is that you use a constructor function with the new operator, but a factory is called directly like any other function. Example 2-10. Using a factory that adds a toJSON function /** * A factory function to create a user profile object, * with the lastUpdated property set to today and a toJSON method * * @param firstName The user's first name * @param lastName The user's last name */ function createUser(firstName, lastName) { return { firstName, lastName, lastUpdated: new Date(), toJSON() { return { firstName: this.firstName, lastName: this.lastName, lastUpdated: this.lastUpdated.getTime(); } } } }
  • 46.
    const userProfile =createUser('Ava', 'Johnson'); Calling JSON.stringify with the object in Example 2-10 returns the same JSON string as before, with lastUpdated properly converted to a timestamp. NOTE There isn’t any mechanism like this for parsing a string back to an object with JSON.parse. If you use the toJSON approach shown here, you’ll still need to write a reviver function to properly deserialize a user profile string. Since functions can’t be serialized, the resulting JSON string won’t have a toJSON property. Whatever method you choose, the resulting JSON is the same. Listening for Storage Changes Problem You want to receive a notification when another tab on the same origin makes changes to local storage. Solution Listen for the storage event on the window object. This event fires when other tabs or sessions in the same browser, on the same origin, make changes to any data in local storage (see Example 2-11). Example 2-11. Listening for storage changes from another tab // Listen for the 'storage' event. If another tab changes the // 'savedColor' item, update this page's color picker with the new value. window.addEventListener('storage', event => { if (event.key === 'savedColor') { console.log('New color was chosen in another tab:', event.newValue);
  • 47.
    colorPicker.value = event.newValue; } }); Considerthe persistent color picker from “Persisting String Data”. If the user has multiple tabs open and changes the color in another tab, you can get notified and update the local in-memory copy of the data to keep everything in sync. NOTE The storage event is not triggered on the tab or page that made the storage change. It’s meant to listen for changes that other pages have made to local storage. A storage event specifies which key was changed and what the new value is. It also includes the old value, in case you need it for comparison. Discussion The main use case for the storage event is to keep multiple sessions in sync with each other in real time. NOTE The storage event is only triggered for other tabs and sessions in the same browser on the same device. Even if you don’t listen for the storage event, all sessions on the same origin still share the same local storage data. If you call localStorage.getItem at any point, you’ll still get the latest value. The storage event just provides a real-time notification when such a change happens so the app can update the local data.
  • 48.
    Finding All KnownKeys Problem You want to know all the keys that are currently in local storage for the current origin. Solution Use the length property with the key function to build a list of all the known keys. Storage objects don’t have a function to return the list of keys directly, but you can build such a list by using the following: The length property returns the number of keys. The key function, given an index, returns the key at that index. You can combine these with a for loop to build an array of all the keys, as shown Example 2-12. Example 2-12. Building a list of keys /** * Generates an array of all keys found in the local storage area * @returns an array of keys */ function getAllKeys() { const keys = []; for (let i = 0; i < localStorage.length; i++) { keys.push(localStorage.key(i)); } return keys; } Discussion You can combine the length property and the key function to perform other types of queries, too. This could be, for example, a function that takes
  • 49.
    an array ofkeys and returns an object containing just those key/value pairs (see Example 2-13). Example 2-13. Querying for a subset of key/value pairs function getAll(keys) { const results = {}; // Check each key in local storage. for (let i = 0; i < localStorage.length; i++) { // Get the ith key. If the keys array includes this key, add it and its value // to the results object. const key = localStorage.key(i); if (keys.includes(key)) { results[key] = localStorage.getItem(key); } } // results now has all key/value pairs that exist in local storage. return results; } NOTE The ordering of the keys, as referenced with the key function, may not be the same across different browsers. Removing Data Problem You want to remove some, or all, data from local storage. Solution Use the removeItem and clear methods as appropriate. To remove a particular key/value pair from local storage, call localStorage.remove​ I⁠ tem with the key (see Example 2-14).
  • 50.
    Example 2-14. Removingan item from local storage // This is a safe operation. If the key doesn't exist, // no exception is thrown. localStorage.removeItem('my-key'); Call localStorage.clear to remove all data from local storage for the current origin, as shown in Example 2-15. Example 2-15. Removing all items from local storage localStorage.clear(); Discussion Browsers limit the amount of data that you can store in Web Storage. Typically, the limit is about 5 MB. To avoid running out of space and throwing an error, you should remove items once they are no longer needed. Depending on what you’re using Web Storage for, you can also provide a way for your users to clear stored data. Consider an emoji picker that stores recently selected emojis in local storage. You might add a Clear Recents button that removes these items.
  • 51.
    Chapter 3. URLsand Routing Introduction Most web pages and applications deal with URLs in some way. This could be an action like crafting a link with certain query parameters, or URL- based routing in a single-page application (SPA). A URL is just a string that complies with some syntax rules as defined in RFC 3986, “Uniform Resource Identifier (URI): Generic Syntax”. There are several component parts of a URL that you may need to parse or manipulate. Doing so with techniques like regular expressions or string concatenation isn’t always reliable. Today, browsers support the URL API. This API provides a URL constructor that can create, derive, and manipulate URLs. This API was somewhat limited at first, but later updates added utilities like the URLSearchParams interface that simplified building and reading query strings. Parts of a URL When you call the URL constructor with a string representing a valid URL, the resulting object contains properties representing the URL’s different component parts. Figure 3-1 shows the most commonly used of these: protocol (1) For web URLs, this is typically http: or https: (note that the colon is included, but not the slashes). Other protocols are possible such as file: (for a local file not hosted on a server) or ftp: (a resource on an FTP server). hostname (2) The domain or host name (example.com).
  • 52.
    pathname (3) The pathof the resource relative to the root, with leading slash (/admin/login). search (4) Any query parameters. The ? character is included (? username=sysadmin). Figure 3-1. An example URL with its component parts highlighted Some other parts of the URL include: hash If the URL contains a hash, returns the hash portion (including the hash symbol, #). This is sometimes used for internal navigation for older SPAs. For the URL https://example.com/app#profile, the value of hash would be #profile. host Similar to hostname, but also includes the port number (if specified), for example localhost:8443. origin The origin of the URL. This usually includes the protocol, hostname, and port (if specified). You can get the entire URL string by calling toString on it, or by accessing its href property.
  • 53.
    If an invalidURL string is passed to the URL constructor, it throws an exception. Resolving a Relative URL Problem You have a partial or relative URL like /api/users that you want to resolve to a full, absolute URL like https://example.com/api/users. Solution Create a URL object, passing the relative URL and the desired base URL, as shown in Example 3-1. Example 3-1. Creating relative URLs /** * Given a relative path and a base URL, resolves a full absolute URL. * @param relativePath The relative path for the URL * @param baseUrl A valid URL to use as the base */ function resolveUrl(relativePath, baseUrl) { return new URL(relativePath, baseUrl).href; } // https://example.com/api/users console.log(resolveUrl('/api/users', 'https://example.com')); Without the second argument, the URL constructor would throw an error because /api/users is not a valid URL. The second argument is the base for constructing a new URL. It constructs the URL by assuming the given path is relative to the base URL. Discussion The second argument must be a valid URL. To construct the final URL, the typical rules for a valid relative URL are applied depending on the first argument.
  • 54.
    If the firstargument starts with a leading slash, the pathname of the base URL is ignored and the new URL is relative to the root of the base URL: // https://example.com/api/v1/users console.log(resolveUrl('/api/v1/users', 'https://example.com')); // https://example.com/api/v1/users // Note that /api/v2 is discarded due to the leading slash in /api/v1/users console.log(resolveUrl('/api/v1/users', 'https://example.com/api/v2')); Otherwise, the URL is calculated relative to the base URL: // https://example.com/api/v1/users console.log(resolveUrl('../v1/users/', 'https://example.com/api/v2')); // https://example.com/api/v1/users console.log(resolveUrl('users', 'https://example.com/api/v1/groups')); If the first argument is a valid URL on its own, the base URL is ignored. If the constructor’s second argument is not a string, toString is called on it and the resulting string is used. This means you can pass other URL objects, or even other objects that are similar to URL. You can even pass window.location (a Location object, which has similar properties to a URL) to generate a new URL on the current origin (see Example 3-2). Example 3-2. Creating a relative URL on the same origin const usersApiUrl = new URL('/api/users', window.location); Removing Query Parameters From a URL Problem You want to remove all query parameters from a URL.
  • 55.
    Random documents withunrelated content Scribd suggests to you:
  • 56.
    an ellipse, aparabola, or a hyperbola, is moving under the influence of gravity. [Transcriber's Note: In Newton's equation above, (SK)2 means to group S and K together and square their product. In the original book, instead of using parentheses, there was a vinculum, a horizontal bar, drawn over the S and the K to express the same grouping.]
  • 58.
    Fig. 63. 53. TheForce that draws the Different Planets to the Sun Varies inversely as the Squares of the Distances of the Planets from the Sun.—Newton compared the distances jK and eF, over which two planets are drawn towards the sun in the same time, and found these distances to vary inversely as the squares of the distances of the planets from the sun: hence he concluded that all the planets are held in their orbits by gravity. He also showed that this would be true of any two bodies that were revolving around the sun's centre, according to Kepler's Third Law. 54. The Copernican System.—The theory of the solar system which originated with Copernicus, and which was developed and completed by Kepler and Newton, is commonly known as the Copernican System. This system is shown in Fig. 64.
  • 59.
  • 60.
    II. THE SUNAND PLANETS. I. THE EARTH. Form and Size. 55. Form of the Earth.—In ordinary language the term horizon denotes the line that bounds the portion of the earth's surface that is visible at any point. (1) It is well known that the horizon of a plain presents the form of a circle surrounding the observer. If the latter moves, the circle moves also; but its form remains the same, and is modified only when mountains or other obstacles limit the view. Out at sea, the circular form of the horizon is still more decided, and changes only near the coasts, the outline of which breaks the regularity. Here, then, we obtain a first notion of the rotundity of the earth, since a sphere is the only body which is presented always to us under the form of a circle, from whatever point on its surface it is viewed. (2) Moreover, it cannot be maintained that the horizon is the vanishing point of distinct vision, and that it is this which causes the appearance of a circular boundary, because the horizon is enlarged when we mount above the surface of the plain. This will be evident from Fig. 65, in which a mountain is depicted in the middle of a plain, whose uniform curvature is that of a sphere. From the foot of the mountain the spectator will have but a very limited horizon. Let him ascend half way, his visual radius extends, is inclined below the first horizon, and reveals a more extended circular area. At the summit of the mountain the horizon still increases; and, if the
  • 61.
    atmosphere is pure,the spectator will see numerous objects where from the lower stations the sky alone was visible. Fig. 65. This extension of the horizon would be inexplicable if the earth had the form of an extended plane. (3) The curvature of the surface of the sea manifests itself in a still more striking manner. If we are on the coast at the summit of a hill, and a vessel appears on the horizon (Fig. 66), we see only the tops of the masts and the highest sails; the lower sails and the hull are invisible. As the vessel approaches, its lower part comes into view above the horizon, and soon it appears entire.
  • 62.
    Fig. 66. In thesame manner the sailors from the ship see the different parts of objects on the land appear successively, beginning with the highest. The reason of this will be evident from Fig. 67, where the course of a vessel, seen in profile, is figured on the convex surface of the sea. Fig. 67.
  • 63.
    As the curvatureof the ocean is the same in every direction, it follows that the surface of the ocean is spherical. The same is true of the surface of the land, allowance being made for the various inequalities of the surface. From these and various other indications, we conclude that the earth is a sphere. 56. Size of the Earth.—The size of the earth is ascertained by measuring the length of a degree of a meridian, and multiplying this by three hundred and sixty. This gives the circumference of the earth as about twenty-five thousand miles, and its diameter as about eight thousand miles. We know that the two stations between which we measure are one degree apart when the elevation of the pole at one station is one degree greater than at the other. 57. The Earth Flattened at the Poles.—Degrees on the meridian have been measured in various parts of the earth, and it has been found that they invariably increase in length as we proceed from the equator towards the pole: hence the earth must curve less and less rapidly as we approach the poles; for the less the curvature of a circle, the larger the degrees on it.
  • 64.
    Fig. 68. 58. TheEarth in Space.—In Fig. 68 we have a view of the earth suspended in space. The side of the earth turned towards the sun is illumined, and the other side is in darkness. As the planet rotates on its axis, successive portions of it will be turned towards the sun. As viewed from a point in space between it and the sun, it will present
  • 65.
    light and darkportions, which will assume different forms according to the portion which is illumined. These different appearances are shown in Fig. 69. Fig. 69. Day and Night.
  • 66.
    59. Day andNight.—The succession of day and night is due to the rotation of the earth on its axis, by which a place on the surface of the earth is carried alternately into the sunshine and out of it. As the sun moves around the heavens on the ecliptic, it will be on the celestial equator when at the equinoxes, and 23-1/2° north of the equator when at the summer solstice, and 23-1/2° south of the equator when at the winter solstice. 60. Day and Night when the Sun is at the Equinoxes.—When the sun is at either equinox, the diurnal circle described by the sun will coincide with the celestial equator; and therefore half of this diurnal circle will be above the horizon at every point on the surface of the globe. At these times day and night will be equal in every part of the earth. Fig. 70.
  • 67.
    Fig. 71. The equalityof days and nights when the sun is on the celestial equator is also evident from the following considerations: one-half of the earth is in sunshine all of the time; when the sun is on the celestial equator, it is directly over the equator of the earth, and the illumination extends from pole to pole, as is evident from Figs. 70 and 71, in the former of which the sun is represented as on the eastern horizon at a place along the central line of the figure, and in the latter as on the meridian along the same line. In each diagram it is seen that the illumination extends from pole to pole: hence, as the earth rotates on its axis, every place on the surface will be in the sunshine and out of it just half of the time. 61. Day and Night when the Sun is at the Summer Solstice.—When the sun is at the summer solstice, it will be 23-1/2° north of the celestial equator. The diurnal circle described by the sun will then be 23-1/2° north of the celestial equator; and more than half of this diurnal circle will be above the horizon at all places north of the equator, and less than half of it at places south of the equator:
  • 68.
    hence the dayswill be longer than the nights at places north of the equator, and shorter than the nights at places south of the equator. At places within 23-1/2° of the north pole, the entire diurnal circle described by the sun will be above the horizon, so that the sun will not set. At places within 23-1/2° of the south pole of the earth, the entire diurnal circle will be below the horizon, so that the sun will not rise. Fig. 72.
  • 69.
    Fig. 73. The illuminationof the earth at this time is shown in Figs. 72 and 73. In Fig. 72 the sun is represented as on the western horizon along the middle line of the figure, and in Fig. 73 as on the meridian. It is seen at once that the illumination extends 23-1/2° beyond the north pole, and falls 23-1/2° short of the south pole. As the earth rotates on its axis, places near the north pole will be in the sunshine all the time, while places near the south pole will be out of the sunshine all the time. All places north of the equator will be in the sunshine longer than they are out of it, while all places south of the equator will be out of the sunshine longer than they are in it. 62. Day and Night when the Sun is at the Winter Solstice.—When the sun is at the winter solstice, it is 23-1/2° south of the celestial equator. The diurnal circle described by the sun is then 23-1/2° south of the celestial equator. More than half of this diurnal circle will therefore be above the horizon at all places south of the equator, and less than half of it at all places north of the equator: hence the days will be longer than the nights south of the equator, and shorter
  • 70.
    than the nightsat places north of the equator. At places within 23- 1/2° of the south pole, the diurnal circle described by the sun will be entirely above the horizon, and the sun will therefore not set. At places within 23-1/2° of the north pole, the diurnal circle described by the sun will be wholly below the horizon, and therefore the sun will not rise. The illumination of the earth at this time is shown in Figs. 74 and 75, and is seen to be the reverse of that shown in Figs. 72 and 73. Fig. 74.
  • 71.
    Fig. 75. 63. Variationin the Length of Day and Night.—As long as the sun is north of the equinoctial, the nights will be longer than the days south of the equator, and shorter than the days north of the equator. It is just the reverse when the sun is south of the equator. The farther the sun is from the equator, the greater is the inequality of the days and nights. The farther the place is from the equator, the greater the inequality of its days and nights. When the distance of a place from the north pole is less than the distance of the sun north of the equinoctial, it will have continuous day without night, since the whole of the sun's diurnal circle will be above the horizon. A place within the same distance of the south pole will have continuous night.
  • 72.
    When the distanceof a place from the north pole is less than the distance of the sun south of the equinoctial, it will have continuous night, since the whole of the sun's diurnal circle will then be below the horizon. A place within the same distance of the south pole will then have continuous day. At the equator the days and nights are always equal; since, no matter where the sun is in the heavens, half of all the diurnal circles described by it will be above the horizon, and half of them below it. 64. The Zones.—It will be seen, from what has been stated above, that the sun will at some time during the year be directly overhead at every place within 23-1/2° of the equator on either side. This belt of the earth is called the torrid zone. The torrid zone is bounded by circles called the tropics; that of Cancer on the north, and that of Capricorn on the south. It will also be seen, that, at every place within 23-1/2° of either pole, there will be, some time during the year, a day during which the sun will not rise, or on which it will not set. These two belts of the earth's surface are called the frigid zones. These zones are bounded by the arctic circles. The nearer a place is to the poles, the greater the number of days on which the sun does not rise or set. Between the frigid zones and the torrid zones, there are two belts on the earth which are called the temperate zones. The sun is never overhead at any place in these two zones, but it rises and sets every day at every place within their limits. 65. The Width of the Zones.—The distance the frigid zones extend from the poles, and the torrid zones from the equator, is exactly equal to the obliquity of the ecliptic, or the deviation of the axis of the earth from the perpendicular to the plane of its orbit. Were this deviation forty-five degrees, the obliquity of the ecliptic would be forty-five degrees, the torrid zone would extend forty-five degrees from the equator, and the frigid zones forty-five degrees from the poles. In this case there would be no temperate zones. Were this deviation fifty degrees, the torrid and frigid zones would overlap ten
  • 73.
    degrees, and therewould be two belts of ten degrees on the earth, which would experience alternately during the year a torrid and a frigid climate. Were the axis of the earth perpendicular to the plane of the earth's orbit, there would be no zones on the earth, and no variation in the length of day and night. 66. Twilight.—Were it not for the atmosphere, the darkness of midnight would begin the moment the sun sank below the horizon, and would continue till he rose again above the horizon in the east, when the darkness of the night would be suddenly succeeded by the full light of day. The gradual transition from the light of day to the darkness of the night, and from the darkness of the night to the light of day, is called twilight, and is due to the diffusion of light from the upper layers of the atmosphere after the sun has ceased to shine on the lower layers at night, or before it has begun to shine on them in the morning. Fig. 76. Let ABCD (Fig. 76) represent a portion of the earth, A a point on its surface where the sun S is setting; and let SAH be a ray of light just grazing the earth at A, and leaving the atmosphere at the point H. The point A is illuminated by the whole reflective atmosphere HGFE.
  • 74.
    The point B,to which the sun has set, receives no direct solar light, nor any reflected from that part of the atmosphere which is below ALH; but it receives a twilight from the portion HLF, which lies above the visible horizon BF. The point C receives a twilight only from the small portion of the atmosphere; while at D the twilight has ceased altogether. 67. Duration of Twilight.—The astronomical limit of twilight is generally understood to be the instant when stars of the sixth magnitude begin to be visible in the zenith at evening, or disappear in the morning. Twilight is usually reckoned to last until the sun's depression below the horizon amounts to eighteen degrees: this, however, varies; in the tropics a depression of sixteen or seventeen degrees being sufficient to put an end to the phenomenon, while in England a depression of seventeen to twenty-one degrees is required. The duration of twilight differs in different latitudes; it varies also in the same latitude at different seasons of the year, and depends, in some measure, on the meteorological condition of the atmosphere. When the sky is of a pale color, indicating the presence of an unusual amount of condensed vapor, twilight is of longer duration. This happens habitually in the polar regions. On the contrary, within the tropics, where the air is pure and dry, twilight sometimes lasts only fifteen minutes. Strictly speaking, in the latitude of Greenwich there is no true night from May 22 to July 21, but constant twilight from sunset to sunrise. Twilight reaches its minimum three weeks before the vernal equinox, and three weeks after the autumnal equinox, when its duration is an hour and fifty minutes. At midwinter it is longer by about seventeen minutes; but the augmentation is frequently not perceptible, owing to the greater prevalence of clouds and haze at that season of the year, which intercept the light, and hinder it from reaching the earth. The duration is least at the equator (an hour and twelve minutes), and increases as we approach the poles; for at the former there are two twilights every twenty-four hours, but at the latter only two in a year, each lasting about fifty days. At the north pole the sun is below the horizon for six months, but from Jan. 29 to the vernal equinox, and from the autumnal equinox to Nov. 12, the sun is less than eighteen degrees below the horizon; so that there is twilight during the whole of these intervals, and thus the length of the actual night is reduced to two months and a half. The length of the day in these regions is about six months, during the whole of which time the sun is constantly above the horizon. The general rule is, that to the inhabitants of an oblique sphere the twilight is longer in proportion as the place is nearer the
  • 75.
    elevated pole, andthe sun is farther from the equator on the side of the elevated pole. The Seasons. 68. The Seasons.—While the sun is north of the celestial equator, places north of the equator are receiving heat from the sun by day longer than they are losing it by radiation at night, while places south of the equator are losing heat by radiation at night longer than they are receiving it from the sun by day. When, therefore, the sun passes north of the equator, the temperature begins to rise at places north of the equator, and to fall at places south of it. The rise of temperature is most rapid north of the equator when the sun is at the summer solstice; but, for some time after this, the earth continues to receive more heat by day than it loses by night, and therefore the temperature continues to rise. For this reason, the heat is more excessive after the sun passes the summer solstice than before it reaches it. 69. The Duration of the Seasons.—Summer is counted as beginning in June, when the sun is at the summer solstice, and as continuing until the sun reaches the autumnal equinox, in September. Autumn then begins, and continues until the sun is at the winter solstice, in December. Winter follows, continuing until the sun comes to the vernal equinox, in March, when spring begins, and continues to the summer solstice. In popular reckoning the seasons begin with the first day of June, September, December, and March. The reason why winter is counted as occurring after the winter solstice is similar to the reason why the summer is placed after the summer solstice. The earth north of the equator is losing heat most rapidly at the time of the winter solstice; but for some time after this it loses more heat by night than it receives by day: hence for some time the temperature continues to fall, and the cold is more intense after the winter solstice than before it.
  • 76.
    Fig. 77. Of course,when it is summer in the northern hemisphere, it is winter in the southern hemisphere, and the reverse. Fig. 77 shows the portion of the earth's orbit included in each season. It will be seen that the earth is at perihelion in the winter season for places north of the equator, and at aphelion in the summer season. This tends to mitigate somewhat the extreme temperatures of our winters and summers.
  • 77.
    Fig. 78. 70. TheIllumination of the Earth at the different Seasons.—Fig. 78 shows the earth as it would appear to an observer at the sun during each of the four seasons; that is to say, the portion of the earth that is receiving the sun's rays. Figs. 79, 80, 81, and 82 are enlarged views of the earth, as seen from the sun at the time of the summer solstice, of the autumnal equinox, of the winter solstice, and of the vernal equinox.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
    Fig. 83 is,so to speak, a side view of the earth, showing the limit of sunshine on the earth when the sun is at the summer solstice; and Fig. 84, showing the limit of sunshine when the sun is at the autumnal equinox. Fig. 84. 71. Cause of the Change of Seasons.—Variety in the length of day and night, and diversity in the seasons, depend upon the obliquity of the ecliptic. Were there no obliquity of the ecliptic, there would be no inequality in the length of day and night, and but slight diversity of seasons. The greater the obliquity of the ecliptic, the greater would be the variation in the length of the days and nights, and the more extreme the changes of the seasons. Tides. 72. Tides.—The alternate rise and fall of the surface of the sea twice in the course of a lunar day, or of twenty-four hours and fifty-one
  • 83.
    Welcome to ourwebsite – the perfect destination for book lovers and knowledge seekers. We believe that every book holds a new world, offering opportunities for learning, discovery, and personal growth. That’s why we are dedicated to bringing you a diverse collection of books, ranging from classic literature and specialized publications to self-development guides and children's books. More than just a book-buying platform, we strive to be a bridge connecting you with timeless cultural and intellectual values. With an elegant, user-friendly interface and a smart search system, you can quickly find the books that best suit your interests. Additionally, our special promotions and home delivery services help you save time and fully enjoy the joy of reading. Join us on a journey of knowledge exploration, passion nurturing, and personal growth every day! ebookbell.com