简体   繁体   中英

Javascript Promises in a loop

I have a function called _problemFunction which takes an array myList as argument. For every item in myList I call a function _myFunction . _myFunction returns a promise. I want to return resolve from _problemFunction if _myFunction returns resolve with any item of my list. I want to return reject from _problemFunction if _myFunction returns reject for all the items in myList. Below is the code block of the scenario:

_problemFunction = (myList) => {
  return new promise((resolve, reject) => {
    myList.forEach(listItem => {
      _myFunction(listItem).then(pass => {
        //Resolve when _myFunction returns resolve with any listitem argument  
      }).catch(fail => {
        //reject only if _myfunction returns rejects for all the listitems
      })
    })
  })
}

Take a look at Promise.all() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

 let request = data.map((i) => {
    return new Promise((resolve, reject) => {
       if(i < 1) {
          reject('value to low')
       }
        resolve(i);
    }); });
    return  Promise.all(data) });
    _problemFunction = (myList) => {
  return new promise((resolve, reject) => {
    const errs = [];
    myList.forEach(listItem => {
      _myFunction(listItem)
      .then(resolve)
      .catch(fail => {
        errs.push(fail);
        errs.length === myList.length ? reject(errs) : '';
      })
    })
  })
}

As i understood, you want to reject only if all the promises reject, so you have to know if the rejects are the same amount of the array length to run the reject callback, something like this:

    return new promise((resolve, reject) => {
   let rejectCount = 0
   let resolved = false
   for(let listItem of myList){
      _myFunction(listItem).then(pass => {
        //Resolve when _myFunction returns resolve with any listitem argument
        if(!resolved){ 
         resolved = true
         resolve(pass) 
        }
      }).catch(fail => {
        //reject only if _myfunction returns rejects for all the listitems
        rejectCount++
        if(rejectCount === myList.length) {
          reject(fail)
        }
      })
      if(resolved) break
   }
})

If you have edge cases where your list is empty or undefined you can add this check before the for loop:

if(!myList || myList.length === 0) {
    reject(new Error('list is empty or undefined')
    return
}

It depends if you want to do all the items in the list in parallel or in series:

To resolve on the very first item that resolves and reject with all errors in series:

//serial resolve on first resolved item
_problemFunction = (myList,failed=[]) => {
  if(myList.length===0){
    return Promise.reject(failed);
  }
  return _myFunction(myList[0])
  .catch(
    //try it again with the next item
    err=>_problemFunction(myList.slice(1),failed.concat([err]))
  )
}

Try all at once and return whatever resolves first but reject with all the failed ones (unless your list is empty then it'll fail with an empty list)

//parallel resolve on first resolved item reject when all reject
_problemFunction = (myList,failed=[]) => {
  if(failed.length===0){
    myList = myList
    .map(([item,index])=>
      [
        index,
        _myFunction(item)
        .catch(err=>Promise.reject([err,index]))
      ]
    );
  }
  if(myList.length===0){
    return Promise.reject(failed);
  }
  return Promise.race(
    myList.map(([i,p])=>p)
  )
  .catch(
    ([err,index])=>//an error remove the failed item and try again
      _problemFunction(
        myList.filter(([i,p])=>i!==index),
        failed.concat([err])
      )
  )
}

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