Search code examples
javascriptnode.jsknex.js

problems with Promises (nodejs, bluebird): process exits wihtout waiting for childs to be finished


I am new to Promises and I am definitely writing incorrect code (I do not yet understand how Promises work).

From the code below I expect that process.exit(0); would be executed only when everyting else would be finished.

But in the code below "console.log(step 2)" is never executed (seen in console), because I think process.exit(0) is run before waiting for other processes. Any suggestoins how to restructure my code?

var wordsToProcess = ['e301', 'e260']; // array with many words (could be mora than 2)
var actions = wordsToProcess.map((str) => {
    console.log('(1) step ');
    ingredient_get(str) 
        .then((ingredient_id) => {            
            console.log('(2) step '); //!!!! never console.log
            return ingredient_id;     // never return ingredient_id
        });
});
var results = Promise.all(actions);
results.then(ingredient_id => {    
    process.exit(0); // should somehow wait for all ingredient_ids to come
});

function ingredient_get(name) {
    var q_ingredients = knex('ingredients')
        .select('id').where('name', name)
        .then(pick_id)

    var q_synonyms = knex('synonyms')
        .select('ingredient_id as id').where('name', name)
        .then(pick_id)

    return Promise.all([q_ingredients, q_synonyms])
        .then(([id1, id2]) => {                
            return id1 || id2; // only one id would be true here
        })
}

Solution

  • This is just a tiny mistake, nearly a typo: You're missing a return in your map callback:

    var actions = wordsToProcess.map((str) => {
        console.log('(1) step ');
        return ingredient_get(str)
    //  ^^^^^^------------------------------------ add this
            .then((ingredient_id) => {            
                console.log('(2) step ');
                return ingredient_id;
            });
    });
    

    Consequently, actions is just filled with undefined instead of refeernces to promises, so there's nothing for Promise.all's promise to wait for.

    If you remove the console.log (or abuse the comma operator, but...don't), you can use a concise arrow instead:

    var actions = wordsToProcess.map((str) =>
        ingredient_get(str)
            .then((ingredient_id) => {            
                console.log('(2) step ');
                return ingredient_id;
            })
    );
    

    (In fact, I wouldn't be surprised if you started out with a concise arrow, then went back and added the logging line and converted it to a verbose arrow but forgot to add the return. It's a common error.)