Chuyển Đổi Hàm Callback Thành Promise Với es6-promisify

Trong lập trình JavaScript bất đồng bộ, việc lồng ghép callback dẫn đến "địa ngục callback" làm giảm khả năng bảo trì mã nguồn. Thư viện es6-promisify cung cấp giải pháp chuyển đổi các hàm dựa trên callback sang mô hình Promise chuẩn ES6, giúp viết code tuyến tính và dễ đọc hơn.

Cơ chế hoạt động

Khi làm việc với API cũ sử dụng callback, cấu trúc code thường rối rắm do xử lý lỗi lồng ghép. Ví dụ minh họa:

database.query('SELECT * FROM users', (error, results) => {
  if (error) return handleError(error);
  database.query('SELECT * FROM orders', (err, orders) => {
    if (err) return handleError(err);
    // Xử lý tiếp tục...
  });
});

es6-promisify giải quyết vấn đề này bằng cách bao bọc hàm callback thành Promise, cho phép sử dụng cú pháp async/await hiện đại.

Cài đặt và sử dụng cơ bản

Cài đặt qua npm:

npm install es6-promisify --save

Ví dụ chuyển đổi hàm truy vấn cơ sở dữ liệu:

const { promisify } = require('es6-promisify');
const db = require('./database');

// Chuyển đổi phương thức query
const fetchRecords = promisify(db.query);

async function loadUserData() {
  try {
    const users = await fetchRecords('SELECT * FROM users');
    console.log('Danh sách người dùng:', users);
  } catch (dbError) {
    console.error('Lỗi truy vấn:', dbError);
  }
}

loadUserData();

Tình huống nâng cao

Xử lý phương thức đối tượng

Áp dụng cho các client có phương thức riêng:

const redisClient = require('redis').createClient();
const executeCommand = promisify(redisClient.send_command.bind(redisClient));

async function checkRedis() {
  try {
    const response = await executeCommand('PING');
    console.log('Kết nối Redis thành công:', response);
  } finally {
    redisClient.quit();
  }
}

Xử lý callback đa tham số

Đối với hàm trả về nhiều giá trị:

function getUserData(id, callback) {
  setTimeout(() => {
    callback(null, 'Nguyễn Văn A', 28, 'nva@example.com');
  }, 150);
}

// Định nghĩa tên tham số
getUserData[promisify.argumentNames] = ['fullName', 'age', 'email'];

const retrieveUser = promisify(getUserData);

async function showProfile() {
  const profile = await retrieveUser(101);
  console.log('Thông tin cá nhân:', profile);
  // Kết quả: { fullName: 'Nguyễn Văn A', age: 28, email: 'nva@example.com' }
}

Tích hợp thư viện Promise tùy chỉnh

Sử dụng Bluebird cho hiệu năng cao hơn:

const Bluebird = require('bluebird');
promisify.Promise = Bluebird;

const optimizedQuery = promisify(db.query);
optimizedQuery('SELECT * FROM products')
  .then(data => console.log('Sản phẩm:', data));

Nguyên lý cốt lõi

Hàm promisify hoạt động dựa trên cơ chế:

  1. Kiểm tra tính hợp lệ của hàm đầu vào
  2. Xác định trình xử lý Promise (mặc định hoặc tùy chỉnh)
  3. Tạo hàm mới bao bọc callback bằng Promise
function promisify(targetFn) {
  if (typeof targetFn !== 'function') 
    throw new TypeError('Chỉ chấp nhận hàm');

  const customPromise = promisify.Promise || Promise;
  
  return function(...params) {
    return new customPromise((resolve, reject) => {
      params.push((err, ...results) => {
        if (err) return reject(err);
        resolve(results.length > 1 ? results : results[0]);
      });
      targetFn.apply(this, params);
    });
  };
}

Thẻ: es6-promisify async-await callback-conversion javascript-async promise-patterns

Đăng vào ngày 13 tháng 6 lúc 17:42