PROMISIFICATION IN JAVASCRIPT
Promisification means changing a function so that it returns a promise instead of accepting callbacks.
This note explains promisification in simple language.
You will learn:
- what promisification is
- the difference between callbacks and promises
- how to turn a callback function into a promise-based function
- how
Promise.resolve()works - how
Promise.reject()works - how to promisify synchronous functions
- how to create delayed promises with a reusable function
1. What is promisification?
Promisification means changing a function so that it no longer accepts callbacks, but instead returns a promise.
A function changed in this way is called a promisified function.
Easy definition:
Old version:
function takes callbacks
Promisified version:
function returns a promise
Diagram 1. Main idea
Callback-style function
-> depends on external callbacks
Promisified function
-> returns promise
-> outer code handles result with then/catch
The function should focus on doing its job. It should not worry about how outer code wants to use the result.
2. Callbacks vs promises
Imagine a function that requests user data from a server.
Callback version
const fetchUserFromServer = (username, onSuccess, onError) => {
// ...
};
This function expects:
onSuccessfor successful resultonErrorfor failure
That means the function knows too much about the outside code, because it expects other functions to be passed in and then must decide when to call them.
Diagram 2. Callback style
Outer code
-> passes callbacks into function
-> function decides when to call them
This works, but it makes the function more dependent on external code.
3. Main differences between callbacks and promises
Important differences:
- callbacks are functions
- promises are objects
- callbacks are passed into a function
- promises are created inside a function and returned
- callbacks handle success or error directly
- promises only store the current state of the async operation
Diagram 3. Callback vs promise
Callback
-> function passed as argument
Promise
-> object returned from function
Easy rule:
callback = you give function to async code
promise = async code gives object back to you
4. Callback version of async code
const fetchUserFromServer = (username, onSuccess, onError) => {
console.log(`Fetching data for ${username}`);
setTimeout(() => {
const isSuccess = true;
if (isSuccess) {
onSuccess("success value");
} else {
onError("error");
}
}, 2000);
};
fetchUserFromServer(
"Mango",
user => console.log(user),
error => console.error(error)
);
What happens here?
- the function starts an asynchronous action
- it waits 2 seconds
- it checks
isSuccess - if
true, it callsonSuccess - if
false, it callsonError
Diagram 4. Callback flow
Call function
-> wait 2 seconds
-> check isSuccess
|- true -> call onSuccess(...)
`- false -> call onError(...)
5. Why promisification is better
It is better if the function does not care about the external result-handling code.
It should simply do the operation and return its result as a promise.
Then the outer code can decide how to handle success or error with then() and catch().
Diagram 5. Better design
Function
-> does async work
-> returns promise
Outer code
-> handles result with then/catch
This makes the function more independent and cleaner.
6. First step of promisification
To promisify the function, start by returning a promise:
const fetchUserFromServer = username => {
return new Promise((resolve, reject) => {
// ...
});
};
Now the function no longer takes onSuccess and onError.
Instead, it creates a promise and returns it.
Diagram 6. First transformation
Before:
(username, onSuccess, onError)
After:
(username) => Promise
7. Full promisified version
const fetchUserFromServer = username => {
return new Promise((resolve, reject) => {
console.log(`Fetching data for ${username}`);
setTimeout(() => {
const isSuccess = true;
if (isSuccess) {
resolve("success value");
} else {
reject("error");
}
}, 2000);
});
};
fetchUserFromServer("Mango")
.then(user => console.log(user))
.catch(error => console.error(error));
What changed?
- the function now returns a promise
- success is handled by
resolve(...) - error is handled by
reject(...) - outer code uses
.then()and.catch()
Diagram 7. Promisified flow
Call function
-> function returns promise
-> wait 2 seconds
-> check isSuccess
|- true -> resolve("success value")
`- false -> reject("error")
Outer code
-> then(...) or catch(...)
8. Handling the returned promise
You can store the promise in a variable:
const userPromise = fetchUserFromServer("Mango");
userPromise
.then(user => console.log(user))
.catch(error => console.error(error));
But in practice, developers usually attach handlers directly:
fetchUserFromServer("Mango")
.then(user => console.log(user))
.catch(error => console.error(error));
The second version is shorter and usually more convenient.
Diagram 8. Two ways to use returned promise
Option 1:
save promise in variable
-> add then/catch later
Option 2:
call function
-> attach then/catch immediately
9. Promise.resolve() and Promise.reject()
These are static methods of the Promise class.
They create promises that are already settled:
Promise.resolve(value)creates a fulfilled promisePromise.reject(error)creates a rejected promise
They are shorter alternatives to writing new Promise(...) for simple cases.
Diagram 9. Quick promise creation
Promise.resolve(value)
-> fulfilled promise
Promise.reject(error)
-> rejected promise
10. Example of Promise.resolve()
Longer version
new Promise(resolve => resolve("success value"))
.then(value => console.log(value))
.catch(error => console.log(error));
Shorter version
Promise.resolve("success value")
.then(value => console.log(value))
.catch(error => console.log(error));
Both create a fulfilled promise, but the second version is simpler.
Diagram 10. Promise.resolve()
"success value"
-> Promise.resolve(...)
-> fulfilled promise
-> then(...) runs
11. Example of Promise.reject()
Longer version
new Promise((resolve, reject) => reject("error"))
.then(value => console.log(value))
.catch(error => console.log(error));
Shorter version
Promise.reject("error")
.then(value => console.log(value))
.catch(error => console.log(error));
This creates a rejected promise, so catch() runs.
Diagram 11. Promise.reject()
"error"
-> Promise.reject(...)
-> rejected promise
-> catch(...) runs
12. Promisifying a synchronous function
Promise.resolve() and Promise.reject() are useful even when the original function is synchronous, but you still want it to return a promise and fit into a promise chain.
13. Callback version of a synchronous function
const makeGreeting = (guestName, onSuccess, onError) => {
if (!guestName) {
onError("Guest name must not be empty");
} else {
onSuccess(`Welcome ${guestName}`);
}
};
makeGreeting(
"Mango",
greeting => console.log(greeting),
error => console.error(error)
);
This function does not wait for any async action, but it still uses callbacks.
14. Promisified synchronous version with new Promise
const makeGreeting = guestName => {
return new Promise((resolve, reject) => {
if (!guestName) {
reject("Guest name must not be empty");
} else {
resolve(`Welcome ${guestName}`);
}
});
};
makeGreeting("Mango")
.then(greeting => console.log(greeting))
.catch(error => console.error(error));
This already works as a promisified version.
15. Shorter version with Promise.resolve() and Promise.reject()
const makeGreeting = guestName => {
if (!guestName) {
return Promise.reject("Guest name must not be empty");
}
return Promise.resolve(`Welcome ${guestName}`);
};
makeGreeting("Mango")
.then(greeting => console.log(greeting))
.catch(error => console.error(error));
This is shorter and easier to read.
Diagram 12. Synchronous promisification
Function checks value
|- bad value -> Promise.reject(...)
`- good value -> Promise.resolve(...)
This is useful when you want all your functions to work in the same promise style.
16. Creating delayed promises
Next, create a reusable function called makePromise(options).
This function returns promises with different:
- values
- delays
- result types, resolve or reject
17. Function structure
const makePromise = ({ value, delay, shouldResolve = true }) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldResolve) {
resolve(value);
} else {
reject(value);
}
}, delay);
});
};
Parameters:
valueis what the promise should returndelayis how long to waitshouldResolvecontrols whether the promise should be fulfilled or rejected
The default value of shouldResolve is true.
Diagram 13. makePromise() logic
makePromise({ value, delay, shouldResolve })
-> wait delay
-> check shouldResolve
|- true -> resolve(value)
`- false -> reject(value)
18. Using makePromise()
makePromise({ value: "A", delay: 1000 })
.then(value => console.log(value))
.catch(error => console.log(error));
makePromise({ value: "B", delay: 3000 })
.then(value => console.log(value))
.catch(error => console.log(error));
makePromise({ value: "C", delay: 2000, shouldResolve: false })
.then(value => console.log(value))
.catch(error => console.log(error));
What happens?
"A"resolves after 1 second"B"resolves after 3 seconds"C"rejects after 2 seconds
Diagram 14. Three delayed promises
Promise A
-> 1 sec
-> resolve "A"
Promise B
-> 3 sec
-> resolve "B"
Promise C
-> 2 sec
-> reject "C"
This is useful because you write the delayed promise logic once and reuse it many times.
19. Why makePromise() is useful
Without this reusable function, you would have to write new Promise(...) and setTimeout(...) again and again for every delayed promise.
The helper function removes repetition and makes the code cleaner.
Diagram 15. Repetition vs reusable function
Without helper
-> write same promise code many times
With makePromise()
-> write logic once
-> reuse it with different options
20. Easy memory rules
Promisification = change callback function into promise-returning function
Callback style
-> function accepts onSuccess / onError
Promisified style
-> function returns promise
-> outer code uses then/catch
Promise.resolve(value)
-> fulfilled promise
Promise.reject(error)
-> rejected promise
21. Quick summary
- Promisification means transforming a callback-based function into a function that returns a promise.
- A promisified function returns a promise instead of accepting callbacks.
- Callbacks are functions, promises are objects.
Promise.resolve(value)creates a fulfilled promise.Promise.reject(error)creates a rejected promise.- Synchronous functions can also be promisified when needed for promise chains.
- A reusable function like
makePromise({ value, delay, shouldResolve })helps create delayed promises without repeating the same code.
22. Final conclusion
If you understand these ideas:
promisification
callback vs promise
returning a promise
Promise.resolve()
Promise.reject()
promisifying sync functions
delayed promises
then you already have a strong foundation for working with promise-based JavaScript code.