简体   繁体   中英

Push promise functions to array. use Promise.all. Promises get resolved to early

I'm pretty new to Node.js. I have a problem with promise functions and Promise.all .

Either I misunderstood some concept or I'm doing something wrong. I've tried researching on that topic but could not find the right answer to my specific problem.

The situation is as follows.

  • I have a promise function to generate a pdf, that returns the path of the generated pdf.

     var myNicePdfCreator={ setting1: value1, setting2: value2, createPdf: function(customerId){ return new Promise(function(resolve, reject){ dataBase.fetchForId(customerId).then(function(data){ somePdfLibrary.createPdf.then(function(path){ resolve(path); },function(err){ //error creating pdf reject(err); }); },function(err){ //error fetching from db reject(err); } }) } }
  • I have a promise function that takes an email address and path to the pdf and then sends an email with the attached pdf

    var myNiceMailSender={ setting1: value1, setting2: value2, sendMail: function(email, path){ return new Promise(function(resolve, reject){ someMailLibrary.createMail(email, title, message, attachment).then(function(status){ resolve(status); },function(err){ reject(err); }); }); } }

I want to do this for every object in an array (for example get a report for every customer and then email it to them). I was trying to come up with a promise function that first creates the pdf and then sends the mail and use a forEach loop to push the promise to an array and then use Promise.all to make all the PDFs and send out the mails. but whatever I try, whenever I push a promise to an array it already gets resolved before I even use Promise.all even if I just try pushing one of both promise functions to the array.

If i do this;

 var promises=[];
    AllCustomers.forEach(customer){
     promises.push(myNicePdfCreator.createPdf(customer.id));
    });

The PDFs are directly created when I push them to the array. I don't even need to call Promise.all .

The same if I try to push the promise function to an array that sends the emails, the mails are sent instantly.

Can anyone point me in the right direction why my promises get resolved when I push them to the array?

Is there a better way to create the PDFs and then email them?

Any help appreciated thank you!

I suppose what you want is to get all the pdfs gereated before you start sending the emails. As someone already said, when you call a Promise, unless you have a .then or an await for it, its execution is not going to wait.

const promises=[];
for(const customer of AllCustomers){
  promises.push(myNicePdfCreator.createPdf(customer.id));
});
Promise.all(promises).then((paths) => {
   // paths is going to have an array with the paths of all the pdfs already generated
});

With this code, in Promise.all is going to wait until all pdfs are generated. So inside the then, you could send the emails.

If you want to create an array of unresolved, unprocessing promises, which create a report for each customer and then email that customer, it would look something like this:

const pfuncs = AllCustomers.map(customer => {
  return async function() {
    const pdfPath = await myNicePdfCreator.createPdf(customer.id);
    const status = await myNiceMailSendor.sendMail(customer.email, pdfPath);
    return {status, pdfPath};
  }
})

This creates an array of functions -- the 'createPdf' request hasn't started to run yet, it's waiting for you to call each function in the array.

When you're ready to send the requests, you would do

const results = await Promise.all(pfuncs.map(f => f()));

And now results is an array that looks like [{status: 200, pdfPath: ''} , {status: 200, pdfPath: ''}, ...]

The promises are executed when they are declared. If you want to "lock" all the promises till all are defined you could encapsulate in a function and after declaring all, make a loop to execute it.

// Await "a" seconds to finish, reject if "a" o "b" are negative
function myPromiseFunction(a, b) {
    return new Promise((res, rej) => {
        setTimeout(() => {
            if (a < 0 || b < 0) {
                rej(0);
            } else {
                res(a+b);
            }
        }, a * 1000);
    })
}

(() => {
    let someData = [{a:2,b:2}, {a:10, b:4}];

    // Generate promises in functions
    let myPromises = someData.map((v) => {
        return () => myPromiseFunction(v.a, v.b);
    });

    // Execute all
    myPromises = myPromises.map((promise) => promise());

    // ...
})();

The Promise.all function only awaits to all the promises to finish the process or any promise is rejected. For example:

All promises good:

// Await "a" seconds to finish, reject if "a" o "b" are negative
function myPromiseFunction(a, b) {
    return new Promise((res, rej) => {
        setTimeout(() => {
            if (a < 0 || b < 0) {
                rej(0);
            } else {
                res(a+b);
            }
        }, a * 1000);
    })
}

(() => {
    let someData = [{a:2,b:2}, {a:10, b:4}];

    // Generate promises in functions
    let myPromises = someData.map((v) => {
        return () => myPromiseFunction(v.a, v.b);
    });

    // Execute all
    myPromises = myPromises.map((promise) => promise());

    // Await all
    Promise.all(myPromises).then(res => {
        console.log(res, myPromises);
    }).catch(err => {
        console.log(err, myPromises);
    });
})();

You will print the console.log in then and will be like this:

// res: [ 4, 14 ]
// myPromises: [ Promise { 4 }, Promise { 14 } ]

But if you have a promise who fails like this:

let someData = [{a:10,b:2}, {a:4, b:-4}, {a:2, b:4}];

The second promise will be rejected by the negative value, but the first promise will not resolve (10 seconds to end) so the output of the catch will be:

// err: 0 (this is the error of the first rejected promise)
// myPromises: [ 
//     Promise { <pending> }, // The rejected promise will not stop the pendings
//     Promise { <rejected> 0 }, // The rejected promise
//     Promise { 6 }, // A "resolved" or maybe "pending"
// ]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM