У меня есть простой модуль узла, который подключается к базе данных и имеет несколько функций для получения данных, например эту функцию:
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);
}
});
});
}
};
Модуль будет вызываться таким образом из другого модуля узла:
app.js:
import dbCon from './dbConnection.js';
dbCon.getUsers(console.log);
Я хотел бы использовать обещания вместо обратных вызовов, чтобы возвращать данные. До сих пор я читал о вложенных обещаниях в следующем потоке: Написание чистого кода с вложенными обещаниями , но я не смог найти решения, которое было бы достаточно простым для этого варианта использования. Как правильно вернуться result
с помощью обещания?
Ответы:
Использование
Promise
классаЯ рекомендую взглянуть на документы MDN Promise, которые предлагают хорошую отправную точку для использования Promises. В качестве альтернативы, я уверен, что в Интернете есть много обучающих программ. :)
Примечание. Современные браузеры уже поддерживают спецификацию обещаний ECMAScript 6 (см. Приведенные выше документы MDN), и я предполагаю, что вы хотите использовать собственную реализацию без сторонних библиотек.
Что касается реального примера ...
Основной принцип работает так:
resolve
иreject
Это может показаться большим, так что вот реальный пример.
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 })
Использование функции языка async / await (Node.js> = 7.6)
В Node.js 7.6 компилятор JavaScript v8 был обновлен с поддержкой async / await . Теперь вы можете объявлять функции как существующие
async
, что означает, что они автоматически возвращают,Promise
который разрешается, когда функция async завершает выполнение. Внутри этой функции вы можете использоватьawait
ключевое слово, чтобы дождаться разрешения другого обещания.Вот пример:
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 }
источник
С bluebird вы можете использовать
Promise.promisifyAll
(иPromise.promisify
) для добавления методов Promise к любому объекту.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') }); };
И используйте вот так:
getUsersAsync().then(console.log);
или
// 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 поддерживает множество функций, одна из них - утилиты удаления, она позволяет безопасно удалить соединение после его завершения с помощью
Promise.using
иPromise.prototype.disposer
. Вот пример из моего приложения: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(); }); }
Тогда используйте это так:
exports.getUsersAsync = function () { return Promise.using(getConnection()).then(function (connection) { return connection.queryAsync('SELECT * FROM Users') }); };
Это автоматически завершит соединение, как только обещание будет разрешено со значением (или отклонено с помощью
Error
).источник
try-catch
при каждом звонке. Так что если вы делаете это довольно часто и сложность вашего кода аналогична образцу, вам следует пересмотреть это.Node.js версии 8.0.0+:
Вы не должны использовать Блюберд больше promisify методы узла API. Потому что с версии 8+ вы можете использовать собственный 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') }); };
Теперь не нужно использовать какие-либо сторонние библиотеки для выполнения обещания.
источник
Предполагая, что API адаптера базы данных не выводит
Promises
себя, вы можете сделать что-то вроде: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(); };
Если API базы данных поддерживает,
Promises
вы можете сделать что-то вроде: (здесь вы видите мощь обещаний, ваш бред обратного вызова в значительной степени исчезает)exports.getUsers = function () { return connection.connect().then(function () { return connection.query('SELECT * FROM Users'); }); };
Используется
.then()
для возврата нового (вложенного) обещания.Звоните с:
module.getUsers().done(function (result) { /* your code here */ });
Я использовал макет API для своих обещаний, ваш API может быть другим. Если вы покажете мне свой API, я смогу его адаптировать.
источник
Promise
конструктор и.promise()
метод?promise = new Promise();
?Promises
вы застряли в использовании обратных вызовов. Как только вы попадете на обещанную территорию, «пирамида» исчезнет. Посмотрите второй пример кода, чтобы узнать, как это будет работать.2019:
Используйте этот собственный модуль
const {promisify} = require('util');
для преобразования простого старого шаблона обратного вызова в шаблон обещания, чтобы вы могли получить выгоду отasync/await
кодаconst {promisify} = require('util'); const glob = promisify(require('glob')); app.get('/', async function (req, res) { const files = await glob('src/**/*-spec.js'); res.render('mocha-template-test', {files}); });
источник
При настройке обещания вы берете два параметра
resolve
иreject
. В случае успеха вызовresolve
с результатом, в случае неудачи вызовreject
с ошибкой.Тогда вы можете написать:
callback
будет вызываться с результатом обещания, возвращенного изgetUsers
, т.е.result
источник
Например, с помощью библиотеки Q:
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; }
источник
Код ниже работает только для узла -v> 8.x
Я использую это промежуточное ПО Promisified MySQL для Node.js
прочтите эту статью Создание промежуточного программного обеспечения базы данных MySQL с помощью Node.js 8 и 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
Вы должны обновить узел -v> 8.x
вы должны использовать асинхронную функцию, чтобы использовать ожидание.
пример:
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) }
источник