1

So it seems I don't quite understand promises, but I've been using them in low code software my company uses for internal tools as a way to perform the same query on different data for a certain number of times.

Anyway, I'm currently using Promises with a Mailgun query, and when I try to resolve Promise.all(promises), I assume I'm hitting them too quickly and too much. So what I would like to do, without having to refactor the entirety of my code, is take what I have and then resolve those Promises one at a time.

let query = Mailgun_MailList_Add_Members;
//let arr = testEmailData.value;
let reps = repInfo.value;
let tableData = table1.selectedRow.data;
let finalResult = [];

for(let i = 0; i < reps.length; i++){
  let emailArr = [];
  let allRepEmails = [];

  /* function that takes an array and checks inside for subarrays, pushing all subvalues into one new array */
  let getAllRepEmails = (arr) => {
    if(arr instanceof Array){
        for(let i = 0; i < arr.length; i++){
            getAllRepEmails(arr[i]);
        }
     }
     else allRepEmails.push(arr); 
  }
  
  for(let j = 0; j < tableData.length; j++){
    /* check if current records owningrep is equal to current index of repinfos lastName */
    if(tableData[j].owningrep.toUpperCase() == reps[i].lastName.toUpperCase()){
      /* takes all the emails from table data in the crrent index and pushes them into array */
      emailArr.push(tableData[j].Emails.replace(/;/g, ",").replace(/:/g, ",").replace(/ +/g, "").replace(/,+/g, ",").split(','));
    }
  }
  /* check inside emailArr for subarrays of emails, pushing emails into new array */
  getAllRepEmails(emailArr);
  /* filters array of all emails for current rep to not include empty strings */
  let noEmptyEmails = _.filter(allRepEmails, el => el != "");
  /* loops over final array of all actual emails, creating objects for each rep with arrays of emails up to 1000 each per API req and pushing them into final array */
  while(noEmptyEmails.length){
      finalResult.push({
        owningrep: reps[i].lastName.toUpperCase(),
        /* converts final email array into JSON format as per API req */
        Emails: JSON.stringify(noEmptyEmails.splice(0,1000))
    });
  }
}
/* maps finalResults to create the promises that perform the query for each record */
let promises = finalResult.map((item) => {
  /* get lastName from repinfo for address variable */
  let name = _.filter(repInfo.value, obj => obj.lastName == item.owningrep)[0].lastName.toLowerCase();
  /* uses name variable and repinfo fromAddress to make address variable representing alias for the mail list we are adding members to */
  let address = _.filter(repInfo.value, obj => obj.lastName == item.owningrep)[0].fromAddress.replace(/^[^@]*/, name + "test");
        query.trigger({
        additionalScope: {
          members: finalResult[finalResult.indexOf(item)].Emails,
          alias: address
        }
      })
  }
);

return Promise.all(promises);

I'm tried using the different methods on Promise to see what happens, I've tried splicing Promises and resolving one. I think the only thing I've learned is that I don't understand Promises.

Does anyone have any ideas?

  • 1
    Should try to create a minimal reproduction of your code with the most essential parts that matters, right now it's quite verbose for anyone reading your code – Endless Jan 13 '23 at 15:56
  • 1
    The `map` callback you're using to create your `promises` array has no return value, so it effectively returns `undefined`. I'm guessing you meant to return the result of calling `query.trigger`? Does `query.trigger` return a promise? – T.J. Crowder Jan 13 '23 at 15:58
  • @T.J.Crowder yes, query.trigger returns a promise, ultimately making "promises" an array of promises. – Shane Dalton Gray Jan 13 '23 at 16:00
  • It's hard to say for sure from the question, but as you said, you *seem* to misunderstand a fundamental aspect of promises: They don't **do** anything. Their purpose is to provide a standardized way to observe the result of something that's **already happening**. More in my answer [here](https://stackoverflow.com/a/74796521/157247). So for instance, when you say *"when I try to resolve Promise.all(promises)"* that doesn't really make any sense. `Promise.all` doesn't do anything to the promises you pass into it. It doesn't cause something to happen. It just asks those promises to tell it... – T.J. Crowder Jan 13 '23 at 16:00
  • ...when they're fulfilled or rejected. [My blog post here](https://thenewtoys.dev/blog/2021/02/08/lets-talk-about-how-to-talk-about-promises/) may help a bit with terminology as well (people misuse "resolve" a **lot**, it's one of the most common misconceptions around promises -- other than the most common one that they cause something to happen). Hope that helps. :-) – T.J. Crowder Jan 13 '23 at 16:01
  • @ShaneDaltonGray - Okay, so at a minimum, you need to `return` the result of calling it, so `map` returns an array of promises instead of an array of `undefined`. – T.J. Crowder Jan 13 '23 at 16:02
  • Related: http://stackoverflow.com/questions/43082934/how-to-execute-promises-sequentially-passing-the-parameters-from-an-array Also, [my answer here](https://stackoverflow.com/a/43766002/157247) describes how to start operations that report their results via promises either in parallel (all running at once, provided that's something the process can do) or in series (one after another). – T.J. Crowder Jan 13 '23 at 16:04
  • @T.J.Crowder So when I do that, and run the code, I do get back more details in the return for some of the objects. I think I kinda get what you're saying about promises, which makes sense. But this does lead me to be abit more confused about how when I run the code, the calls happen... And how to ultimately make it so the calls don't happen all at once. – Shane Dalton Gray Jan 13 '23 at 16:10
  • `Array.map()` is synchronous and it invokes all your callbacks right away. the next callback dose not wait for the previous invocation to resolve. `Array.map` + `Promises` is also error prone for some ppl. you are essentually making a batch/parallel request – Endless Jan 13 '23 at 16:16
  • 1
    Methodology advice: decompose... (1) write a function that calls the mailgun async endpoint with hardcoded params and returns a promise, test/fix til it works, (2) change the hard-coded literals into params to the new function, test/fix (3) write another function that takes an array of hardcoded params and calls the first function, mapping, returning promises, then runs them with promise all, test/fix that (4) fully separate the logic that produces a well-formed set of params from the inputs, test that. Put it all together. – danh Jan 13 '23 at 16:30

1 Answers1

1

2 things:

  • your finalResult.map((item) => { don't seems to return any promise as TJ explained. I think you meant to do return query.trigger either way that map runs instantly (and in parallel) so the function you have written dosen't really wait for anything so it could be that other chained calls to your function is invoked immediately b/c Promise.all dose not really wait for anything.

The let promises = seems to be an array of undefined values? so again Promise.all(promises) dose nothing for you.

  • if you want to run one at the time, then remove finalResult.map((item) => and instead use something like a classic for loop and use async/await:
for (const item of finalResult) {
  await query.trigger(...)
}

your function is required to have the async keyword if you want to use await,
async function foo() { ... }

Endless
  • 34,080
  • 13
  • 108
  • 131
  • That seems to make more sense, however when switching to an async function and just using the for loop, I still seem to get the same results ultimately in the end, with only a few of the queries actually completing. So I guess I'm scrapping using promises. Should I make a whole new post, or would you happen to be able to recommend another way to loop over an array of objects, performing a query on each object? – Shane Dalton Gray Jan 13 '23 at 19:31