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

Unable to integrate the datepicker module into angular.js framework

I encountered an issue when trying to integrate the angular-datepicker module using angular.js. Error: Uncaught Error: [$injector:modulerr] http://errors.angularjs.org/1.4.6/$injector/modulerr?p0=Channabasavashwara&…0at%20d%20(http%3A%2F%2Fodite ...

Ways to extract pertinent information from a PHP API

I've been attempting to add parameters to my query, but I keep getting inconsistent results. Despite trying different methods, I haven't been successful. Take a look at the code below. First, here is my code that functions properly without using ...

What is the best way to implement the settimeout method in react?

I need assistance on how to effectively utilize the setTimeout() method in my code. Specifically, I am looking to trigger a click event on an element after a certain delay and execute a function afterwards. Here is the current implementation of my code: ...

When the program is executed, immediately use .trigger('click')

There is a spelling game that features a grid filled with hidden words. The objective of the game is to spell out these words by clicking on the letters of the alphabet, aided by hints such as images and sounds. Players are given the word they need to spe ...

Determining the condition of the menu: understanding whether it is open or closed

I'm diving into the world of jQuery and JavaScript, trying to grasp the ins and outs of the mmenu API. Despite my efforts to understand the libraries, the JavaScript code remains a mystery to me. Following the tutorial provided on , I successfully cr ...

The visibility of content that flickers on the webpage should be hidden with the display: none property during loading

Currently working on a new toy website, but encountering some unexpected behavior. On the homepage HTML file, there are two separate sets of <body> tags: <body class = "intro">...</body> <body class = "home">...</body& ...

Extend the row of the table according to the drop-down menu choice

I am working on a feature where a dropdown menu controls the expansion of rows in a table. Depending on the option selected from the dropdown, different levels of items need to be displayed in the table. For example, selecting level 1 will expand the first ...

JavaScript Question: How can I extract the click value from a JavaScript visualization when displayed in a table?

I am working with a Hierarchical Edge Bundling visualization in JS. My goal is to have the name of the value displayed on the table when I click on it. Currently, I am facing an issue with retrieving the value dynamically. If I manually input a value, I c ...

Automatically divide the interface into essential components and additional features

Consider the following interfaces: interface ButtonProps { text: string; } interface DescriptiveButtonProps extends ButtonProps { visible: boolean, description: string; } Now, let's say we want to render a DescriptiveButton that utilize ...

Struggling to decide on the perfect CSS selector for my puppeteer script

I am trying to use puppeteer page.type with a CSS selector. <div class="preloader"> <div class="cssload-speeding-wheel"></div> </div> <section id="wrapper" class="login-register"> <div class="login-box"> <d ...

Special effects for the images动画效果。

Is there a way to add animation effects to images in the about section using this code: <div id="about" class="row section bgimg3"> <div class="col-sm-8"> <h2 style="color:black;">Want to Know More About me?</h2> ...

Issue with Vue Loading Overlay Component functionality in nuxt2 .0

I've integrated the vue-loading-overlay plugin into my project. plugins/vueloadingoverlaylibrary.js import Vue from 'vue'; import Loading from 'vue-loading-overlay'; // import 'vue-loading-overlay/dist/vue-loading.css'; ...

Display the two values of an object pair using ng-repeat in AngularJS

I have an array of objects structured like this: myCtrl.siteNameLabels = myCtrl.actual.map(function (value, index) { return { actualSite: { actualSiteName : value, actualSiteData: myCtrl.tableData[index] }, ...

Mastering the utilization of API routes within the Next JS 13 App Router framework

As a newcomer to React JS and Next.js, I recently made the switch from using the Page Router API in Next.js to utilizing the new App Router introduced in Next.js 13. Previously, with the Page Router, creating a single GET request involved nesting your "JS ...

`How can I enable the download attribute feature on Safari browser?`

Is there a workaround for saving files with a specified name in Safari? The following HTML code does not work properly in Safari, as it saves the file as 'unknown' without an extension name. <a href="data:application/csv;charset=utf-8,Col1%2C ...

Imitate adjustment of orientation without the need to resize the browser window

Is there a way to simulate page portrait orientation on a PC browser without resizing the window? In my HTML code, I have a <div> element that displays content dynamically based on screen orientation. Is it possible to use JavaScript to trick this & ...

Tips for validating an email address using ReactJS

I'm currently working on customizing the email verification process for a signup form in ReactJS. My goal is to replace the default email verification with my own validation criteria. Initially, I want to ensure that the entered email address contains ...

Utilizing One-to-Many Microphone Streaming Technology

I'm looking to create a unique one-to-many microphone streaming system where a user can record from their microphone and others can listen in. I also need to be able to record the microphone session. Would it be better to use WebRTC for client commun ...

Unable to conceal the scrollbar while keeping the div scrollable

I've been attempting to implement the techniques outlined in the guides on this site, but I'm encountering difficulty hiding the scroll bar while still maintaining scrolling functionality! My current approach involves setting the parent as relat ...

What is the best way to choose a specific row with Enzyme?

We have chosen Jest for doing UI Test-Driven Development on our React application. Our component rendering structure looks like this: <Div> <Row> </Row> <ROW> <Row> <ROW> <Link> <Link> ...