0
votes

Node JS Promise puis blocage exécuté avant la résolution de la promesse

Chemin de code simple, mieux expliqué avec des puces

  1. Appel API
  2. Appel à la base de données
  3. Le résultat est une liste d'objets
  4. À l'intérieur du bloc then, effectuez un appel à la base de données pour chaque objet afin d'hydrater les enfants
  5. À l'intérieur d'un autre bloc puis res.send (objet hydraté)

Problème

L'étape 5 se produit avant la fin de l'étape 4 (Code ci-dessous)

//api endpoint
router.post('/get-data', getObjects);

export const getObjects: express.RequestHandler = (req, res) => {
    queryContainer(containerId, querySpec)
    .then((result) => {
        return getChildren(result, req.body.criteria);
    })
    .then((result) => {
        res.send(result);
    });
}

export async function queryContainer(containerId, querySpec) {
    const { result: results } = await client.database(databaseId).container(containerId).items.query(querySpec, {enableCrossPartitionQuery: true}).toArray()
    .catch( (error) => {
        console.log("Error! ", error);
    });
    return results;
}

function getChildren(result: any, criteria: any) {
    if(criteria) {
        result.children = [];
        var actions = result
                        .map(result => result.element)
                        .map(dbCallToGetChildren);
        var results = Promise.all(actions);
        results.then(children => {
            result.children.push(...children)
            return result;
        });
    } 

    return result;
}

export const dbCallToGetChildren = (async function (username) {
    const querySpec = {
        query: "SELECT * FROM root r WHERE r.userName=@userName",
        parameters: [
            {name: "@userName", value: username}
        ]
    };
    queryContainer(containerId, querySpec)
    .then((results) => { 
        return results;
    })
    .catch((error) => {
        console.log("Error " + error);
        return Promise.resolve;
    });
});


0 commentaires

3 Réponses :


0
votes

L'étape 5 se produit avant la fin de l'étape 4 (fonction getChildren ) car getChildren ne renvoie pas de promesse. Le modifier comme suit peut résoudre le problème:

function getChildren(result: any, criteria: any) {
  return new Promise(resolve => {
    if(criteria) {
        result.children = [];
        var actions = result
                        .map(result => result.element)
                        .map(dbCallToGetChildren);
        var results = Promise.all(actions);
        results.then(children => {
            result.children.push(...children)
            resolve(result);
        });
    } else {
      resolve(result);
    }
  });
}

Dans results.then (children => {...} , il y a maintenant resolution (result ); pour garantir que l'instruction return getChildren (result, req.body.criteria); dans l'appel queryContainer ne se termine pas tant que la promesse n'est pas résolue.


0 commentaires

0
votes

Pour que l'étape 4 se termine complètement avant que l'étape 5 ne soit exécutée, nous devons promettre chaque chemin de code qui passe par getChildren .

Cela signifie que nous devons changer ceci: p >

function getChildren(result: any, criteria: any) {
    if(criteria) {
        result.children = [];
        var actions = result
                        .map(result => result.element)
                        .map(dbCallToGetChildren);
        var results = Promise.all(actions);
        return results.then(children => { // returns a promise 
            result.children.push(...children)
            return result;
        });
    } 
    return Promise.resolve(result); // returns a promise
}

Dans ce qui suit (faites attention aux commentaires du code):

function getChildren(result: any, criteria: any) {
    if(criteria) {
        result.children = [];
        var actions = result
                        .map(result => result.element)
                        .map(dbCallToGetChildren);
        var results = Promise.all(actions);
        results.then(children => {
            result.children.push(...children)
            return result;
        });
    } 
    return result;
}

Il est important de renvoyer sinon, le code de cette ligne est exécuté de manière asynchrone (ce qui ne garantit pas que l'étape 4 se terminera avant le début de l'étape 5).


0 commentaires

0
votes

J'ai quelques commentaires sur votre code:

  1. vous faites une mutation, ce qui est une mauvaise pratique, la fonction getChildren accepte le type de résultat any, puis elle y apporte des modifications comme

result.children = []

  1. Évitez d'en utiliser et essayez de définir un type

  2. dans votre code parce que vous faites une mutation donc vous n'avez même pas besoin de renvoyer le résultat car l'objet d'origine est déjà modifié mais comme je l'ai mentionné plus tôt, vous devez éviter la mutation.

  3. Le principal problème que l'étape 5 exécute avant l'étape 4 est que getChildren ne retournera pas sa promesse, j'ai apporté quelques modifications à votre code pour tenir compte de la promesse.

      function getChildren(result: any, criteria: any): Promise<any> {
      return new Promise((resolve, reject) => {
        if (criteria) {
          result.children = []
          const actions = result
            .map(item => item.element)
            .map(dbCallToGetChildren)
          Promise.all(actions).then(children => { // returns a promise
            result.children.push(...children)
            return resolve("successfully is processed!")
          })
        }
        reject("invalid criteria!")
      })
    }


3 commentaires

L'ajout de la promesse a résolu le problème. Excellente solution, Mehran.


Dans les mots célèbres de @Bergi (qui a dû le dire 1000 fois), évitez l'anti-modèle de construction de promesse explicite .


@ Roamer-1888 Je suis d'accord avec vous, si je voulais écrire le code, je retournerais toute la promesse au lieu de créer une nouvelle promesse, mais je n'avais pas l'image complète du code et j'ai juste essayé de répondre au question dans un contexte très fermé.