it-swarm.asia

استبدال عمليات الاسترجاعات بالوعود في 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);

أرغب في استخدام الوعود بدلاً من عمليات الاسترجاعات من أجل إرجاع البيانات. لقد قرأت حتى الآن عن الوعود المتداخلة في الخيط التالي: كتابة التعليمات البرمجية النظيفة ذات الوعود المتداخلة ، لكن لم أجد أي حل بسيط بما يكفي لحالة الاستخدام هذه. ما هي الطريقة الصحيحة للعودة result باستخدام الوعد؟

88
Lior Erez

باستخدام فئة Promise

أوصي بإلقاء نظرة على مستندات الوعد الخاصة بـ MDN والتي توفر نقطة انطلاق جيدة لاستخدام الوعود. بدلاً من ذلك ، أنا متأكد من وجود العديد من البرامج التعليمية المتاحة عبر الإنترنت. :)

ملاحظة: تدعم المتصفحات الحديثة بالفعل مواصفات ECMAScript 6 الخاصة بالوعود (انظر مستندات MDN المرتبطة أعلاه) وأفترض أنك تريد استخدام التطبيق الأصلي ، بدون مكتبات الجهات الخارجية.

على سبيل المثال الفعلي ...

المبدأ الأساسي يعمل مثل هذا:

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

استخدام ميزة اللغة المتزامن/انتظار (Node.js> = 7.6)

في Node.js 7.6 ، تمت ترقية برنامج التحويل البرمجي لجافا سكريبت v8 مع دعم غير متزامن/انتظار . يمكنك الآن تعريف الدالات على أنها 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
}
92
Robert Rossmann

معbluebirdيمكنك استخدام Promise.promisifyAllPromise.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 الكثير من الميزات ، واحدة منها عبارة عن التخلص منها ، فهي تتيح لك التخلص بأمان من الاتصال بعد انتهائه بمساعدة 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).

31
Madara Uchiha

Node.js الإصدار 8.0.0+:

لم تعد مضطرًا إلى استخدام bluebird لتقديم طرق واجهة برمجة تطبيقات العقدة بعد الآن. لأنه ، من الإصدار 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')
        });
};

الآن ، لا تضطر إلى استخدام أي طرف ثالث للقيام بالوعد.

9
asmmahmud

على افتراض أن 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();
};

إذا كانت واجهة برمجة تطبيقات قاعدة البيانات تدعم 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) الخاصة بك ، يمكنني تخصيصها.

3
Halcyon

عند إعداد وعد ، فإنك تأخذ معلمتين ، resolve و reject. في حالة النجاح ، اتصل بـ resolve بالنتيجة ، في حالة فشل الاتصال reject مع الخطأ.

ثم يمكنك الكتابة:

getUsers().then(callback)

سيتم استدعاء callback كنتيجة الوعد الذي تم إرجاعه من getUsers ، أي result

2
Tom

باستخدام مكتبة 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;   
}
2
satchcoder

أدناه رمز يعمل فقط للعقدة -v> 8.x

يمكنني استخدام هذا MySQL الوسيطة Promisified لـ Node.js

قراءة هذا المقال إنشاء قاعدة بيانات MySQL لقاعدة بيانات مع Node.js 8 و 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

يجب ترقية العقدة -v> 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)
       }
0
hoogw