Programing

Node.js에서 콜백을 프라 미스로 대체

lottogame 2020. 9. 5. 10:28
반응형

Node.js에서 콜백을 프라 미스로 대체


데이터베이스에 연결하고 데이터를 수신하는 여러 기능이있는 간단한 노드 모듈이 있습니다 (예 :이 기능).


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를 사용하고 싶습니다. 지금까지 다음 스레드에서 중첩 된 약속에 대해 읽었습니다 : Writing Clean Code With Nested Promises ,하지만이 사용 사례에 대해 충분히 간단한 솔루션을 찾을 수 없습니다. result약속을 사용하여 반환하는 올바른 방법은 무엇입니까 ?


Promise수업 사용

Promise 사용을위한 좋은 시작점을 제공하는 MDN의 Promise 문서살펴 보는 것이 좋습니다 . 또는 온라인에서 사용할 수있는 많은 자습서가 있다고 확신합니다. :)

참고 : 최신 브라우저는 이미 ECMAScript 6 Promises 사양을 지원하며 (위에 링크 된 MDN 문서 참조) 타사 라이브러리없이 기본 구현을 사용한다고 가정합니다.

실제 예는 ...

기본 원리는 다음과 같습니다.

  1. API가 호출되었습니다.
  2. 새로운 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 언어 기능 사용 (Node.js> = 7.6)

Node.js 7.6에서 v8 JavaScript 컴파일러는 async / await 지원 으로 업그레이드되었습니다 . 이제 함수를로 선언 할 수 있습니다. asyncPromise, 비동기 함수가 실행을 완료하면 해결되는 a 자동으로 반환합니다 . 이 함수 내에서 await키워드를 사용하여 다른 Promise가 해결 될 때까지 기다릴 수 있습니다 .

다음은 예입니다.

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모든 개체에 약속 준비 방법을 추가).

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 supports a lot of features, one of them is disposers, it allows you to safely dispose of a connection after it ended with the help of Promise.using and Promise.prototype.disposer. Here's an example from my app:

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

Then use it like this:

exports.getUsersAsync = function () {
    return Promise.using(getConnection()).then(function (connection) {
            return connection.queryAsync('SELECT * FROM Users')
        });
};

This will automatically end the connection once the promise resolves with the value (or rejects with an Error).


Node.js version 8.0.0+:

You don't have to use bluebird to promisify the node API methods anymore. Because, from version 8+ you can use native 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')
        });
};

Now, don't have to use any 3rd party lib to do the promisify.


Assuming your database adapter API doesn't output Promises itself you can do something like:

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

If the database API does support Promises you could do something like: (here you see the power of Promises, your callback fluff pretty much disappears)

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

Using .then() to return a new (nested) promise.

Call with:

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

I used a mockup API for my Promises, your API might be different. If you show me your API I can tailor it.


When setting up a promise you take two parameters, resolve and reject. In the case of success, call resolve with the result, in the case of failure call reject with the error.

Then you can write:

getUsers().then(callback)

callback will be called with the result of the promise returned from getUsers, i.e. result


Using the Q library for example:

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

2019:

Use that native module const {promisify} = require('util'); to conver plain old callback pattern to promise pattern so you can get benfit from 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});
});


Below code works only for node -v > 8.x

I use this Promisified MySQL middleware for Node.js

read this article Create a MySQL Database Middleware with Node.js 8 and 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

You must upgrade node -v > 8.x

you must use async function to be able to use await.

example:

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

참고URL : https://stackoverflow.com/questions/28432401/replacing-callbacks-with-promises-in-node-js

반응형