2017-12-02 3 views
-1

Ich versuche, async/await zu bekommen, um Ereignisse in der Reihenfolge auszulösen, aber es scheint, dass ich etwas vermisse, da meine console.log-Markierungen rückwärts in der Reihenfolge auslösen, auf die ich gehofft hatte.Async/erwarten in Knoten geschachtelte Funktionen?

Ich frage mich, ob ist mit meiner Verwendung von geschachtelten Funktionen in users.js zu tun, aber mehrere Varianten von async/await versucht hat, funktioniert es nicht wie erwartet.

// index.js

var users = require("./users.js"); 

app.post("/getToken", async function(req, res) { 
    if (req.body.email && req.body.password) { 
     const email = req.body.email; 
     const password = req.body.password; 
     const user = await users(email, password) 
     // running this should output console.log("No 1") 
     // from users.js first, but doesn't ? 

     console.log('No 2') 
     if (user) { 
      var payload = { 
       id: user.id 
      }; 
      var token = jwt.encode(payload, cfg.jwtSecret); 
      res.json({ 
       token: token 
      }); 
     } else { 
      res.sendStatus(401); 
     } 
    } else { 
     res.sendStatus(401); 
    } 
}); 

// users.js

module.exports = function(emailAddress, password) { 
    db.connect(); 
    var query = ` 
     SELECT 
      id, 
      email, 
      password, 
      salt 
     FROM 
      users 
     WHERE 
      email = ?`; 
    var query_params = [emailAddress]; 

    db.query(
     query, 
     query_params, 
     function(error, result, fields) { 
      console.log('No 1') 

      if (error) throw error; 
      if (result.length == 1) { 
       if (checkPass(password, result[0].password, result[0].salt)) { 
        return { id: result[0].id } 

       } else { 
        console.log("login False | Password"); 
        return false; 
       } 

      } else { 
       console.log("login False | username"); 
       return false; 
      } 
     } 
    ) 
} 
+0

Erklären Sie Ihre downvote, wenn Sie bitte können? Ich bin glücklich, die Frage bei Bedarf hinzuzufügen – denden

Antwort

3

Ihre users.js Funktion nichts zurückgibt. Die Rückrufe, die Sie query übergeben, tun, aber die Gesamtfunktion nicht. Da es nie etwas explizit zurückgibt, ist das Ergebnis des Aufrufs undefined. Wenn Sie awaitundefined sind, ist es wie await Promise.resolve(undefined) und so wird Ihr Lösungshandler ziemlich schnell aufgerufen.

Sie möchten, dass diese Funktion ein Versprechen zurückgibt, das erst gelöst wird, wenn die Arbeit erledigt ist. Da es eine alte Node-Callbck-API verwendet, ist es vernünftig, new Promise zu verwenden, um dieses Versprechen zu erstellen (alternativ dazu, eine Promise-fähige API für diese DB zu erhalten oder zu erstellen).

Ich vermute auch, dass Sie connect falsch aufrufen, da dies normalerweise eine asynchrone Aktion wäre, aber Sie behandeln es als ob es synchron wäre.

Siehe Anmerkungen:

users.js

module.exports = function(emailAddress, password) { 
    return new Promise((resolve, reject) => { 
     // Use the callback to know when the connection is established 
     db.connect(error => { 
      if (error) { 
       // Connection failed 
       reject(error); 
       return; 
      } 
      var query = ` 
       SELECT 
        id, 
        email, 
        password, 
        salt 
       FROM 
        users 
       WHERE 
        email = ?`; 
      var query_params = [emailAddress]; 
      db.query(
       query, 
       query_params, 
       function(error, result, fields) { 
        // Throwing an error here does nothing useful. Instead, 
        // reject the promise. 
        if (error) { 
         reject(error); 
         return; 
        } 
        // Resolve our promise based on what we got 
        if (result.length == 1) { 
         if (checkPass(password, result[0].password, result[0].salt)) { 
          resolve({ id: result[0].id }); 
         } else { 
          console.log("login False | Password"); 
          resolve(false); 
         } 
        } else { 
         console.log("login False | username"); 
         resolve(false); 
        } 
       } 
      ); 
     }); 
    }); 
} 

mit es dann:

app.post("/getToken", async function(req, res) { 
    // You must handle errors, since `post` won't do anything with the return 
    // value of this function 
    try { 
     if (req.body.email && req.body.password) { 
      const email = req.body.email; 
      const password = req.body.password; 
      // Now this waits here, since `users` returns a promise that 
      // isn't resolved until the query completes 
      const user = await users(email, password) 
      console.log('No 2') 
      if (user) { 
       var payload = { 
        id: user.id 
       }; 
       var token = jwt.encode(payload, cfg.jwtSecret); 
       res.json({ 
        token: token 
       }); 
      } else { 
       res.sendStatus(401); 
      } 
     } else { 
      res.sendStatus(401); 
     } 
    } catch (e) { 
     res.sendStatus(401); 
    } 
}); 
+0

Hi T.J. Diese Option scheint nur bei der ersten Anfrage, nicht aber bei der zweiten zu funktionieren. basierend auf der Fehlermeldung scheint es, dass einer der Versprechungen, die sich auf die DB-Verbindung beziehen, sich nicht selbst auflöst. '/ * Dies ist von db.connect (Fehler => {...}) Fehler */ (node: 29523) UnhandledPromiseRejectionWarning: Unbehandelte Versprechen Ablehnung (Ablehnung ID: 2): Fehler: Kann Handshake nach dem Aufrufen von beenden nicht in die Warteschlange stellen. (node: 29523) [DEP0018] DeprecationWarning: Nicht behandelte Versprechensverweigerungen sind veraltet. In der Zukunft werden Versprechensverweigerungen, die nicht bearbeitet werden, den Node.js-Prozess mit einem Beendigungscode ungleich null beenden. – denden

+0

@denden: Es gibt keine unbehandelten Versprechen im Code in der Antwort, also überprüfe das du wirklich wendete es richtig an. Aber die Tatsache, dass die Ablehnung nicht behandelt wird, ist getrennt von dem anderen Problem, das offensichtlich ist, dass Sie die DB nicht richtig benutzen. Das ist für diese Frage nicht möglich, aber ich würde mich nicht wundern, wenn Sie sich paarweise verbinden oder trennen müssen. –

+0

ok danke, das sehe ich mir so an. – denden

3

Das Problem ist, dass db.query Funktion asynchron ist - Sie Callback-Funktion zur Verfügung stellen, die ausgeführt wird, wenn Datenbank-Aufruf beendet ist . Sie müssen wahrscheinlich diese ganze Funktion in Versprechen wickeln:

So sollten Sie zusätzlich machen connect synchron:

module.exports = function(emailAddress, password) { 
    return new Promise(function(resolve, reject) { 
    db.connect(); 
     var query = ` 
      SELECT 
       id, 
       email, 
       password, 
       salt 
      FROM 
       users 
      WHERE 
       email = ?`; 
     var query_params = [emailAddress]; 

     db.query(
      query, 
      query_params, 
      function(error, result, fields) { 

       if (error) return reject(error) 

       if (result.length == 1) { 
        if (checkPass(password, result[0].password, result[0].salt)) { 
         resolve({id: result[0].id}) 

        } else { 
         console.log("login False | Password"); 
         reject(); 
        } 

       } else { 
        console.log("login False | username"); 
        reject(); 
       } 
      } 
    ) 
    }) 
} 

Sie mehr über Versprechen API here

EDIT lernen können. Hier ist ein Codeabschnitt, den ich für Sie überarbeitet habe. Es sollte gut funktionieren. Ich habe einige ES6-Elemente verwendet, um es lesbarer zu machen.

const connect =() => new Promise((resolve, reject) => { 
    db.connect((err) => { 
     if (err) return reject(err); 

     resolve(); 
    }) 
    }) 

const makeDbRequest = (emailAddress, password) => new Promise((resolve, reject) => { 
    const query = ` 
     SELECT 
      id, 
      email, 
      password, 
      salt 
     FROM 
      users 
     WHERE 
      email = ?`; 

    const query_params = [emailAddress]; 

    db.query(
     query, 
     query_params, 
     handleDbData(resolve, reject, password), 
    ); 
    }) 

const handleDbData = (resolve, reject, password) => (error, result, fields) => { 
    if (error) return reject(error) 

    if (result.length == 1) { 
     if (checkPass(password, result[0].password, result[0].salt)) { 
      resolve({id: result[0].id}) 

     } else { 
      console.log("login False | Password"); 
      reject(); 
     } 

    } else { 
     console.log("login False | username"); 
     reject(); 
    } 
} 

module.exports = (emailAddress, password) => new Promise((resolve, reject) => { 
    connect() 
     .then(() => { 
     makeDbRequest(emailAddress, password) 
      .then(resolve) 
      .catch(reject) 
     }) 
     .catch(reject); 
    }) 
+0

Es ist wahr, dass das OP ein Versprechen in dieser Funktion verwenden muss, aber es gibt eine Menge anderer Dinge, die auch reparieren müssen. (Nicht zuletzt ist "db.connect" vermutlich auch asynchron, ganz zu schweigen von Problemen beim Aufruf dieser Funktion von 'users.js'.) –

+0

Wahrscheinlich ja. Aber das liegt außerhalb des Geltungsbereichs dieser Frage. Die Sache ist, dass er async/await auf synchrone Funktion verwendet, die asynchrone Funktionen aufruft. –

+0

Ich kann nicht zustimmen, dass die Adressierung des gesamten Problems außerhalb des Geltungsbereichs liegt. –

Verwandte Themen