`I vow to never delay until the task is complete``

I am currently experimenting with chaining promises together in order to execute a series of requests. As someone who is fairly new to working with promises, I'm not entirely confident in my approach. I'm encountering an issue where the third promise does not wait for the second one to complete, resulting in incomplete information being retrieved.

After examining my functions, it seems that they are returning the correct information. My main question revolves around how I can structure my promises so that each one waits for the previous one to finish before proceeding?

function getPaths(folderpath) {
  return new Promise(function(resolve, reject) {
    db.getPaths(folderpath, function(response) {
      //get paths for files and folders
      for (let i = 0; i < response.entries.length; i++) {
        let entries = response.entries[i];
        if (entries[".tag"] == "folder") {
          folderPaths.push(response.entries[i].path_lower);
        }
        if (entries[".tag"] == "file") {
          filePaths.push(response.entries[i].path_lower);
        }
      }
      resolve();
    });
  });
}

function openFolders(folders) {
  return new Promise(function(resolve, reject) {
    for (let i = 0; i < folders.length; i++) {
      db.getPaths(folders[i], function(response) {
        for (let j = 0; j < response.entries.length; j++) {
          let entries = response.entries[j];
          if (entries[".tag"] == "file") {
            filePaths.push(response.entries[j].path_lower);
          }
        }
        //console.log(filePaths); //returns correct information
      });
    }
    resolve();
  });
}

getPaths("/path").then(function() {
  openFolders(folderPaths);
}).then(function() {
  console.log(filePaths); //returns incomplete information
});

Answer №1

You are facing a significant issue highlighted in bold

function openFolders(folders) {
  return new Promise(function(resolve, reject) {
    for (let i = 0; i < folders.length; i++) {
      <b>db.getPaths(folders[i], function(response) {
        // ...
      });</b>
    }
    resolve();
  });
}</pre>

You have encountered a situation where you are executing a synchronous for loop around an asynchronous function call. The loop completes its iteration synchronously and then directly calls resolve(). This means that the asynchronous code inside the handler (e.g., filePaths.push(...)) does not execute before the Promise is resolved.


Your challenges do not end there. You are using Promises unconventionally by modifying global state and resolving an empty Promise with resolve() instead of resolve(someValue). @FrankModica's response delves deeper into this unconventional use.


To address these issues, I suggest exploring the usage of Promise.all and adopting a more standard approach when working with Promises.

In the example below, I have simulated your db function to read from a fictional in-memory database called fakedb. Subsequently, I created a getPaths function that wraps around db.getPaths but returns a Promise instead. Then, I revamped openFolders to create an array of Promises, pass them to Promise.all, and finally obtain the desired values upon resolution.

Note: Promise.all will execute the array of Promises in parallel. If running them sequentially is preferred, you can use .reduce in conjunction with .then.

const db = {
  getPaths: (path, k) => {
    setTimeout(k, 50, fakedb[path])
  }
}

function getPaths (path) {
  return new Promise((resolve, reject) =>
    db.getPaths(path, response => resolve(response)))
}

function openFolders (folders) {
  return Promise.all(folders.map(getPaths))
    .then(responses =>
      responses.reduce((acc, {entries}) =>
        acc.concat(entries.filter(x => x['.tag'] === 'file').map(x => x.path_lower)), []))
}

const fakedb = {
  'cat': {
    'entries': [
        { '.tag': 'dir', 'path_lower': './cat/.' },
        { '.tag': 'dir', 'path_lower': './cat/..' },
        { '.tag': 'file', 'path_lower': './cat/a' },
        { '.tag': 'file', 'path_lower': './cat/b' },
        { '.tag': 'file', 'path_lower': './cat/c' }
      ]
  },
  'dog': {
    'entries': [
        { '.tag': 'dir', 'path_lower': './dog/.' },
        { '.tag': 'dir', 'path_lower': './dog/..' },
        { '.tag': 'file', 'path_lower': './dog/a' },
        { '.tag': 'file', 'path_lower': './dog/b' },
        { '.tag': 'file', 'path_lower': './dog/c' }
      ]
  }
}

openFolders(['cat','dog']).then(console.log, console.error)

The operation performed within openFolders may seem intricate as it handles all transformations of the responses in a single .then handler. Optionally, you could segregate the tasks into multiple .then calls to alleviate cognitive load.

function openFolders (folders) {
  return Promise.all(folders.map(getPaths))
    // extract `.entries` from each response
    .then(responses =>
      responses.map(x => x.entries))
    // flatten array of entries arrays into a single array
    .then(arrOfEntries =>
      arrOfEntries.reduce((acc, entries) =>
        acc.concat(entries), []))
    // retain only entries where `.tag` is `'file'`
    .then(entries =>
      entries.filter(x => x['.tag'] === 'file'))
    // retrieve `path_lower` of resulting entries
    .then(entries =>
      entries.map(x => x.path_lower))
}

Answer №2

It seems that you are utilizing variables from the external scope (folderPaths and filePaths). While this may work, there could be better approaches to consider. One suggestion is to ensure that you return the promise from openFolders, allowing it to complete before moving on:

getPaths("/path").then(function() {
  return openFolders(folderPaths);
}).then(function() {
  console.log(filePaths);
});

Another recommendation would be to resolve the required information before proceeding with the next function call:

resolve(folderPaths);

And:

resolve(filePaths);

This way, your code structure could resemble something like this:

getPaths("/path").then(function(folderPaths) {
  return openFolders(folderPaths);
}).then(function(filePaths) {
  console.log(filePaths); 
});

Edit: Another potential issue pointed out by @naomik is that if db.getPaths operates asynchronously, the openFolders function might resolve before all asynchronous calls within the loop are completed. If this is the case, further adjustments may be necessary. Perhaps @naomik can provide a solution when available.

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

Trouble accessing Facebook Messenger through the integrated browser on Facebook network

My goal is to promote a webpage by sharing it on Facebook Messenger. While everything works smoothly on desktop and mobile browsers, the issue arises when using Facebook's built-in browser - the Facebook Messenger app fails to open and the page just r ...

"Utilizing the power of the Neo4j Driver in JavaScript to parse JSON

While it may not be a significant issue, I can't shake the feeling that there must be a more efficient way to achieve my goal. Currently, I am developing an API in Express with data stored in a neo4j database. To interact with neo4j, which is running ...

What is the best way to refresh a Windows 7 command prompt screen before executing a new function in Node.js?

I attempted system calls for cls and also tested out this code snippet: function clear() { process.stdout.write('\u001B[2J\u001B[0;0f'); } Unfortunately, none of the options seem to be effective. ...

Hovering over Ajax in jQuery does not trigger an update

I recently followed a helpful guide on creating an Ajax example of jQueryUI tooltip. I made some tweaks to load different files using data attributes. The issue I'm facing is that the tooltip only displays the first content even when hovering over the ...

How to Fetch JSON Data from a URL using JQuery AJAX?

Working on a coding project that involves retrieving data from an API. My approach is to fetch json data and decode it using file_get_contents and json_decode in PHP, which I've successfully done before. Here's the specific scenario: I am imple ...

Using ISO-8601 format to display dates in JSON within a .NET WebService

While working with a .net asmx webservice, I encountered an issue with the date format being returned. The date field is in the form of: "effective_date":"\/Date(978411600000)\/" After researching on Stack Overflow here: How do I format a Micros ...

What is the best way to verify a v-if condition with an array in Vue.js HTML?

I am looking to dynamically add rows based on multiple options selected from a dropdown menu. When I select one option at a time, I am able to get the desired result. However, when I select multiple options, no rows are added. For example, if I select opt ...

What is the most effective way to extract the output from a JSONP request and utilize it beyond the

I have a function that is working well. I had to use JSONP to handle cross-domain issues, and I also wrote an HTTP module to change the content-type. However, I did not include a callback name in the URL. function AddSecurityCode(securityCode, token) { va ...

What is the method for implementing conditions within a button element in Vue.js 2?

Here is an example of my component code: ... <button type="button" class="btn btn-default" data-dismiss="modal">Close</button> ... <script> import { mapGetters } from 'vuex' export default{ ...

Tips for managing the react-bootstrap carousel using personalized buttons

In order to have control over carousel slides using custom buttons, I managed to achieve this with reference to this Example. Below is the code implementation: import React, { useState } from "react"; import { Carousel, Button, Container, Row } ...

Show image stored in Django database on React JS interface

I'm encountering an issue where the images I upload to my Django database are not showing up on the React JS frontend. My model looks like this: class Project(models.Model): title = models.CharField(max_length=100, null=False) description = mo ...

the integration of shapes in three.js is malfunctioning

There has been a dynamic change in the path. The previous render function was as follows (where LENGTH, getPosition(), RADIUS, MATERIAL, and SCENE have already been set) var prevPosition = getPosition(); for(var i =0; i < LENGTH; i++) { drawPath(g ...

Angular form submission results in receiving an object instead of a simple value

While building a basic Angular form, I am encountering an issue where instead of retrieving the form value upon submission using onsubmit, I am getting an object as the value. Side Note: I have included some sample code for reference. Could you please ad ...

What is the proper way to implement parameters and dependency injection within a service?

My objective is to achieve the following: (function(){angular.module('app').factory("loadDataForStep",ls); ls.$inject = ['$scope','stepIndex'] function ls ($scope, stepIndex) { if ($routeParams ...

"Repeatedly encountering 'undefined' when subscribing to the selected option of a select list in Knockout.js

In my dropdown list, I have prepopulated a selection of strings. My select list is bound as follows: <select id="industryDropDown" data-bind="options: $root.orgIndustrySuggestions, value: $root.selectedIndustryTag, optionsCaption: ''"> ...

Vue Router configuration functions properly when accessed through URL directly

I need guidance on how to handle the routing setup in this specific scenario: Below is the HTML structure that iterates through categories and items within those categories. The <router-view> is nested inside each category element, so when an item i ...

Am I on the right track with my service definition in Angular?

(function(){ angular.module('myApp',[]) })(); (function(){ angular.module('myApp.dashboard',[]) })(); (function(){ angular.module('myApp.value',[]) })(); (function(){ 'use strict'; angular.modu ...

How to use an Angular filter to extract boolean values from an API response?

I am currently working on a feature to display whether a user is active or not based on data retrieved from an API in Boolean form. The API returns 'true' if the user is active and 'false' if not. To achieve this, I initially implemente ...

Having Trouble Creating the production APK in React Native

Having trouble generating an APK after following the steps outlined in the documentation. Build Failed even though I tried: 1. cd android 2. gradlew clean "build Successful" 3. gradlew assembleRelease "BUILD FAILED" with this Erro ...

Difficulties in rendering the JSON data retrieved from a server

I am diving into the world of JSON/AJAX and running into some issues with extracting data from a JSON object retrieved from a server. The URL "http://localhost:8387/rest/resourcestatus.json" contains the object that I want to showcase using HTML/JavaScript ...