What is the reason behind the specific sequence in which this JavaScript console log is displayed?

Extracted from Chapter 11 of the book Eloquent Javascript, this code snippet showcases the use of Promises. You can interact with the code in a sandbox environment here. I recently dabbled with the Promises featured in this snippet and was surprised to find that the output was not in the expected order. Rather than seeing "1" followed by "2", the sequence was reversed. See below for the snippet and its corresponding output.

The function readStorage is invoked first, preceding the execution of the Promise. Given that readStorage is a synchronous operation, I anticipated its output to be displayed prior to the callback within then. However, it appears after the aforementioned callback's results.

Code:

import { bigOak } from "./crow-tech";

bigOak.readStorage("food caches", caches => {
    caches.forEach(firstCache => {
        bigOak.readStorage(firstCache, info => {
            console.log("1:", info);
        });
    });
});


function storage(nest, name) {
    return new Promise(resolve => {
        nest.readStorage(name, result => resolve(result));
    });
}

storage(bigOak, "enemies")
    .then(value => console.log("2: Got", value));

Output:

2: Got ["Farmer Jacques' dog", "The butcher", …]

1: A hollow above the third big branch from the bottom. Several pieces of bread and a pile of acorns.

1: Buried below the patch of nettles (south side). A dead snake.

1: Middle of the hedge at Gilles' garden. Marked with a forked twig. Two bottles of beer.

Answer №1

It isn't guaranteed that the logs for 1: always occur last, but it's more probable. The asynchronous nature of readStorage allows for other events to take place while storage is being accessed. Additionally, the food caches involve a nested load for detailed information, giving ample time for enemy loading to complete. To better understand the sequence in your scenario, additional logging can be helpful:

[food caches] Load cache in the oak
[food caches] Load cache in the meadow
[food caches] Load cache under the hedge
2: Got ["Farmer Jacques' dog", "The butcher", …]
[food caches/ cache in the oak ] A hollow above the third big branch from the bottom. Several pieces of bread and a pile of acorns.
[food caches/ cache in the meadow ] Buried below the patch of nettles (south side). A dead snake.
[food caches/ cache under the hedge ] Middle of the hedge at Gilles' garden. Marked with a forked twig. Two bottles of beer.

Furthermore, utilizing async/await provides a clearer perspective, as demonstrated here:

//import { bigOak } from "./crow-tech";
// Oddly, using async/await triggers an error related to the import
// however, the code runs properly without the import
console.log(bigOak);
bigOak.readStorageAsync = function(name) {
  return new Promise(resolve=>this.readStorage(name, resolve));
};
// load food
const loadFoods = (async() => {
    console.log("[food caches] started");
    const foodCaches = await bigOak.readStorageAsync("food caches")
    for(const foodCache of foodCaches) {
      console.log("[food caches] Load",foodCache);
      const foodInfo = await bigOak.readStorageAsync(foodCache);
      console.log("[food caches/",foodCache,"]", foodInfo);
    }
})();

const loadEnemies = (async() => {
    console.log("[enemies] started");
    const enemies = await bigOak.readStorageAsync("enemies");
    for(const enemy of enemies) {
      console.log("[enemies] Found",enemy);
      //const enemyInfo = await bigOak.readStorageAsync(enemy);
      //console.log("[enemies/",enemy,"]", enemyInfo);
    }
})();

(async() =>{
  await Promise.all([loadFoods, loadEnemies]);
  console.log("All done");
})();

From this revised code snippet, it's evident that enemy and food loading are separate asynchronous processes that function independently. Additionally, the method to wait for all operations to conclude is highlighted at the end.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Are there any methods available to adjust the size of a View component in react-native?

My react-native application includes a View that contains several components. The layout displays perfectly on iPhone 6 and 5 models, but on an iPhone 4s, the bottom of one component is being clipped slightly. I'm aware of scaling base64 icons, but I ...

Battle between Comet and Ajax polling

I'm looking to develop a chat similar to Facebook's chat feature. Using Comet would require more memory to maintain the connection. There seems to be a latency issue when using Ajax polling if requests are sent every 3-4 seconds. Considering t ...

Can a variable in Typescript be defined to accept any value as long as it follows a specific format?

For instance, I am searching for a way to represent any value that consists of two strings separated by an underscore "_". I attempted something along the lines of: let key:string = string1 + "_" + string2; Upon entering this code, I quickly realized ...

Showing pictures from a JSON source

I am currently facing an issue while trying to display the cover art along with the search results. There seems to be a problem in the img src tag that is preventing the app from loading properly. Interestingly, when I direct the img to data.tracks[i].albu ...

Issue with loop detected in <fieldset> tags - prematurely adding closing tag

In the development of my web app using Google Apps Script, I am faced with the challenge of creating a set of checkbox fields for each learner/student, displayed in rows of three. These checkboxes are generated from data stored in a spreadsheet. My goal i ...

Organizing dates with Javascript

Is there a way to convert a string from this format : 2014-06-12T23:00:00 To another format using JavaScript, like so : 12/06/2014 23:00 ...

How can we minimize the redundancy of using module.exports for multiple functions?

In index.js, I've created 2 different functions: module.exports.func1 = func1 module.exports.func2 = func2 When it comes to using them in my code, I typically require them like so: const func1 = require('./index').func1 const func2 = requ ...

Search for the booking object based on the user in MongoDB

In my Next.js app, I have 2 models - Booking and User. The object below represents a booking object. When a user books some dates, a new object is created in the bookings model. On the booking details page, users should be able to see details of their book ...

Show a table with rows that display an array from a JSON object using JavaScript

My current code includes JSON data that I need to display in table rows, but I'm struggling to understand how to do so effectively. The output I am currently seeing is all the rows from the array stacked in one row instead of five separate rows as in ...

Challenges faced when subscribing to global events in JavaScript

I have some questions about the JavaScript code snippet below: What does .events.slice(-1)[0][0] signify? Similarly, could you explain the purpose of nodes_params += "&ns=" + GLOBAL_EVENT + "," + start_from + ",-,-";? Could you elaborate on what is m ...

Navigate to a refreshed version of the page with varying parameters using React Navigation

Currently, I am utilizing React Navigation for navigating between different pages within my app. One of the pages is the Profile page which displays a user info card along with their posts. Within this Profile component, I have integrated the Post componen ...

Troubleshooting CSS transition issues on ASP.NET Web Forms

Is there a way to make CSS3 transitions work in a .aspx page? I prefer Web Forms for designing forms and any suggestions would be helpful. The following is the code snippet from the .aspx page: <%@ Page Title="" Language="C#" MasterPageFile="~/Sit ...

There seems to be a glitch preventing Highchart from functioning within a jQuery function

Hello there! I'm currently utilizing the highchart API to generate graphs in a loop. for(i=1;i<10;i++) { xseries = "{'INCOPAV','B&M','SGS-ETSA'}"; yseries = "[{name: &apos ...

How can I incorporate a fade opacity effect into my Div scrolling feature?

I successfully implemented code to make div elements stick at the top with a 64px offset when scrolling. Now, I am trying to also make the opacity of these divs fade to 0 as they scroll. I am struggling to figure out how to achieve this effect. Below is ...

Checking React props using Jest and Enzyme - A simple guide

Trying to understand React testing, I came across two files: Button.js and Button.test.js The code below contains the question along with comments: // Button.js import React from 'react'; import { string, bool, func } from 'prop-types&apos ...

Execution of Promises in sequence

In my current implementation, I have multiple promise functions structured like: return new Promise(function(resolve, reject) { //logic }); cart.getBasket(req) cart.updateBasket(req) cart.updateDefaultShipment(req) cart.getBasketObject(basket) The cod ...

Tips for storing data from a table using a button

I have implemented a data table to display information from a database. I am looking for a way to save user information whenever they export data in Excel, CSV, or PDF format from the table. I have already created a function to handle saving user data, but ...

Working with dynamic checkbox values in VueJS 2

In my project using VueJS 2 and Vuetify, I am creating a subscription form. The form is dynamic and fetches all the preferences related to a subscription from the server. In the example below, the preferences shown are specifically for a digital magazine s ...

Encountering installation issues with Next.js and experiencing a package failure with react.js during the installation process

Issue: Error encountered during Next.js installation, multiple installation failures reported including missing packages peema@DESKTOP-6UGCO8V MINGW64 ~/Documents/alert/peeapp $ next build The module 'react' was not found. Next.js requires that ...

Creating interconnected select boxes based on JSON data to display parent-child relationships in a cascading manner

A dynamic creation of chained select-boxes has been implemented based on JSON data fetched from the server. In this cascading process, each select-box is an object with specific properties: Parent Attribute: The name of the parent object for the current ...