简体   繁体   中英

Conditionally chaining promises inside and outside of a loop in javascript

In the following code, when the condition within the loop is satisfied we can't really be sure in which order are both asyncFunction called.

siblings.forEach(function(sibling, index) {
  // This condition may only be satisfied once (only one sibling may have data-selected true)
  if (sibling.getAttribute('data-selected') == 'true') { 
        asyncFunction(sibling);     
  }
})

asyncFunction(element);

Since I know asyncFunction() returns a promise I could chain those to ensure the order:

asyncFunction(sibling).then(asyncFunction(element));

But how to do this taking the condition into account?

I considered wrapping the loop in a promise that resolves when the condition is satisfied or after the loop ends, but it seems a tad convoluted.

// Untested
function checkSiblings(siblings) {
    let promise = new Promise(function(resolve, reject) {
        siblings.forEach(function(sibling, index) {
          if (sibling.getAttribute('data-selected') == 'true') { 
                resolve(asyncFunction(sibling));    
          }
        })
        resolve();
    })
}

checkSiblings(siblings).then(asyncFunction(element));

I am sure this has been addressed before, but I can't find the right search keyboards.

Thank you

If only one of the siblings can meet the criteria, using find will simplify your code:

const selected = [...siblings].find( elem => elem.getAttribute('data-selected') == 'true' );

Mark checkSiblings as async so it always returns a Promise, and have it return the asyncFunction Promise when appropriate:

return selected ? asyncFunction(selected) : null;

Then you can chain them as before:

checkSiblings(siblings).then(() => asyncFunction(element));

All together you'd have something like this:

async function checkSiblings (siblings) {
  const selected = [...siblings].find( elem => elem.getAttribute('data-selected') == 'true' );
  return selected ? asyncFunction(selected) : null
}

checkSiblings(siblings).then(() => asyncFunction(element));

There are several ways. One of them is using Array.reduce to resolve promises sequentially :

[...siblings].reduce(
  async (prev, sibling)) => {

    // Await initial/previous promise (discard results)
    await prev;

    // This condition may only be satisfied once
    // (only one sibling may have data-selected true)
    if (sibling.getAttribute('data-selected') == 'true') { 
      // Return the async function promise
      return asyncFunction(sibling);
    }

    // async functions always returns a promise.
    // No need to return anything

   // Initial promise autoresolved
}, Promise.resolve())

// Once all have been executed
.then( () => asyncFunction(element));

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