Creating multiple asynchronous calls within a loop in JavaScript

I am currently working on a task in my gulpfile.js that involves uploading an app using Gulp and SharePoint.

'use strict';

const gulp = require('gulp');
const build = require('@microsoft/sp-build-web');
const spsync = require('gulp-spsync-creds').sync;
const sppkgDeploy = require('node-sppkg-deploy');

const config = require('./dev-config.json');
var coreOptions = {
        siteUrl: config.coreOptions.siteUrl,
        appCatalog: config.coreOptions.appCatalog
    };
var creds = {
        username: config.creds.username,
        password: config.creds.password
    };

build.task('upload-single-app', {
  execute: (config) => {
      return new Promise((resolve, reject) => {
          const pluginList = require('./config/plugin-deployment.json');
          if (pluginList)
          {
            for (let i = 0; i < pluginList.plugins.length; i++) {
                  const folderLocation = `./plugins/` + pluginList.plugins[i].name;
                  for (let x = 0; x < pluginList.plugins[i].sites.length; x++) {

                        console.log(pluginList.plugins[i].sites[x]);
                        return gulp.src(folderLocation)
                        .pipe(spsync({
                            "username": creds.username,
                            "password": creds.password,
                            "site": coreOptions.siteUrl + pluginList.plugins[i].sites[x],
                            "libraryPath": coreOptions.appCatalog,
                            "publish":: true
                        }))
                        .on('finish', resolve);
                      }//end inner for
              }// end for
          } else {
            console.log("Plugin list is empty");
          }
        });
  }
});

Here is the JSON data that drives this process:

{
  "plugins":
  [
    {
      "name": "Bluebeam.OpenRevuExtension.sppkg",
      "description": "some description",
      "version":"20.2.30.5",
      "sites":["sp_site1","sp_site2"]
    }
  ]
}

Upon running the code, the package successfully deploys to site1 but not site 2 without any errors. The output looks like this:

devbox:plugintest admin$ gulp upload-single-app
Build target: DEBUG
[14:51:48] Using gulpfile /src/plugintest/gulpfile.js
[14:51:48] Starting gulp
[14:51:48] Starting 'upload-single-app'...
sp_site1
[14:51:48] Uploading Bluebeam.OpenRevuExtension.sppkg
[14:51:50] Upload successful 1919ms
[14:51:51] Published file 982ms
[14:51:51] Finished 'upload-single-app' after 2.92 s
[14:51:51] ==================[ Finished ]==================
[14:51:52] Project plugintest version:1.0.0
[14:51:52] Build tools version:3.12.1
[14:51:52] Node version:v10.24.1
[14:51:52] Total duration:6.48 s

I'm considering refactoring the code into two separate tasks to handle deployment to multiple sites asynchronously. Here is a pseudocode example of what I have in mind:

build.task('main', {
     for each plugin in json file {
         for each site I need to deploy to {
             call build.task('upload_app');
             call build.task('deploy_app');
         }
     }
  });

Do you think this approach is suitable? Any suggestions on how to implement it effectively?

Thank you.

Answer №1

Your behavior is currently being influenced by a single Promise that resolves during the first loop iteration, triggering resolve when the initial site completes.

Sequential Approach

To improve this, consider having a separate Promise for each site within the second loop. Utilize an async / await structure to ensure that each site's Promise resolves before moving on to the next one in the loop.

build.task("upload-single-app", {
  execute: async () => {
    const pluginList = require("./config/plugin-deployment.json");

    if (!pluginList) {
      return;
    }
    
    for (const { name, sites } of pluginList.plugins) {
      const folderLocation = `./plugins/${name}`;

      for (const site of sites) {
        // Wait for each gulp pipeline to finish before proceeding.
        await new Promise((resolve) => {
          gulp
            .src(folderLocation)
            .pipe(
              spsync({
                username: creds.username,
                password: creds.password,
                site: coreOptions.siteUrl + site,
                libraryPath: coreOptions.appCatalog,
                publish: true,
              })
            )
            .on("finish", resolve);
        });
      }
    }
  },
});

Parallel Approach

Alternatively, iterate through each site and create a Promise with the map function of an array. Aggregate all promises into a single array and use Promise.all() to wait until all promises are fulfilled concurrently.

build.task("upload-single-app", {
  execute: async () => {
    const pluginList = require("./config/plugin-deployment.json");

    if (!pluginList) {
      return;
    }
    
    const pluginBuilds = await Promise.all(pluginList.plugins.flatMap(({ name, sites }) => {
      const folderLocation = `./plugins/${name}`;

      return sites.map(site => new Promise((resolve) => {
        gulp
          .src(folderLocation)
          .pipe(
            spsync({
              username: creds.username,
              password: creds.password,
              site: coreOptions.siteUrl + site,
              libraryPath: coreOptions.appCatalog,
              publish: true,
            })
          )
          .on("finish", resolve);
      }));
    }));

    return pluginBuilds;
  },
});

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

Tips on effectively storing extra object properties across all entries in a MongoDB document group

Currently, I have a node module that involves parsing a .csv file into MongoDB documents. The issue I am encountering is that only the first inserted document contains certain metadata fields, while the rest do not retain these fields. I am seeking guidan ...

What steps do I need to take to extract the date and month from a single datepicker using the code below?

<div class="col-md-3 pull-left" style="padding:9px"> <input type="text" id="datepicker" class="form-control"> </div> The HTML and C ...

Vanilla JavaScript - Conceal all remaining div elements

I have a situation where multiple divs are only visible after clicking a link. How can I ensure that when one div is clicked, all others are closed so that only the clicked one remains visible? Currently, I am using the following JavaScript: functio ...

Retrieving FormData() parameters sent via Ajax with PHP

After successfully testing FormData() with jQuery.ajax, I encountered a roadblock when trying to implement a progress bar for file uploads using the same method. This led me to use AJAX directly and post form data, but unfortunately, retrieving the form da ...

Attempting to establish both the minimum and maximum time date in a jQuery datepicker across two separate inputs

I've been working on a project for my classes that requires setting minimum and maximum dates for two input fields. The first input should allow dates from now to 12 months in the future, while the second input should be restricted to dates picked wit ...

Error: The "render" method is not available for the IncomingMessage object

While working on a basic application using node.js and express, everything seems to be in order except for this error that keeps popping up: res.render("aggregatedCostList",{ ^ TypeError: Object #<IncomingMessage> has no method 'render&ap ...

Using the express.Router instance for handling errors can be a useful tool in your development

Based on the documentation, it states that any nodejs express middleware function has the capability of being swapped out by App or Router instances: Given that router and app adhere to the middleware interface, they can be used just like any other midd ...

How can I correctly update values from a sibling component that has been imported in Vue.js 2.0?

My Vue 2.0 project follows the single-file component approach and consists of three components: App (parent), AppHeader, and FormModal. Both AppHeader and FormModal are direct children of App and siblings of each other. The main objective is to toggle the ...

The JSON file overwrites entire objects instead of targeting individual ones

Is there a way to update just one specific object in a JSON file without affecting the rest? I've implemented a put request on the front-end using axios to send data to the back-end for processing. However, the current functionality replaces all obje ...

Error: Invariant Violation occurred with code 29 while using React Apollo and GraphQL

I encountered an error that says "Invariant Violation: 29." Could someone explain what this error means and if I missed something in my code that triggered it? The error occurred when I was trying to import the LocationSearch component into my index.js. im ...

Using Node.js to create a RESTful API that pulls information from MongoDB

I am currently working on creating a REST API using Node.js to retrieve the last N rows from a MongoDB collection. Here is my current code snippet: var express = require("express"); var app = express(); var bodyParser = require("body-pa ...

Learn the steps to export a constant value in Next.js using React!

I need to export the constant value views from BlogPost.js to blog.js. This is the content of BlogPost.js: import React from 'react'; import useSWR from 'swr'; import format from 'comma-number'; import { useColorMode, He ...

Ensure that the execution of the function is completed before moving on to the next iteration within a $.each loop

While I'm not an expert in JS or jQuery, I'm currently working on coding a chat application that requires the following functionality: Retrieve conversation list through an AJAX call Display the conversations on the left side of the webpage aft ...

NServicebus JavaScript message handler: Enhancing Communication

I am in the process of developing a JavaScript event subscriber for NServicebus, and I am seeking feedback on my approach as well as any potential pitfalls to watch out for in this design. My proposed components are as follows: ASP.NET MVC BusControll ...

Disabling the shadow when setting the face color in Three.js

When creating geometric objects in my project, I am randomly setting colors on the faces: // Material used to create the mesh var material = new THREE.MeshLambertMaterial({ color: 0xffffff, ambient: 0xffffff, vertexColors: THREE.FaceColors}) function ad ...

Issue with function incorrectly computing values and returning NaN

My challenge is to develop a countdown timer, but it keeps returning NaN instead of counting down. I have multiple counters in place - one that counts down, another that counts up until the stop time specified by stoptime, and a third that kicks in after s ...

Utilizing reactjs (MERN stack) to dynamically update content on a single page based on both URL parameters and database queries

Hello everyone, please excuse my English Imagine I have page1 with content in a database, and page2 with different content in another database. Both page1 and page2 share the same template, but I want to dynamically change the content based on the URL or ...

perform an action in PHP when a button is clicked

I'm currently developing a PHP admin panel that displays a list of users in an HTML table format. Each row in the table includes a button that allows the admin to send a notification to the selected user. Below is the code I used to create and displa ...

Controller encounters a error when requiring a module

Struggling to set up Stripe for my app, I've encountered some issues with the module implementation. Typically, I would require a module at the top of the file to use it. However, in the paymentCtrl file, when I do this, it doesn't work and I rec ...

Execute HTML and JS files through Eclipse PDT to view in a web browser

Is it possible to open HTML and JS files in a web browser within Eclipse PDT? Right now, only PHP files seem to launch successfully, otherwise an "Unable to Launch" dialog pops up. Any advice is appreciated! ...