javascript - sincronas - node.js ~ construye secuencia encadenada de Promesa resuelve
promise nodejs (2)
La clave para recordar acerca de las promesas es que then
devuelve una nueva promesa (al igual que la catch
). La forma en que se resuelva esa nueva promesa depende de lo que devuelva el controlador: si devuelve una promesa, la nueva promesa de then
/ catch
queda subordinada a la que devolvió; si devuelve un valor, la nueva promesa se resuelve con ese valor.
Entonces puedes encadenarlos juntos. Piense en los manejadores de then
y de catch
como filtros transformadores a través de los cuales fluye el resultado final.
Tenga en cuenta también que si tiene un punto de partida que le da una promesa ( cn.connect()
), no necesita new Promise
: simplemente use y catch
para transformar lo que pasa a través de la cadena devolviendo el (nuevo) valor de resolución .
Otra cosa clave para recordar es que si un controlador catch
devuelve un valor, convierte un rechazo en una resolución. Para continuar por la ruta de rechazo, un controlador catch
debe lanzar una excepción o devolver una promesa que sea / será rechazada.
Finalmente: las llamadas require
siempre deben estar al comienzo del módulo.
Entonces, sin eliminar su conversión de rechazos a resoluciones (más sobre eso en un momento):
var sql = require(''mssql'');
var myDao = require(''./myDao'');
module.exports = {
dbConnection: function () {
return { user: ''sa'', password: ''mypassword'', server: ''localhost'', database: ''mydb'' };
},
CanIConnectToTheDB: function () {
var cn = new sql.ConnectionPool(myDao.dbConnection());
return cn.connect()
.then(function () {
var req = new sql.Request(cn);
var qry = ''select serverproperty(/'productversion/') as /'rs/''';
return req.query(qry)
.then(function (rs) {
qry = ''select isnull(object_id(/'SomeObjectIKnowExists/'), -1)'';
return req.query(qry)
.then(function (rss) { // Note you''re not using rss anywhere
return '' CONNECTED// MASTER DB SUCCESS// MY DB SUCCESS'';
})
.catch(function (err) {
return '' CONNECTED// MASTER DB SUCCESS// ISSUE QUERYING MY DB //'' + err + ''//'';
});
})
.catch(function (er) {
return '' CONNECTED// COULD NOT QUERY MASTER DB //'' + er + ''//'';
});
})
.catch(function() {
return '' CAN NOT CONNECT'';
});
}
};
Nota: ES MI intención NO usar el rej [ect] aquí. Lo que ves es que solo regresa la res [olve]. Lo que significa que el código al que regresa solo necesita una ruta para manejar el valor devuelto. De este modo, el código devuelto es más simple en su flujo.
Los rechazos siguen un camino separado de las resoluciones por una razón. No hace las cosas más complicadas, hace las cosas más simples . No convierta los rechazos en resoluciones a menos que haya realizado una recuperación de errores de algún tipo y pueda continuar como si el rechazo no hubiera sucedido.
Aquí está el código donde los rechazos pueden ser rechazos:
var sql = require(''mssql'');
var myDao = require(''./myDao'');
module.exports = {
dbConnection: function () {
return { user: ''sa'', password: ''mypassword'', server: ''localhost'', database: ''mydb'' };
},
CanIConnectToTheDB: function () {
var cn = new sql.ConnectionPool(myDao.dbConnection());
return cn.connect()
.then(function () {
var req = new sql.Request(cn);
var qry = ''select serverproperty(/'productversion/') as /'rs/''';
return req.query(qry)
.then(function (rs) {
qry = ''select isnull(object_id(/'SomeObjectIKnowExists/'), -1)'';
return req.query(qry)
.then(function (rss) { // Note you''re not using rss anywhere
return '' CONNECTED// MASTER DB SUCCESS// MY DB SUCCESS'';
});
});
});
}
};
Utilizándolo:
theModule.CanIConnectToTheDB()
.then(function() {
// Yes, let''s do something
})
.catch(function() {
// No, report the problem, etc.
});
Probablemente también resuma el bit que supongo que terminará haciendo una y otra vez: establecer una conexión y obtener un objeto de solicitud de ella:
var sql = require(''mssql'');
var myDao = require(''./myDao'');
function getRequest() {
var cn = new sql.ConnectionPool(myDao.dbConnection());
return cn.connect().then(function() {
return new sql.Request(cn);
});
}
module.exports = {
dbConnection: function () {
return { user: ''sa'', password: ''mypassword'', server: ''localhost'', database: ''mydb'' };
},
CanIConnectToTheDB: function () {
return getRequest().then(function(req) {
var qry = ''select serverproperty(/'productversion/') as /'rs/''';
return req.query(qry)
.then(function (rs) {
qry = ''select isnull(object_id(/'SomeObjectIKnowExists/'), -1)'';
return req.query(qry)
.then(function (rss) { // Note you''re not using rss anywhere
return '' CONNECTED// MASTER DB SUCCESS// MY DB SUCCESS'';
});
});
});
}
};
¿Alguien puede sugerir una mejor manera de estructurar este uso de Promesas? Soy nuevo en Promises y me pregunto si me falta algo sobre cómo construir esta cadena de eventos.
Nota: ES MI intención NO usar el rej [ect] aquí. Lo que ves es que solo regresa la res [olve]. Lo que significa que el código al que regresa solo necesita una ruta para manejar el valor devuelto. De este modo, el código devuelto es más simple en su flujo.
Puede ser útil saber, si no lo reconoces, esto se toma de un módulo que creé. Piense en ello como un Dao.
module.exports = {
dbConnection: function () {
return { user: ''sa'', password: ''mypassword'', server: ''localhost'', database: ''mydb'' };
},
CanIConnectToTheDB: function () {
return new Promise(function (res, rej) {
var sql = require(''mssql'');
var myDao = require(''./myDao'');
var cn = new sql.ConnectionPool(myDao.dbConnection());
cn.connect().then(function () {
var req = new sql.Request(cn);
var qry = ''select serverproperty(/'productversion/') as /'rs/''';
req.query(qry)
.then(function (rs) {
qry = ''select isnull(object_id(/'SomeObjectIKnowExists/'), -1)'';
req.query(qry)
.then(function (rss) {
res('' CONNECTED// MASTER DB SUCCESS// MY DB SUCCESS'');
})
.catch(function (err) {
res('' CONNECTED// MASTER DB SUCCESS// ISSUE QUERYING MY DB //'' + err + ''//'');
});
})
.catch(function (er) {
res('' CONNECTED// COULD NOT QUERY MASTER DB //'' + er + ''//'');
});
})
.catch(function () {
res('' CAN NOT CONNECT'');
});
});
}
};
En lugar de algo como esto:
.then(function (rs) {
qry = ''...'';
req.query(qry)
.then(function (rss) {
puedes usar algo como esto:
.then(function (rs) {
qry = ''...'';
return req.query(qry);
}).then(function (rss) {
Es decir, puede devolver una promesa en una sola devolución de llamada y obtener el valor resuelto de esa promesa en la siguiente devolución de llamada, por lo que su sangría se mantiene constante.
Ejemplo más simple - en lugar de esto:
a().then(va => {
b(va).then(vb => {
c(vb).then(vc => {
// you can use vc here
});
});
});
tu puedes hacer:
a().then(va => {
return b(va);
}).then(vb => {
return c(vb);
}).then(vc => {
// you can use vc here
});
O, incluso más simple si usa async
y await
:
va = await a();
vb = await b(va);
vc = await c(vb);
// you can use vc here
Tenga en cuenta que solo puede usar await
dentro de una función creada con la palabra clave async
. En los lugares donde no tiene soporte nativo para async
y await
, puede usar Babel o con una sintaxis ligeramente diferente, un enfoque basado en generador como en co
o corutinas de Bluebird. Para obtener más información y asistencia en navegadores y Nodo, consulte esta respuesta:
Actualizar
Esto no está probado, pero esto es más o menos cómo lo escribiría:
module.exports = {
dbConnection: function () {
return { user: ''sa'', password: ''mypassword'', server: ''localhost'', database: ''mydb'' };
},
CanIConnectToTheDB: function () {
var sql = require(''mssql'');
var myDao = require(''./myDao'');
var cn = new sql.ConnectionPool(myDao.dbConnection());
var req;
return cn.connect()
.catch(err => Promise.reject(''Error 1: '' + err))
.then(() => {
req = new sql.Request(cn);
var qry = ''select serverproperty(/'productversion/') as /'rs/''';
return req.query(qry)
.catch(err => Promise.reject(''Error 2: '' + err));
}).then(rs => {
var qry = ''select isnull(object_id(/'SomeObjectIKnowExists/'), -1)'';
return req.query(qry)
.catch(err => Promise.reject(''Error 3: '' + err));
}).then(function (rss) {
return ''CONNECTED// MASTER DB SUCCESS// MY DB SUCCESS'';
}).catch(err => {
// if you want it always resolved:
return ''CAN NOT CONNECT: '' + err;
});
}
};
Por supuesto, mantendría la promesa final devuelta por esa función para ser rechazada por errores y solo se resolvería en caso de éxito, pero como usted incluyó explícitamente ese extraño requisito en su pregunta, entonces lo escribí como quería.
Pero si rechaza la promesa de cualquier error, entonces sería mucho más fácil de usar, especialmente si lo único que le importa es la respuesta a la pregunta en el nombre de la función: ¿Puedo conectarme a la base de datos ?:
CanIConnectToTheDB()
.then(() => console.log("Yes I can"))
.catch(() => console.log("No I can''t"));
Más información:
Para más información, vea esas respuestas:
- Cómo convertir el método Waterfall en una promesa
- jQuery: Devuelve los datos después del éxito de la llamada ajax (ver explicación en las actualizaciones de esta respuesta)