J'ai un module de nœud simple qui se connecte à une base de données et a plusieurs fonctions pour recevoir des données, par exemple cette fonction:
dbConnection.js:
import mysql from 'mysql';
const connection = mysql.createConnection({
Host: 'localhost',
user: 'user',
password: 'password',
database: 'db'
});
export default {
getUsers(callback) {
connection.connect(() => {
connection.query('SELECT * FROM Users', (err, result) => {
if (!err){
callback(result);
}
});
});
}
};
Le module serait appelé ainsi à partir d'un module de nœud différent:
app.js:
import dbCon from './dbConnection.js';
dbCon.getUsers(console.log);
J'aimerais utiliser des promesses au lieu des rappels pour renvoyer les données . Jusqu'à présent, j'ai lu sur les promesses imbriquées dans le fil suivant: Écriture de code propre avec des promesses imbriquées , toute solution assez simple pour ce cas d'utilisation .. Quel serait le bon moyen de retourner result
en utilisant une promesse?
Promise
Je vous recommande de consulter les documents Promise de MDN qui constituent un bon point de départ pour utiliser Promises. Alternativement, je suis sûr qu'il existe de nombreux tutoriels disponibles en ligne. :)
Remarque: Les navigateurs modernes prennent déjà en charge la spécification ECMAScript 6 de Promises (voir la documentation MDN liée ci-dessus) et je suppose que vous souhaitez utiliser l'implémentation native, sans bibliothèques tierces.
En ce qui concerne un exemple réel ...
Le principe de base fonctionne comme ceci:
resolve
et reject
.Cela peut sembler beaucoup alors voici un exemple réel.
exports.getUsers = function getUsers () {
// Return the Promise right away, unless you really need to
// do something before you create a new Promise, but usually
// this can go into the function below
return new Promise((resolve, reject) => {
// reject and resolve are functions provided by the Promise
// implementation. Call only one of them.
// Do your logic here - you can do WTF you want.:)
connection.query('SELECT * FROM Users', (err, result) => {
// PS. Fail fast! Handle errors first, then move to the
// important stuff (that's a good practice at least)
if (err) {
// Reject the Promise with an error
return reject(err)
}
// Resolve (or fulfill) the promise with data
return resolve(result)
})
})
}
// Usage:
exports.getUsers() // Returns a Promise!
.then(users => {
// Do stuff with users
})
.catch(err => {
// handle errors
})
Dans Node.js 7.6, le compilateur JavaScript v8 a été mis à niveau avec async/wait support . Vous pouvez maintenant déclarer des fonctions comme étant async
, ce qui signifie qu'elles renvoient automatiquement une Promise
qui est résolue lorsque la fonction asynchrone termine son exécution. Dans cette fonction, vous pouvez utiliser le mot clé await
pour attendre la résolution d'une autre promesse.
Voici un exemple:
exports.getUsers = async function getUsers() {
// We are in an async function - this will return Promise
// no matter what.
// We can interact with other functions which return a
// Promise very easily:
const result = await connection.query('select * from users')
// Interacting with callback-based APIs is a bit more
// complicated but still very easy:
const result2 = await new Promise((resolve, reject) => {
connection.query('select * from users', (err, res) => {
return void err ? reject(err) : resolve(res)
})
})
// Returning a value will cause the promise to be resolved
// with that value
return result
}
Avec bluebird, vous pouvez utiliser Promise.promisifyAll
(et Promise.promisify
) pour ajouter des méthodes Promise ready à tout objet.
var Promise = require('bluebird');
// Somewhere around here, the following line is called
Promise.promisifyAll(connection);
exports.getUsersAsync = function () {
return connection.connectAsync()
.then(function () {
return connection.queryAsync('SELECT * FROM Users')
});
};
Et utilisez comme ceci:
getUsersAsync().then(console.log);
ou
// Spread because MySQL queries actually return two resulting arguments,
// which Bluebird resolves as an array.
getUsersAsync().spread(function(rows, fields) {
// Do whatever you want with either rows or fields.
});
Bluebird prend en charge de nombreuses fonctionnalités, parmi lesquelles les dispositifs de suppression, elle vous permet de disposer en toute sécurité d'une connexion après l'avoir terminée à l'aide de Promise.using
et Promise.prototype.disposer
. Voici un exemple de mon application:
function getConnection(Host, user, password, port) {
// connection was already promisified at this point
// The object literal syntax is ES6, it's the equivalent of
// {Host: Host, user: user, ... }
var connection = mysql.createConnection({Host, user, password, port});
return connection.connectAsync()
// connect callback doesn't have arguments. return connection.
.return(connection)
.disposer(function(connection, promise) {
//Disposer is used when Promise.using is finished.
connection.end();
});
}
Ensuite, utilisez-le comme ceci:
exports.getUsersAsync = function () {
return Promise.using(getConnection()).then(function (connection) {
return connection.queryAsync('SELECT * FROM Users')
});
};
Cela mettra automatiquement fin à la connexion une fois que la promesse aura résolu avec la valeur (ou rejetera avec une Error
).
Node.js version 8.0.0+:
Vous n'êtes plus obligé d'utiliser bluebird pour promisifier les méthodes de l'API de nœud. Parce qu'à partir de la version 8, vous pouvez utiliser natif util.promisify :
const util = require('util');
const connectAsync = util.promisify(connection.connectAsync);
const queryAsync = util.promisify(connection.queryAsync);
exports.getUsersAsync = function () {
return connectAsync()
.then(function () {
return queryAsync('SELECT * FROM Users')
});
};
Maintenant, n'utilisez aucune bibliothèque tierce pour faire la promisify.
En supposant que votre API d’adaptateur de base de données ne génère pas Promises
elle-même, vous pouvez effectuer les opérations suivantes:
exports.getUsers = function () {
var promise;
promise = new Promise();
connection.connect(function () {
connection.query('SELECT * FROM Users', function (err, result) {
if(!err){
promise.resolve(result);
} else {
promise.reject(err);
}
});
});
return promise.promise();
};
Si l'API de base de données prend en charge Promises
, vous pourriez faire quelque chose comme: (ici, vous voyez le pouvoir de Promises, votre fluff callback disparaît à peu près)
exports.getUsers = function () {
return connection.connect().then(function () {
return connection.query('SELECT * FROM Users');
});
};
Utiliser .then()
pour renvoyer une nouvelle promesse (imbriquée).
Appeler avec:
module.getUsers().done(function (result) { /* your code here */ });
J'ai utilisé une API de maquette pour mes promesses, votre API pourrait être différente. Si vous me montrez votre API, je peux l’adapter.
Lors de la création d'une promesse, vous prenez deux paramètres, resolve
et reject
. En cas de succès, appelez resolve
avec le résultat, en cas d'échec, appelez reject
avec l'erreur.
Ensuite, vous pouvez écrire:
getUsers().then(callback)
callback
sera appelé avec le résultat de la promesse renvoyée de getUsers
, c.-à-d. result
En utilisant la bibliothèque Q par exemple:
function getUsers(param){
var d = Q.defer();
connection.connect(function () {
connection.query('SELECT * FROM Users', function (err, result) {
if(!err){
d.resolve(result);
}
});
});
return d.promise;
}
Utilisez ce module natif const {promisify} = require('util');
pour convertir un ancien modèle de rappel en un modèle prometteur vous permettant d'obtenir un bénéfice de async/await
code
const {promisify} = require('util');
const glob = promisify(require('glob'));
app.get('/', async function (req, res) {
var glob = promisify(require('glob'));
const files = await glob('src/**/*-spec.js');
res.render('mocha-template-test', {files});
});
Le code ci-dessous ne fonctionne que pour le noeud -v> 8.x
J'utilise ce Middleware MySQL promisifié pour Node.js
lire cet article Créer un middleware de base de données MySQL avec Node.js 8 et Async/Await
database.js
var mysql = require('mysql');
// node -v must > 8.x
var util = require('util');
// !!!!! for node version < 8.x only !!!!!
// npm install util.promisify
//require('util.promisify').shim();
// -v < 8.x has problem with async await so upgrade -v to v9.6.1 for this to work.
// connection pool https://github.com/mysqljs/mysql [1]
var pool = mysql.createPool({
connectionLimit : process.env.mysql_connection_pool_Limit, // default:10
Host : process.env.mysql_Host,
user : process.env.mysql_user,
password : process.env.mysql_password,
database : process.env.mysql_database
})
// Ping database to check for common exception errors.
pool.getConnection((err, connection) => {
if (err) {
if (err.code === 'PROTOCOL_CONNECTION_LOST') {
console.error('Database connection was closed.')
}
if (err.code === 'ER_CON_COUNT_ERROR') {
console.error('Database has too many connections.')
}
if (err.code === 'ECONNREFUSED') {
console.error('Database connection was refused.')
}
}
if (connection) connection.release()
return
})
// Promisify for Node.js async/await.
pool.query = util.promisify(pool.query)
module.exports = pool
Vous devez mettre à niveau le noeud -v> 8.x
vous devez utiliser la fonction asynchrone pour pouvoir utiliser wait.
exemple:
var pool = require('./database')
// node -v must > 8.x, --> async / await
router.get('/:template', async function(req, res, next)
{
...
try {
var _sql_rest_url = 'SELECT * FROM arcgis_viewer.rest_url WHERE id='+ _url_id;
var rows = await pool.query(_sql_rest_url)
_url = rows[0].rest_url // first record, property name is 'rest_url'
if (_center_lat == null) {_center_lat = rows[0].center_lat }
if (_center_long == null) {_center_long= rows[0].center_long }
if (_center_zoom == null) {_center_zoom= rows[0].center_zoom }
_place = rows[0].place
} catch(err) {
throw new Error(err)
}