Convert Callback Based Function to Promise Based Function

Updated on 08 May, 2025
Convert Callback Based Function to Promise Based Function header image

Problem Statement

In modern programming, especially in JavaScript, handling asynchronous operations is crucial. Traditionally, these operations were managed using callbacks, which eventually led to complex situations like "callback hell". To simplify asynchronous code management, promises were introduced. The task here involves creating a function promisify that converts a callback-based function fn into a promise-based function.

The function fn accepts a callback as its primary argument followed by any additional arguments passed separately. When fn is invoked, if the operation completes successfully, the callback is called with the result as its first parameter. On the other hand, if the operation fails, the callback is invoked with an error as the second parameter. The promisify function should return a new function that, when called, returns a promise. This promise should resolve or reject based on how the callback within fn is executed.

For instance, consider a function sum that calculates the sum of two parameters and uses a callback for its result handling. If used with promisify, the function should work seamlessly with promise-based handling, returning a promise that resolves with the sum of the parameters or rejects with an error if the input parameters are invalid.

Examples

Example 1

Input:

fn = (callback, a, b, c) => {
callback(a * b * c);
}
args = [1, 2, 3]

Output:

{"resolved": 6}

Explanation:

const asyncFunc = promisify(fn);
asyncFunc(1, 2, 3).then(console.log); // 6
fn is called with a callback as the first argument and args as the rest. The promise based version of fn resolves a value of 6 when called with (1, 2, 3).

Example 2

Input:

fn = (callback, a, b, c) => {
callback(a * b * c, "Promise Rejected");
}
args = [4, 5, 6]

Output:

{"rejected": "Promise Rejected"}

Explanation:

const asyncFunc = promisify(fn);
asyncFunc(4, 5, 6).catch(console.log); // "Promise Rejected"
fn is called with a callback as the first argument and args as the rest. As the second argument, the callback accepts an error message, so when fn is called, the promise is rejected with a error message provided in the callback. Note that it did not matter what was passed as the first argument into the callback.

Constraints

  • 1 <= args.length <= 100
  • 0 <= args[i] <= 104

Approach and Intuition

The task requires wrapping a traditional callback-based function in a modern promise-based envelope. Here's a breakdown of the steps and the operational logic involved:

  1. Creating the promisify Function:

    • The promisify function accepts a function fn as its argument. This function in turn uses a callback to handle its result or error.
    • promisify returns a new function that retains the arguments of fn except for the callback (which it internally manages).
  2. Handling Invocation of fn:

    • Inside the returned function from promisify, a new promise is created.
    • The promise executor (the function passed to the new Promise constructor) will call fn, passing a custom callback and the arguments intended for fn.
    • The custom callback checks if it receives an error as its second argument. If it does, the promise is rejected with this error.
    • If there is no error, the promise resolves with the result passed as the first argument to the callback.
  3. Using the Promisified Function:

    • Once a function has been promisified, it can be used like any other function that returns a promise.
    • One can use .then for successful resolution or .catch to handle errors, leading to cleaner, more readable asynchronous code.

Explanation from Examples

  • Example 1:

    • A function multiplying three numbers returns the result via a callback. This function, once promisified, is called with arguments 1, 2, 3 leading to a resolution of the promise with the product 6.
  • Example 2:

    • A similar multiplication function, but designed to always return an error in the callback. When promisified and invoked with 4, 5, 6, it results in the promise being rejected with the error message "Promise Rejected".

Promisification eases the management of asynchronous operations, particularly when interfacing with APIs or libraries that use old-style callbacks, paving the way towards using async/await syntax which is syntactically cleaner and easier to follow.

Solutions

  • JavaScript
js
var makePromise = (func) => async (...parameters) =>
  new Promise((resolve, reject) =>
    func((result, error) => error ? reject(error) : resolve(result), ...parameters)
  );

In JavaScript, transforming a callback-based function into a promise-based function enhances the readability and maintainability of asynchronous operations. The provided solution implements this transformation using a higher-order function makePromise.

  • Define makePromise as an arrow function that accepts func as its argument.
  • The function returns an asynchronous function which takes any number of parameters using the spread syntax (...parameters).
  • Inside this returned function, construct a new Promise.
    • The executor function of this Promise takes two parameters: resolve and reject.
    • Call the original func within the executor. The first argument of func is another function that handles the callback logic. This inner function checks if an error exists; if so, it calls reject(error), otherwise, it calls resolve(result).
    • Spread the parameters as additional arguments to func.

This approach converts any callback-based function to a promise-based function efficiently, allowing usage with modern async/await syntax for better asynchronous flow control.

Comments

No comments yet.