How to convert a Promise-based function to callback in Javascript?

Asked

Viewed 72 times

2

We can convert asynchronous functions that work with callback to work with Promises, as shown in the examples below:

callback:

const timeInMs = 1_000;

function asyncCallback(timeMs, callback) {
  setTimeout(() => callback(null, timeMs), timeMs);
}

console.log('first');

asyncCallback(timeInMs, (err, result) => {
  if (err) return console.log('Error: ', err);

  console.log('last');

  console.log('Time passed: ', result);
});

console.log('second');

Switching to promise:

const timeInMs = 1_000;

function asyncCallback(timeMs, callback) {
  setTimeout(() => callback(null, timeMs), timeMs);
}

// função simples para converter callback em promise
function promisify(callback) {
  return function promisified(...args) {
    return new Promise((resolve, reject) => {
      const argsArray = [
        ...args,
        function (err, result) {
          if (err) return reject(err);

          resolve(result);
        },
      ];

      callback(...argsArray);
    });
  };
}

// promisificando e convertendo a função asyncCallback
const asyncPromise = promisify(asyncCallback);

console.log('first');

asyncPromise(timeInMs).then((result) => {
  console.log('last');

  console.log('Time passed: ', result);
});

console.log('second');

// usando async/await
(async () => {
  const result = await asyncPromise(timeInMs);

  console.log('[async/await] Time passed: ', result);
})();

Note that it was possible to change callback for Promise thanks to the customized function promisify().

The question is:

  • Like convert a function based on Promise for callback, that is, to do the opposite of promisify()? Something like callbackify().

It has a function in Node.js called callbackify of core module util who already does the reverse way:

import { callbackify } from 'util'

const timeInMs = 1_000

function asyncCallback (timeMs, callback) {
  setTimeout(() => callback(null, timeMs), timeMs)
}

function promisify (callback) {
  return function promisified (...args) {
    return new Promise((resolve, reject) => {
      const argsArray = [
        ...args,
        function (err, result) {
          if (err) return reject(err)

          resolve(result)
        },
      ]

      callback(...argsArray)
    })
  }
}

const asyncPromise = promisify(asyncCallback)

const callbackFunction = callbackify(asyncPromise)

callbackFunction(timeInMs, (err, result) => {
  console.log('Time passed: ', result) // Time passed:  1000
})

But I want to know how a function would be done "in hand" (similar to promisify()), step by step and with detailed explanation, because I want to understand the logic of implementation.

This doubt of mine is just a proof of concept I’d like to know.

  • If you found something similar, feel free to flag it as duplicate. I thought it would be an interesting subject. : D

  • 1

    It would be something like function callbackify(seuPromise, csuccess, cerror) { seuPromise.then(response => { csuccess(response); }).catch(error => { cerror(error) }); } and use so callbackify(asyncPromise, (resp) => { console.log(resp); }, error => { console.error(error); })?

  • @Guilhermenascimento is, and there is proof that if alcohol and direction do not match, sleep at dawn and also programming.

1 answer

3


By convention, the first parameter of the callback is always what indicates the error, if occurred. The others are the possible success values.

As a promise can only solve one value at a time, so the callback created from a Promise there may be at most two parameters.

It’s nothing very special. I could do something like this:

function callbackify(fn) {
  return function (...args) {
    const callback = args.pop();
    
    if (typeof callback !== 'function') {
      throw new TypeError('Expected a callback, which was not given.');
    }

    Promise.resolve(fn(...args))
      .then((successValue) => callback(null, successValue))
      .catch((errorValue) => callback(errorValue, null));
  };
}

Note that the function callbackify expects a function fn. Ideally this function should return an object Promise, although Promise.resolve avoids cases where this is not done.

A new function is returned (waiting for a callback in its last argument, when invoked). Note that the function returned is variadic, that is, expects a varied number of arguments, which will be passed to fn when it is called. Except the last, the callback, removed via Array.prototype.pop.

Of course I could fill up the code above with a few more checks, but I omitted this kind of thing for the brevity of the code.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.