如何在嵌套循环中处理承诺

时间:2019-08-20 04:18:36

标签: javascript arrays node.js mongodb promise

我有一个这样的对象数组:

汇总的结果为我提供了具有以下相同结构的结果:

results = [
  {id: 1, test: biology, candidates:[ {cid: 11},{cid: 12},{cid: 13}]},
  {id: 2, test: chemistry, candidates:[ {cid: 15},{cid: 16},{cid: 17}]},
  {id: 3, test: physics, candidates:[ {cid: 1},{cid: 6},{cid: 7}]}
];

所以我需要在数组中循环,然后为每个候选项调用一个promise函数getTotalMarksPerCandidate(具有Promise.all并在进行一些计算后解析该变量)。但是,由于在数组中循环,不等待诺言完成,所以我得到了诺言...

问题:您知道如何解决这个问题,以便在循环我的候选数组时,它等待结果并继续吗?或任何周转解决方案?

所以我映射到results数组中,然后在候选人数组中执行了forEach,使用候选对象作为参数调用函数getTotalMarksPerCandidate,将结果推送到promise数组中。然后将解决所有收到的承诺。

var new_Result = results.map( function (subject) {
 let promises = [];
 if (subject.candidates && subject.candidates.length > 0) {
  subject.candidates.forEach( (element) => {
   promises.push(mobileUtil. getTotalMarksPerCandidate(element));
  });
 }
 return Promise.all(promises).then( result => {
  console.log('Check this out', result);
  subject.newCandidatelist = result;
  return subject;
 });
});
console.log('R: ', new_Result);
resolve(params);

因此,在return Promise.all(promises)回调函数中,我可以看到已解决了promise,而且我看到了getTotalMarksPerCandidate函数的返回值。如何将具有totalMarks的新候选对象设置为其各自的父对象,如

new_results = [
{id: 1, test: biology, candidates:[ {cid: 11, score: 88},{cid: 12, score: 90},
  {cid: 13, score: 91}]},
{id: 2, test: chemistry, candidates:[ {cid: 15, score: 91},{cid: 16, score: 91}, 
  {cid: 17, score: 91}]},
{id: 3, test: physics, candidates:[ {cid: 1, score: 91},{cid: 6, score: 91},
  {cid: 7, score: 91}]}
];

谢谢

3 个答案:

答案 0 :(得分:0)

仅基于嵌套Promise链的一种方法如下:

/*
Obtain array of promises via results.map() and resolve each sequentially
*/
const new_Result = Promise.all(results.map((subject) => {

    /* 
    Start a promise chain for this subject 
    */
    return Promise.resolve()
    .then(() => {

        /* 
        Get candidates from current subject, or just an empty array if no valid candidates data present 
        */
        const candidates = Array.isArray(subject.candidates) && subject.candidates.length > 0 ? subject.candidates : [];

        /*
        Map candidates (if any) to this Promise.all() 
        */
        return Promise.all(candidates.map((candidate) => {
            return mobileUtil.getTotalMarksPerCandidate(element).then(function(score) {
                /*
                Assign the score returned from getTotalMarksPerCandidate() for current candidate object
                */
                candidate.score = score;

                /*
                Return the updated candidate to mapped array of promise results
                */
                return candidate;
            })
        }));
    })
    .then((result) => {

        console.log('Check this out', result);
        subject.newCandidatelist = result;
        return subject;
    });
}));

这里的关键是这些行,它们基本上将getTotalMarksPerCandidate()的已解决承诺返回的得分值“附加”到当前要迭代的candidate上:

return mobileUtil.getTotalMarksPerCandidate(element).then(function(score) {

    candidate.score = score;

    return candidate;
})

希望有帮助!

答案 1 :(得分:0)

使用异步/等待,流可以为

(async () => {
    try {
        const new_Result = results.map(({ candidates, ...rest }) => {
            const updatedCandidates = candidates.map(async candidate => {
                const score = await getTotalMarksPerCandidate(candidate)
                return { score, ...candidate }
            })

            return  { candidates: updatedCandidates, ...rest }
        })

        console.log(new_Result)
    } catch(err) {
        // handle error
    }
})()

答案 2 :(得分:0)

感谢Dacre&Chridam&Bergi花时间回答此问题。因此,我尝试了两种解决方案,并对Dacre解决方案进行了一些调整,并且都奏效了,我在前端获得了所需的东西。

Params是我作为参数传递给函数的json对象。 mobileUtil.getTotalMarksPerCandidate是一个正在调用的函数,该函数返回一个对象,该对象最初带有作为参数发送的对象中带有修改的Marks。

Promise.all( results.map( jobtitle => {
                    return Promise.resolve()
                    .then(() => {

                        const candidates = Array.isArray(subject.candidates) && subject.candidates.length > 0 ? subject.candidates : [];
                        /*
                        Map candidates (if any) to this Promise.all() 
                        */
                       if (candidates && candidates.length > 0)
                       {
                            return Promise.all(candidates.map((candidate) => {
                                return mobileUtil. getTotalMarksPerCandidate(candidate).then( function(candidate) {
                                    /*
                                    Return the updated candidate to mapped array of promise results
                                    */
                                    return candidate;
                                })
                            }));
                       }
                    })
                    .then((result) => {
                        return subject;
                    });
                })
                ).then( res => {
                    params.listofnewCandidates = res;
                    resolve(params);
                });