Promise.all with external resolve never resolves

136 views Asked by At

I'm working on a SPA (Single Page Application) project and need to load files in a dynamic and controlled way (CSS, HTML and JS)

I created the structure below (in this example only for CSS), but although the files are injected into the page, the promise is never resolved

A peculiarity of the structure is that the "resolve" and "reject" of each "promise" created are saved to be executed outside the original "promise" function

This is necessary because if the "promise" is successful, the control events must be removed, and if an error occurs, the element must be removed

'use strict';

const

  css=
    (
      ()=>
      {
        const
          load=
            url=>
            {
              return new Promise(
                (resolve,reject)=>
                {
                  let
                    link=document.createElement("link");
                  link.setAttribute("href",url);
                  link.setAttribute("rel","stylesheet");
                  link.addEventListener("load",onSuccess);
                  link.addEventListener("error",onError);
                  resolveAhead=resolve;
                  rejectAhead=reject;
                  document.head.appendChild(link);
                }
              );
            },
          onSuccess=
            event=>
            {
              event.target.removeEventListener("load",onSuccess);
              event.target.removeEventListener("error",onError);
              resolveAhead(event);
            },
          onError=
            event=>
            {
              document.head.removeChild(event.target);
              rejectAhead(event);
            };
        let
          resolveAhead,
          rejectAhead;
        return {
          load
        }
      }
    )();

Promise.all(
  [
    css.load("style1.css"),
    css.load("style2.css"),
    css.load("style3.css"),
    css.load("style4.css"),
    css.load("style5.css"),
    css.load("style6.css")
  ]
).then(
  response=>
  {
    console.log(response);
    alert("Well done");
  }
).catch(
  response=>
  {
    console.log(response);
    alert("Houston... we've had a problem here");
  }
);
1

There are 1 answers

0
Nick Parsons On BEST ANSWER

Your issue is that you have two variables:

let resolveAhead, rejectAhead;

The first time you call .load(), you set these to be the resolve and reject functions of the new Promise you're constructing. However, the next time you call .load(), you're again creating a new Promise, and overwriting these variables to be the resolve/reject of the new Promise. Only the resolve/reject functions from the last constructed Promise can then be called.

You can create references to these inside of your load() function instead, so that further calls to load() don't impact the already defined resolveAhead and rejectAhead functions:

'use strict';
const css = (() => {
  const load = url => {
    let resolveAhead, rejectAhead;
    
    const onSuccess = event => {
      event.target.removeEventListener("load", onSuccess);
      event.target.removeEventListener("error", onError);
      resolveAhead(event);
    };
    const onError = event => {
      document.head.removeChild(event.target);
      rejectAhead(event);
    };
    
    return new Promise(
      (resolve, reject) => {
        let link = document.createElement("link");
        link.setAttribute("href", url);

        link.setAttribute("rel", "stylesheet");
        link.addEventListener("load", onSuccess);
        link.addEventListener("error", onError);
        resolveAhead = resolve;
        rejectAhead = reject;
        document.head.appendChild(link);
      }
    );
  };
  
  return {load};
})();

Promise.all([
 css.load("https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"),
 css.load("https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css"),
]).then(response => {
  console.log(response);
  alert("Well done");
}).catch(response=> {
  console.log(response);
  alert("Houston... we've had a problem here");
});