beek
beek

Reputation: 3746

Generate an array of promises to run sequentially

I'm trying to generate an array of Promises to run sequentially. I've seen lots of tips on this but can't get it to run in my use case.

export default function generateIcons(){
  return new Promise((resolve, reject) => {
    const containers = document.querySelectorAll('.html2CanvasTarget')
    const promises = containers.map(child => processIcon(child))
    promises.reduce((p, fn) => p.then(fn), Promise.resolve())
    resolve()
  })
}

function processIcon(child){
  return new Promise((resolve, reject) => html2canvas(child).
 then(canvas => uploadFromCanvas(canvas,
  child.childNodes[0].className.split(' ')[1] + '.png'))
 .then(resolve).catch(reject))
}

Any tips? This just rejects and I can't see why

Upvotes: 0

Views: 391

Answers (3)

CertainPerformance
CertainPerformance

Reputation: 371233

containers is a NodeList, and NodeLists don't have a .map method, which is why your code is throwing an error.

Because processIcon already returns a Promise, there's no need to use the Promise constructor again. html2canvas already returns a Promise too, so there's no need for any Promise constructor anywhere (see What is the explicit promise construction antipattern and how do I avoid it?)

If possible, just await each call of it in a for loop. Because uploadFromCanvas returns a Promise too, and you want to wait for it, return it (or await it) as well:

export default async function generateIcons() {
  const containers = document.querySelectorAll('.html2CanvasTarget');
  for (const container of containers) {
    await processIcon(container);
  }
}

function processIcon(child) {
  return html2canvas(child, {backgroundColor:null})
    .then(canvas => uploadFromCanvas(canvas, child.className.split(' ')[1] + '.png'))
    .catch(console.log);        
}

Upvotes: 1

Neel Rathod
Neel Rathod

Reputation: 2111

As per your question, you can use Q module module for that

You need to take an empty array and push promises into it, and just pass this array in Q method (Q.allSettled), Take a look with an example

const Q = require('q');

const promiseHolder = [];

for (let i = 0; i < 10; i += 1) {
  promiseHolder.push('Your Promises');
}

Q.allSettled(promises)
  .then((results) => {
    results.forEach((result) => {
      if (result.state === 'fulfilled') {
        const value = result.value;
        return value;
      } 
        const reason = result.reason;
        throw reason;      
    });
  });

In Q.allSettled() The method you always get the result in .then(). There are 2 states. One for success and one for failure.

Success => state === 'fulfilled', value: 'Whatever your promise return'

Failure => state === 'rejected', reason: 'Whatever your promise thrown'

In this case, you have a number of successful and unsuccessful promises.

There is the second approach which is Promise.all() do the same but the issue is whenever any of promise rejected further promise never called.

const promiseHolder = [];

for (let i = 0; i < 10; i += 1) {
  promiseHolder.push('Your Promises');
}

Promise.all(promiseHolder)
  .then((results) => {
    return results;
  })
  .catch((err) => {
    throw err;
  });

In the second approach ( Promise.all()), It consists of all your promises pushed from for loop. If any of promise rejected no more promise called and suddenly you got the state of promise rejection in Promise.all().

Promise.all(promiseHolder)
  .then((results) => {
    return results;
  })
  .catch((err) => {
    console.log('Promise will reject here', err);
    throw err;
  });

I hope it helps, Happy Coding :)

Upvotes: 0

Charlie
Charlie

Reputation: 23858

Looks like you want to resolve the main promise when the canvases are available for all the child elements. You can use Promise.All() for this.

It should also be noted that the querySelectorAll doesn't return anything you can call the .map on. You will have to create an array from what the querySelectorAll returns - which is a NodeList.

export default function generateIcons(){
  return new Promise((resolve, reject) => {

    const containers = document.querySelectorAll('.html2CanvasTarget');
    const promises = Array.from(containers).map(child => processIcon(child))

    Promises.All(promises).then(() => resolve());
  })
}

Upvotes: 2

Related Questions