JavaScript How to Convert Callback to Async Await
Promise and then use await to wait for it inside an async function, like const result = await new Promise((resolve, reject) => callbackFunction(args, (err, data) => err ? reject(err) : resolve(data))).Examples
How to Think About It
Algorithm
Code
function multiplyAsync(x, callback) { setTimeout(() => { if (typeof x !== 'number') return callback('Input must be a number'); callback(null, x * x); }, 100); } function multiplyPromise(x) { return new Promise((resolve, reject) => { multiplyAsync(x, (err, result) => { if (err) reject(err); else resolve(result); }); }); } async function run() { try { const result = await multiplyPromise(5); console.log(result); } catch (error) { console.error('Error:', error); } } run();
Dry Run
Let's trace multiplyAsync(5) converted to async/await through the code
Call multiplyPromise(5)
Returns a Promise that calls multiplyAsync(5, callback)
Inside multiplyAsync, after 100ms delay
Checks if 5 is a number, then calls callback(null, 25)
Promise resolves with 25
await receives 25 and assigns to result
| Step | Action | Value |
|---|---|---|
| 1 | Call multiplyPromise(5) | Promise pending |
| 2 | multiplyAsync calls callback | callback(null, 25) |
| 3 | Promise resolves | 25 assigned to result |
Why This Works
Step 1: Wrap callback in Promise
The callback function is wrapped inside a Promise constructor to convert it into a Promise-based function.
Step 2: Use async function with await
An async function uses await to pause execution until the Promise resolves or rejects.
Step 3: Handle errors with try/catch
Errors from the callback are caught by rejecting the Promise and handled in the async function using try/catch.
Alternative Approaches
const util = require('util'); const multiplyPromise = util.promisify(multiplyAsync); async function run() { try { const result = await multiplyPromise(5); console.log(result); } catch (error) { console.error('Error:', error); } } run();
async function multiplyAsyncAwait(x) { return new Promise((resolve, reject) => { multiplyAsync(x, (err, result) => { if (err) reject(err); else resolve(result); }); }); } (async () => { try { const result = await multiplyAsyncAwait(5); console.log(result); } catch (e) { console.error(e); } })();
Complexity: O(1) time, O(1) space
Time Complexity
The conversion does not add loops or recursion; it only wraps existing asynchronous calls, so time complexity remains constant.
Space Complexity
Extra space is used for the Promise object, but this is minimal and constant.
Which Approach is Fastest?
Using util.promisify is fastest and cleanest in Node.js, while manual Promise wrapping is more flexible but slightly more verbose.
| Approach | Time | Space | Best For |
|---|---|---|---|
| Manual Promise Wrapper | O(1) | O(1) | Any environment, flexible |
| util.promisify | O(1) | O(1) | Node.js, standard callbacks |
| Inline async wrapper | O(1) | O(1) | Quick conversion, small code |