Function work incorrectly when using promise in no

2019-07-23 17:21发布

问题:

I have routes like that:

router.get('/:projectid/, (req, res) => {
    testCase.getTestCaseDetail(req.params.projectid, req.params.testcaseid, req.params.snapshotId).then(testcaseData => {
      res.render('testCaseService', {
        title: 'Page',
        testcase: testcaseData,
        layout: 'project_layout',
      });
    });
  });

In the handler function, I have getTestCaseDetail function:

function getTestCaseDetail(projectId, id, snapshotId) {
  let testCaseId = parseInt(id);
  return new Promise(((resolve, reject) => {
    return testCaseSchema.aggregate([
      { $match: { 'projectId': projectId, 'testCaseId': testCaseId } },
      {
        $lookup: {
          from: snapshotInfoSchema.collection.collectionName,
          localField: testCaseObj.SERVICE_ID,
          foreignField: 'artifacts.id',
          as: 'services',
        },
      },
      { $unwind: '$services' },
      {
        $match: {
          'services.snapshot.id': snapshotId,
        }
      }
    ]).then(testCaseResult => {
      resolve(addTestCasesV2(testCaseResult, snapshotId));
    })
      .catch(err => {
        reject(err);
      })
  }));
}

and addTestCasesV2 function

const addTestCasesV2 = function (testcases, snapshotId) {
  const result = [];
  let serviceTypeMapping;
  let serviceName;
  let testCase = {
    id: '',
    testCaseId: '',
    name: '',
    serviceName: '',
    serviceType: '',
    modifiedAt: '',
    testScripts: '',
    snapshotId: '',
    services: '',
    inputs: [],
    outputs: [],
  };
  let promiseInputResults, promiseOutputResults;
  const testcasesList = lodash.map(testcases, (tc) => {
    const artifacts = lodash.map(tc.services.artifacts, (art) => {
      if (art.id === tc.service_id) {
        serviceTypeMapping = art.processType.serviceTypeName;
        serviceName = art.name;
        if (!commonUtil.isUndefined(art.processParameters)) {
          if (!commonUtil.isUndefined(art.processParameters.input)) {
            promiseInputResults = lodash.map(art.processParameters.input, (ip) => {
              let classId = commonUtil.getArtifactId(ip.classId);
              return objectType.getObjectTypeByClassId(snapshotId, classId)
            });
          }

          if (!commonUtil.isUndefined(art.processParameters.output)) {
            promiseOutputResults = lodash.map(art.processParameters.output, (ip) => {
              let classId = commonUtil.getArtifactId(ip.classId);
              return objectType.getObjectTypeByClassId(snapshotId, classId)
            });
          }
        }
        testCase.id = tc.testCaseId;
        testCase.testCaseId = tc.testCaseId;
        testCase.name = tc.name;
        testCase.serviceName = serviceName;
        testCase.serviceType = serviceTypeMapping;
        testCase.modifiedAt = tc.modifiedAt;
        testCase.testScripts = tc.testScripts;
        testCase.snapshotId = snapshotId;
        testCase.services = tc.services;

        Promise.all(promiseInputResults).then(inputItems => {
          return testCase.inputs = inputItems;
        });

        Promise.all(promiseOutputResults).then(outputItems => {
          return testCase.outputs = outputItems;
        });

      }
    });
  });
  return testCase;
};

The inputs/outputs is an list of item, like that: inputs:[ { name: "test1", type: "String" }, { name: "test2", type: "number" }, ]

I have a problem with promise lifecycle, this is the current flow 1. Routes 2. function getTestCaseDetail 3. resolve(addTestCasesV2(testCaseResult, snapshotId)); 4. addTestCasesV2 ==> return testCase but without go to 2 promise.all functions 5. resolve(addTestCasesV2(testCaseResult, snapshotId)); 6. Routes 7. go back 2 promise.all functions 8. end at return testCase.outputs = outputItems;

Please see the image to more detail flow (the white number is current flow, the orange number is my expect flow)

Please advice me. Many thanks.

回答1:

Your code doesn't seem correct. If testcases is an array with more than one item, your lodash.map callback will be called testcases.length time. Each time overwriting testCase.id assigned in previous callback.

Anyways, I have corrected bits of your code to make it in run order that you wanted. I have logged ==step== at various places for your help.

First Function:

function getTestCaseDetail(projectId, id, snapshotId) {
    let testCaseId = parseInt(id);
    return new Promise(((resolve, reject) => {
        return testCaseSchema.aggregate([
            { $match: { 'projectId': projectId, 'testCaseId': testCaseId } },
            {
                $lookup: {
                    from: snapshotInfoSchema.collection.collectionName,
                    localField: testCaseObj.SERVICE_ID,
                    foreignField: 'artifacts.id',
                    as: 'services',
                },
            },
            { $unwind: '$services' },
            {
                $match: {
                    'services.snapshot.id': snapshotId,
                }
            }
        ]).then(testCaseResult => {
            console.log('=======STEP 1=======');
            resolve(addTestCasesV2(testCaseResult, snapshotId));//=======STEP 2=======
            console.log('=======STEP 5=======')

        })
            .catch(err => {
                reject(err);
            })
    }));
}

Second function

const addTestCasesV2 = function (testcases, snapshotId) {
    console.log('=======STEP 2=======')
    const result = [];
    let serviceTypeMapping;
    let serviceName;
    let testCase = {
        id: '',
        testCaseId: '',
        name: '',
        serviceName: '',
        serviceType: '',
        modifiedAt: '',
        testScripts: '',
        snapshotId: '',
        services: '',
        inputs: [],
        outputs: [],
    };
    let promiseInputResults, promiseOutputResults;

    return Promise.resolve()
        .then(()=>{
            console.log('=======STEP 3=======');
            const testcasesList = lodash.map(testcases, (tc) => {
                const artifacts = lodash.map(tc.services.artifacts, (art) => {
                    if (art.id === tc.service_id) {
                        serviceTypeMapping = art.processType.serviceTypeName;
                        serviceName = art.name;
                        if (!commonUtil.isUndefined(art.processParameters)) {
                            if (!commonUtil.isUndefined(art.processParameters.input)) {
                                promiseInputResults = lodash.map(art.processParameters.input, (ip) => {
                                    let classId = commonUtil.getArtifactId(ip.classId);
                                    return objectType.getObjectTypeByClassId(snapshotId, classId)
                                });
                            }

                            if (!commonUtil.isUndefined(art.processParameters.output)) {
                                promiseOutputResults = lodash.map(art.processParameters.output, (ip) => {
                                    let classId = commonUtil.getArtifactId(ip.classId);
                                    return objectType.getObjectTypeByClassId(snapshotId, classId)
                                });
                            }
                        }
                        testCase.id = tc.testCaseId;
                        testCase.testCaseId = tc.testCaseId;
                        testCase.name = tc.name;
                        testCase.serviceName = serviceName;
                        testCase.serviceType = serviceTypeMapping;
                        testCase.modifiedAt = tc.modifiedAt;
                        testCase.testScripts = tc.testScripts;
                        testCase.snapshotId = snapshotId;
                        testCase.services = tc.services;


                        /*=======FOLLOWING IS NOT REQUIRED=======*/
                        // Promise.all([promiseOutputResults]).then(outputItems => {
                        //     return testCase.outputs = outputItems;
                        // });

                    }
                });
            });
            return Promise.all([promiseInputResults,promiseOutputResults]);
        })
        .then(inputItems => {//array of resolved values
            console.log('=======STEP 4=======');
            testCase.inputs = inputItems[0];
            testCase.outputs = inputItems[1];
            return testCase;
        })
};

Now you can use following to extract testcase from first function:

getTestCaseDetail(myProjectId, id, mySnapshotId)
    .then(testCase=>console.log(testCase))

JSfiddle for your understanding.