用Node.js中的promise来replacecallback

我有一个简单的节点模块连接到一个数据库,并有几个function来接收数据,例如这个function:


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); 

我想用promise来代替callback来返回数据。 到目前为止,我已经阅读了以下线程中的嵌套承诺: 使用嵌套承诺编写干净的代码 ,但找不到任何足够简单的解决scheme。 使用承诺返回result的正确方法是什么?

使用Promise

我build议看一下MDN的Promise文档 ,它提供了使用Promise的一个很好的起点。 另外,我确定网上有很多教程。:)

注意:现代浏览器已经支持Promise的ECMAScript 6规范(请参阅上面链接的MDN文档),我假定您要使用本地实现,没有第三方库。

至于一个实际的例子…

基本原则是这样的:

  1. 你的API被调用
  2. 你创build一个新的Promise对象,这个对象接受一个单一的函数作为构造器参数
  3. 你提供的函数是由底层的实现调用的,函数有两个函数 – resolvereject
  4. 一旦你做了你的逻辑,你可以调用其中的一个来满足承诺,或拒绝一个错误

这可能看起来很多,所以这里是一个实际的例子。

 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语言function(Node.js> = 7.6)

在Node.js 7.6中,v8 JavaScript编译器升级了asynchronous/等待支持 。 您现在可以将函数声明为async ,这意味着它们会自动返回一个Promise ,在asynchronous函数完成执行时会parsing该Promise 。 在这个函数内部,可以使用await关键字来等待另一个Promiseparsing。

这里是一个例子:

 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 } 

蓝鸟,你可以使用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. }); 

join垃圾桶

蓝鸟支持很多function,其中之一是垃圾处理器,它允许您在Promise.usingPromise.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 ),这将自动结束连接。

假设你的数据库适配器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你可以这样做:(在这里你看到了Promise的威力,你的callback函数几乎消失了)

 exports.getUsers = function () { return connection.connect().then(function () { return connection.query('SELECT * FROM Users'); }); }; 

使用.then()返回一个新的(嵌套的)promise。

打电话给:

 module.getUsers().done(function (result) { /* your code here */ }); 

我为我的Promises使用了一个模型API,您的API可能会有所不同。 如果你向我展示你的API,我可以定制它。

设置承诺时,您需要两个参数, resolvereject 。 在成功的情况下,调用resolve结果,在失败的情况下调用reject与错误。

那么你可以写:

 getUsers().then(callback) 

callback将被调用getUsers返回的promise的结果,即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; } 

Node.js版本8.0.0+:

您不必再使用蓝鸟来渲染节点API方法。 因为从版本8+开始你可以使用native.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') }); }; 

现在,不必使用任何第三方库来做promisify。