Search code examples
javascriptnode.jsmongodbmongooseasync-await

Assigning the length of the result of the query from the await operation to the variable encounters the following problem


I use the mongoose operation in Nodejs to query the number of eligible data and assign it to an object property:

...
const ability_count = {
    api_count: 0,
    func_count: 0,
    event_count: 0
};
...
ability_count.api_count = await APIService.findByList({cid: comp_id}).length;
ability_count.func_count = await FuncService.findByList({cid: comp_id}).length;
ability_count.event_count = await EventService.findByList({cid: comp_id}).length;
...
const result = {ability_count: ability_count}; rm.successMsg();
rm.setResult(result);
res.send(rm) 

The result is then returned to the Web, but the ablity_count object that Web receives is missing any property field like this console.log:

result: {
    ability_count: {}
}

What causes this?

I know that the await expression is followed by a promise instance, so I can't use.lenth directly, but I still don't know why ability_count doesn't have any properties. Even so I think it should print:

result: {
    ability_count: {
        api_count: undefined,
        func_count: undefined,
        event_count: undefined
    }
}

Solution

  • If you would look at the object before it is returned to the client, you'd see that indeed it looks like you expected:

    {
        ability_count: {
            api_count: undefined,
            func_count: undefined,
            event_count: undefined
        }
    }
    

    But as the object is to be sent back as an HTTP response, it is serialized to JSON, and at that moment the undefined properties are lost (there is no undefined in JSON). Here is a little illustration of that effect:

    let result = {
        ability_count: {
            api_count: undefined,
            func_count: undefined,
            event_count: undefined
        }
    };
    
    console.log("Original:");
    console.log(result);
    // Now the object is serialized for the HTTP response:
    json = JSON.stringify(result);
    console.log("serialized:");
    console.log(json);

    As to the original problem of the findByList promises: the length property access is made on the promise, and then the await operator acts on that length property, not on the promise. It should be the other way round: first you want to await the promise, and only when it is resolved, you want to access the length property of the value given by await. So use parentheses to force the await to be executed first:

    ability_count.api_count = (await APIService.findByList({cid: comp_id})).length;