নোড.জেএস-এ প্রতিশ্রুতি দিয়ে কলব্যাকগুলি প্রতিস্থাপন করা হচ্ছে


94

আমার একটি সাধারণ নোড মডিউল রয়েছে যা একটি ডাটাবেসের সাথে সংযোগ স্থাপন করে এবং ডেটা পাওয়ার জন্য বেশ কয়েকটি ফাংশন রয়েছে, উদাহরণস্বরূপ এই কার্য:


ডিবি সংযোগ.জেএস:

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প্রতিশ্রুতি ব্যবহার করে ফিরে আসার সঠিক উপায় কী হবে ?


4
অ্যাডাপ্টিং নোড দেখুন , যদি আপনি ক্রিস্কোওয়ালের কিউ লাইব্রেরি ব্যবহার করেন।
বারট্রান্ড মেররন

4
সম্ভাব্য সদৃশ কিভাবে আমি একটি বিদ্যমান কলব্যাক এপিআই প্রতিশ্রুতি রূপান্তর করতে পারি? দয়া করে আপনার প্রশ্নটিকে আরও সুনির্দিষ্ট করুন বা আমি এটি বন্ধ করতে যাচ্ছি
বার্গি

@ লিও .২49৯: আপনি কিউ ডকুমেন্টেশন পড়েছেন? আপনি কি ইতিমধ্যে আপনার কোডটিতে এটি প্রয়োগ করার চেষ্টা করেছেন - যদি হ্যাঁ, তবে দয়া করে আপনার প্রচেষ্টা পোস্ট করুন (কাজ না করেও)? ঠিক কোথায় আটকে গেছিস? আপনি একটি অ-সহজ সমাধান খুঁজে পেয়েছেন বলে মনে হচ্ছে, দয়া করে এটি পোস্ট করুন।
বার্গি

4
@ লিও .২৪৯ কিউ প্রায়োগতভাবে অবিরাম - সর্বশেষ প্রতিশ্রুতিটি 3 মাস আগে হয়েছিল। কেবলমাত্র ভি 2 শাখাটি কিউ বিকাশকারীদের কাছে আকর্ষণীয় এবং এটি যেভাবেই উত্পাদন প্রস্তুত হওয়ার কাছাকাছি নয়। অক্টোবর থেকে ইস্যু ট্র্যাকারে মন্তব্য ছাড়াই শোধহীন সমস্যা রয়েছে। আমি আপনাকে দৃ strongly়ভাবে প্রস্তাব দিচ্ছি যে আপনি একটি ভাল রক্ষণাবেক্ষণ প্রতিশ্রুতি গ্রন্থাগার বিবেচনা করুন।
বেনিয়ামিন গ্রুইনবাউম

উত্তর:


103

Promiseক্লাস ব্যবহার করে

আমি এমডিএন এর প্রতিশ্রুতি ডক্সের দিকে একবার নজর দেওয়ার পরামর্শ দিচ্ছি যা প্রতিশ্রুতি ব্যবহারের জন্য একটি ভাল সূচনা পয়েন্ট দেয়। বিকল্পভাবে, আমি নিশ্চিত যে অনলাইনে অনেক টিউটোরিয়াল উপলব্ধ :)

দ্রষ্টব্য: আধুনিক ব্রাউজারগুলি ইতিমধ্যে ECMAScript 6 প্রতিশ্রুতির স্পেসিফিকেশনটিকে সমর্থন করে (উপরে লিঙ্কিত MDN ডক্স দেখুন) এবং আমি ধরে নিয়েছি যে আপনি তৃতীয় পক্ষের লাইব্রেরি ছাড়াই দেশীয় বাস্তবায়নটি ব্যবহার করতে চান।

প্রকৃত উদাহরণ হিসাবে ...

মূল নীতিটি এইভাবে কাজ করে:

  1. আপনার এপিআই কল করা হয়
  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
  })

অ্যাসিঙ্ক / অপেক্ষারত ভাষা বৈশিষ্ট্যটি ব্যবহার করুন (নোড.জেএস> = .6..6)

নোড.জেএস .6..6 এ, ভি 8 জাভাস্ক্রিপ্ট সংকলকটি async / অপেক্ষারত সহায়তায় আপগ্রেড করা হয়েছিল । আপনি এখন ফাংশনগুলি অস্তিত্ব হিসাবে ঘোষণা করতে পারেন async, যার অর্থ তারা Promiseযখন স্বয়ংক্রিয়ভাবে এসিঙ্ক ফাংশন সম্পাদন সম্পূর্ণ করে তখন তারা সমাধান হয়ে যায় automatically এই ফাংশনের অভ্যন্তরে, আপনি 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 স্পেসিফিকেশনের অংশ এবং নোড v0.12 দ্বারা ব্যবহৃত v8 অনুচ্ছেদে এই অংশটির বাস্তবায়ন সরবরাহ করে। সুতরাং হ্যাঁ, তারা নোড কোরের অংশ নয় - এগুলি ভাষার অংশ।
রবার্ট রোসম্যান

4
জেনে রাখা ভাল, আমি এই ধারণাটির মধ্যে ছিলাম যে প্রতিশ্রুতিগুলি ব্যবহার করতে আপনাকে এনপিএম প্যাকেজ ইনস্টল করতে হবে এবং ব্যবহারের প্রয়োজন হবে ()। আমি এনএমপি তে প্রতিশ্রুতি প্যাকেজটি পেয়েছি যা বেয়ার হাড় / এ ++ স্টাইল প্রয়োগ করে এবং এটি ব্যবহার করেছি, তবে নিজেই নোডে নতুন (জাভাস্ক্রিপ্ট নয়) am
macguru2000

প্রতিশ্রুতি এবং আর্কিটেক্ট অ্যাসিঙ্ক কোডটি লেখার এটি আমার প্রিয় উপায়, মূলত কারণ এটি একটি সামঞ্জস্যপূর্ণ প্যাটার্ন, সহজেই পড়া এবং উচ্চ কাঠামোগত কোডের জন্য মঞ্জুরি দেয়।

31

সঙ্গে Bluebird আপনি ব্যবহার করতে পারেন 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। আমার অ্যাপ থেকে একটি উদাহরণ এখানে দেওয়া হয়েছে:

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


4
দুর্দান্ত উত্তর, আমি কিউ ধন্যবাদ পরিবর্তে ব্লুবার্ড ব্যবহার করে শেষ করেছি, আপনাকে ধন্যবাদ!
লিওর ইরজ

4
মনে রাখবেন যে প্রতিশ্রুতি ব্যবহার try-catchকরে আপনি প্রতিটি কলটিতে ব্যবহার করতে সম্মত হন । সুতরাং আপনি যদি বেশিরভাগ সময় এটি করেন এবং আপনার কোড জটিলতা নমুনার অনুরূপ, তবে আপনার এটি পুনর্বিবেচনা করা উচিত।
আন্দ্রে পোপভ

14

নোড.জেএস সংস্করণ 8.0.0+:

আপনাকে নোড এপিআই পদ্ধতিগুলি প্রচারের জন্য ব্লুবার্ড ব্যবহার করতে হবে না। কারণ, সংস্করণ থেকে 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 ব্যবহার করতে হবে না।


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

যদি ডাটাবেস এপিআই সমর্থন করে তবে Promisesআপনি এর মতো কিছু করতে পারেন: (এখানে আপনি প্রতিশ্রুতিগুলির শক্তি দেখেন, আপনার কলব্যাক ফ্লাফটি প্রায় অদৃশ্য হয়ে যায়)

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

.then()নতুন (নেস্টেড) প্রতিশ্রুতি ফিরিয়ে দিতে ব্যবহার করা।

সাথে কল করুন:

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

আমি আমার প্রতিশ্রুতিগুলির জন্য একটি মকআপ এপিআই ব্যবহার করেছি, আপনার এপিআই আলাদা হতে পারে। আপনি যদি আমাকে আপনার এপিআই দেখান তবে আমি এটি তৈরি করতে পারি।


4
কোন প্রতিশ্রুতি গ্রন্থাগারের একটি Promiseকনস্ট্রাক্টর এবং একটি .promise()পদ্ধতি রয়েছে?
বার্গি

ধন্যবাদ. আমি কিছু নোড.জেএস অনুশীলন করছি এবং আমি যা পোস্ট করেছি তা এখানে রয়েছে, প্রতিশ্রুতি কীভাবে ব্যবহার করতে হয় তা নির্ধারণ করার জন্য খুব সহজ উদাহরণ। আপনার সমাধানটি দেখতে ভাল লাগছে তবে ব্যবহারের জন্য আমাকে কোন এনপিএম প্যাকেজটি ইনস্টল করতে হবে promise = new Promise();?
লিয়র এরেজ

আপনার এপিআই যখন এখন প্রতিশ্রুতি দেয়, আপনি কিছুর পিরামিড থেকে মুক্তি পাননি বা কলব্যাকগুলি প্রতিস্থাপনে প্রতিশ্রুতিগুলি কীভাবে কাজ করে তার একটি উদাহরণ তৈরি করেন নি।
মাদারার ঘোস্ট

@ Leo.249 আমি জানি না, প্রতিশ্রুতি / এ + এর সাথে সম্মতিযুক্ত কোনও প্রতিশ্রুতি পাঠাগারটি ভাল হওয়া উচিত। দেখুন: িড়সাপ্লাস.com/@ বার্গি এটি অপ্রাসঙ্গিক। @ সেকেন্ড রিকুডো যদি আপনি যে এপিআইয়ের সাথে ইন্টারফেস করছেন সেটি যদি সমর্থন না করে 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

উদাহরণস্বরূপ কিউ লাইব্রেরি ব্যবহার করে:

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

4
অন্যথায় {d.reject (নতুন ত্রুটি (ত্রুটি)); }, ঠিক আছে?
রাসেল

0

নীচের কোডটি কেবল নোড -v> 8.x এর জন্য কাজ করে

আমি এই প্রচারিত মাইএসকিউএল মিডওয়্যারটি নোড.জেএস এর জন্য ব্যবহার করি

এই নিবন্ধটি পড়ুন নোড.জেএস 8 এবং অ্যাসিঙ্ক / অ্যাওয়েট সহ একটি মাইএসকিউএল ডেটাবেস মিডলওয়্যার তৈরি করুন

ডাটাবেস.জেএস

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

আপনাকে অবশ্যই নোড-ভি> 8.x আপগ্রেড করতে হবে

অপেক্ষারত ব্যবহার করতে সক্ষম হতে আপনাকে অ্যাসিঙ্ক ফাংশনটি ব্যবহার করতে হবে।

উদাহরণ:

   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)
       }
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.