Node.js में वादों के साथ कॉलबैक की जगह


94

मेरे पास एक सरल नोड मॉड्यूल है जो एक डेटाबेस से जुड़ता है और डेटा प्राप्त करने के लिए कई फ़ंक्शन हैं, उदाहरण के लिए यह फ़ंक्शन:


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किसी वादे का उपयोग करके लौटने का सही तरीका क्या होगा ?


1
यदि आप kriskowal की Q लाइब्रेरी का उपयोग कर रहे हैं, तो Adapting Node देखें ।
बर्ट्रेंड मारोन

1
संभावित डुप्लिकेट मैं मौजूदा कॉलबैक API को वादों में कैसे परिवर्तित करूं? कृपया अपने प्रश्न को और अधिक विशिष्ट बनाएं, या मैं इसे बंद करने जा रहा हूं
बर्गी

@ leo.249: क्या आपने क्यू प्रलेखन पढ़ा है? क्या आपने इसे अपने कोड में पहले से ही लागू करने की कोशिश की है - यदि हाँ, तो कृपया अपना प्रयास पोस्ट करें (भले ही काम न हो)? आप वास्तव में कहाँ फंस गए हैं? आपको एक गैर-सरल समाधान मिला है, कृपया इसे पोस्ट करें।
बरगी

3
@ leo.249 Q व्यावहारिक रूप से अप्रसिद्ध है - अंतिम प्रतिबद्ध 3 महीने पहले था। केवल वी 2 शाखा क्यू डेवलपर्स के लिए दिलचस्प है और यह भी वैसे भी तैयार होने के करीब नहीं है। अक्टूबर से इश्यू ट्रैकर में टिप्पणियों के बिना अनसुने मुद्दे हैं। मैं दृढ़ता से सुझाव देता हूं कि आप एक अच्छी तरह से बनाए रखा वादा पुस्तकालय पर विचार करें।
बेंजामिन ग्रुएनबाम

जवाबों:


102

Promiseकक्षा का उपयोग करना

मैं MDN के प्रॉमिस डॉक्स पर एक नज़र डालने की सलाह देता हूं जो प्रोमिस का उपयोग करने के लिए एक अच्छा शुरुआती बिंदु प्रदान करते हैं। वैकल्पिक रूप से, मुझे यकीन है कि ऑनलाइन कई ट्यूटोरियल उपलब्ध हैं। :)

नोट: आधुनिक ब्राउज़र पहले से ही ECMAScript 6 विनिर्देशन के वादों का समर्थन करते हैं (ऊपर एमडीएन डॉक्स देखें) और मेरा मानना ​​है कि आप 3 जी पुस्तकालयों के बिना, मूल कार्यान्वयन का उपयोग करना चाहते हैं।

वास्तविक उदाहरण के लिए ...

मूल सिद्धांत इस तरह काम करता है:

  1. आपका API कहा जाता है
  2. आप एक नया वादा ऑब्जेक्ट बनाते हैं, यह ऑब्जेक्ट कंस्ट्रक्टर पैरामीटर के रूप में एकल फ़ंक्शन लेता है
  3. आपके प्रदान किए गए फ़ंक्शन को अंतर्निहित कार्यान्वयन द्वारा बुलाया जाता है और फ़ंक्शन को दो फ़ंक्शन दिए जाते हैं - resolveऔरreject
  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 / प्रतीक्षित भाषा सुविधा का उपयोग करना (Node.js> = 7.6)

Node.js 7.6 में, v8 जावास्क्रिप्ट कंपाइलर को async / प्रतीक्षा समर्थन के साथ अपग्रेड किया गया था । अब आप कार्यों को घोषणा के रूप में घोषित कर सकते हैं 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
}

14
वादे ECMAScript 2015 विनिर्देश का हिस्सा हैं और N8 v0.12 द्वारा उपयोग किया गया v8 कल्पना के इस हिस्से को लागू करता है। तो हां, वे नोड कोर का हिस्सा नहीं हैं - वे भाषा का हिस्सा हैं।
रॉबर्ट रॉसमैन

1
पता करने के लिए अच्छा है, मैं इस धारणा के तहत था कि वादों का उपयोग करने के लिए आपको एक एनपीएम पैकेज स्थापित करने और आवश्यकता () का उपयोग करने की आवश्यकता होगी। मुझे npm पर वादा पैकेज मिला जो नंगे हड्डियों / ए ++ शैली को लागू करता है और इसका इस्तेमाल किया है, लेकिन अभी भी खुद को नोड करने के लिए नया है (जावास्क्रिप्ट नहीं)।
macguru2000

वादे और वास्तुविद् async कोड लिखने के लिए यह मेरा पसंदीदा तरीका है, मुख्यतः क्योंकि यह एक सुसंगत पैटर्न है, आसानी से पढ़ा जाता है, और अत्यधिक संरचित कोड के लिए अनुमति देता है।

31

ब्लूबर्ड के साथ आप किसी भी वस्तु के लिए तैयार तरीकों को जोड़ने के लिए 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 का समर्थन करता है सुविधाओं का एक बहुत, उनमें से एक disposers है, यह आप सुरक्षित रूप से एक कनेक्शन के निपटान के लिए के बाद यह की मदद से समाप्त हो गया की अनुमति देता है Promise.usingऔर Promise.prototype.disposer। यहाँ मेरे 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();
        });
}

फिर इसे इस तरह उपयोग करें:

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

एक बार वादा (या के साथ अस्वीकार Error) के साथ वादा हल होने पर यह स्वचालित रूप से कनेक्शन समाप्त कर देगा ।


3
उत्कृष्ट उत्तर, मैंने Q के बजाय ब्लूबर्ड का उपयोग करके आपका धन्यवाद किया, धन्यवाद!
लायर एरेज़

2
ध्यान रखें कि वादों का उपयोग करके आप try-catchप्रत्येक कॉल पर उपयोग करने के लिए सहमत हैं। इसलिए यदि आप इसे अक्सर करते हैं, और आपकी कोड जटिलता नमूने के समान है, तो आपको इस पर पुनर्विचार करना चाहिए।
एंड्री पोपोव

14

Node.js संस्करण 8.0.0+:

अब आपको नोड 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')
        });
};

अब, किसी भी 3 पार्टी का उपयोग करने के लिए वादा नहीं करना है।


3

अपने डेटाबेस एडेप्टर एपीआई मान 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 */ });

मैंने अपने प्रॉमिस के लिए मॉकअप एपीआई का उपयोग किया है, आपका एपीआई अलग हो सकता है। यदि आप मुझे अपना एपीआई दिखाते हैं तो मैं इसे दर्जी कर सकता हूं।


2
क्या वादा पुस्तकालय एक Promiseनिर्माता और एक .promise()विधि है?
बरगी

धन्यवाद। मैं बस कुछ नोड का अभ्यास कर रहा हूं। जेएस और मैंने जो कुछ भी पोस्ट किया था, वह यह है कि वादों का उपयोग करने का तरीका जानने के लिए बहुत सरल उदाहरण है। आपका समाधान अच्छा लग रहा है, लेकिन उपयोग करने के लिए मुझे क्या npm पैकेज स्थापित करना होगा promise = new Promise();?
लायर एरेज़

जबकि आपका एपीआई अब एक वादा करता है, आपने कयामत के पिरामिड से छुटकारा नहीं पाया है, या कॉलबैक को बदलने के लिए वादे कैसे काम करते हैं, इसका एक उदाहरण दिया है।
मदार का भूत

@ leo.249 मैं नहीं जानता, किसी भी प्रोमिस लाइब्रेरी जो कि प्रोमिस / ए + के अनुरूप है, अच्छा होना चाहिए। देखें: वादों का निष्कर्ष.com/@ Bergi यह अप्रासंगिक है। @SecondRikudo यदि आप जिस एपीआई के साथ हस्तक्षेप कर रहे हैं वह समर्थन नहीं करता है Promisesतो आप कॉलबैक का उपयोग करने के साथ फंस गए हैं। एक बार जब आप वादा क्षेत्र में आते हैं तो 'पिरामिड' गायब हो जाता है। दूसरा कोड उदाहरण देखें कि यह कैसे काम करेगा।
हेल्सिओन

@ हेलसीयन मेरा उत्तर देखें। यहां तक ​​कि एक मौजूदा एपीआई जो कॉलबैक का उपयोग करता है, वह एक प्रॉमिस रेडी एपीआई में "प्रॉमिस" किया जा सकता है, जिसके परिणामस्वरूप बहुत क्लीनर कोड होता है।
मदारा का भूत

3

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

2

एक वादा करते समय आप दो पैरामीटर लेते हैं, resolveऔर reject। सफलता के मामले में, resolveपरिणाम के साथ कॉल करें , विफलता के मामले में rejectत्रुटि के साथ।

तो आप लिख सकते हैं:

getUsers().then(callback)

callbackसे आए वादे के परिणाम के साथ बुलाया जाएगा getUsers, अर्थातresult


2

उदाहरण के लिए 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;   
}

1
चाहेंगे, और {d.reject (नई त्रुटि (इरेट)); },उसे फिक्स करें?
रसेल

0

नीचे कोड केवल नोड -v> 8.x के लिए काम करता है

मैं Node.js के लिए इस Promisified MySQL मिडलवेयर का उपयोग करता हूं

इस लेख को पढ़ें Node.js 8 और Async / Await के साथ एक 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 अपग्रेड करना होगा

आप का उपयोग करने में सक्षम होने के लिए async फ़ंक्शन का उपयोग करना चाहिए।

उदाहरण:

   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)
       }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.