在Node.js的承诺更换回调在Node.js的承诺更换回调(Replacing callbacks

2019-05-12 17:35发布

我有连接到数据库,并具有多种功能来接收数据,例如该函数的简单节点模块:


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使用一个承诺?

Answer 1:

使用Promise

我建议看看MDN的承诺文件 ,其提供了使用承诺一个良好的起点。 另外,我相信有很多教程在网上提供。:)

注:现代浏览器已经支持承诺的的ECMAScript 6规范(见上面链接的MDN文档)和我假设你要使用本机实现,没有第三方库。

作为一个实际的例子...

其基本原理是这样的:

  1. 你的API调用
  2. 您创建一个新的承诺的对象,这个对象需要一个单一的功能构造函数的参数
  3. 您提供的功能是通过底层实现调用,该函数给出了两个功能- resolvereject
  4. 一旦你这样做你的逻辑,你可以调用其中的一个要么fullfill无极或者因为错误拒绝

这似乎是很多,所以这里是一个实际的例子。

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

使用异步/等待语言特性(的Node.js> = 7.6)

在Node.js的7.6,JavaScript编译器与升级的V8 异步/等待支持 。 现在,您可以声明函数为async ,这意味着他们自动返回一个Promise ,在异步函数完成执行其解决。 在这个函数中,你可以使用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
}


Answer 2:

随着蓝鸟你可以使用Promise.promisifyAll (和Promise.promisify )承诺准备方法添加到任何对象。

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

添加处置器

蓝鸟支持很多功能,其中之一是处置器,它可以让你安全地处理一个连接后的帮助下结束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 )。



Answer 3:

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

现在,不必使用任何第三方的lib做promisify。



Answer 4:

假设你的数据库接口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我可以调整它。



Answer 5:

当建立一个承诺,你需要两个参数, resolvereject 。 在成功的情况下,呼叫resolve其结果,在呼叫失败的情况下, reject与错误。

然后,你可以这样写:

getUsers().then(callback)

callback将与返回的承诺的结果叫getUsers ,即result



Answer 6:

使用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;   
}


Answer 7:

下面的代码仅适用于节点-v> 8.x的

我用这个Promisified MySQL的中间件的Node.js

看完这篇文章与Node.js的8和异步/等待创建MySQL数据库中间件

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的

您必须使用异步功能,能够使用的await。

例:

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


文章来源: Replacing callbacks with promises in Node.js