Resolve a promise from outside in JavaScript: practical use cases
![Resolve a promise from outside in JavaScript: practical use cases](/_next/image?url=https%3A%2F%2Fwp.codingbeautydev.com%2Fwp-content%2Fuploads%2F2024%2F05%2Fresolve-promise-from-outside-js.png&w=3840&q=75)
It's one of those "cool" things you can do in JavaScript that are actually immensely powerful when put to good use.
let promiseResolve;
let promiseReject;
const promise = new Promise((resolve, reject) => {
promiseResolve = resolve;
promiseReject = reject;
});
promiseResolve();
Powerful practical use cases
Action B waiting for action A
A is ongoing but the user wants to do B but A needs to happen first.
Example: Social app where users can create, save, and publish posts. Like Medium.
<p>
Save status:
<b><span id="save-status">Not saved</span></b>
</p>
<p>
Publish status:
<b><span id="publish-status">Not published</span></b>
</p>
<button id="save">Save</button>
<button id="publish">Publish</button>
![A simple app where users can create, save and publish posts.](/_next/image?url=https%3A%2F%2Fwp.codingbeautydev.com%2Fwp-content%2Fuploads%2F2022%2F07%2Fimage-18.png&w=3840&q=75)
What if the user wants to publish the post when it's saving?
Solution: Ensure the post is saved before publishing happens.
saveButton.onclick = () => {
save();
};
publishButton.onclick = async () => {
await publish();
};
let saveResolve;
let hasSaved = false;
async function save() {
hasSaved = false;
saveStatus.textContent = 'Saving...';
// ✅ Resolve promise from outside
await makeSaveRequest();
saveResolve();
hasSaved = true;
saveStatus.textContent = 'Saved';
}
async function waitForSave() {
if (!hasSaved) {
await new Promise((resolve) => {
saveResolve = resolve;
});
}
}
async function publish() {
publishStatus.textContent = 'Waiting for save...';
await waitForSave();
publishStatus.textContent = 'Published';
return;
}
![Post is saved before publish happens.](/_next/image?url=https%3A%2F%2Fwp.codingbeautydev.com%2Fwp-content%2Fuploads%2F2022%2F07%2Fgif.gif&w=3840&q=75)
It gets even better when you abstract this logic into a kind of Deferred
class:
class Deferred {
constructor() {
this.promise = new Promise((resolve, reject) => {
this.reject = reject;
this.resolve = resolve;
});
}
}
const deferred = new Deferred();
// Resolve from outside
deferred.resolve();
Refactoring✅:
// ...
const deferredSave = new Deferred();
let hasSaved = false;
async function save() {
hasSaved = false;
saveStatus.textContent = 'Saving...';
// ✅ Resolve promise from outside
await makeSaveRequest();
saveResolve();
hasSaved = true;
saveStatus.textContent = 'Saved';
}
async function waitForSave() {
if (!hasSaved) await deferredSave.promise;
}
async function publish() {
// ...
}
And it works exactly like before:
![The functionality works as before after the refactor.](/_next/image?url=https%3A%2F%2Fwp.codingbeautydev.com%2Fwp-content%2Fuploads%2F2022%2F07%2Fgif-1.gif&w=3840&q=75)
Deferred
is much cleaner, which is why we've got tons of NPM libraries like it: ts-deferred
, deferred
, promise-deferred
...
![](/_next/image?url=https%3A%2F%2Fwp.codingbeautydev.com%2Fwp-content%2Fuploads%2F2024%2F05%2F2024-05-03-22_07_43-deferred-npm-and-6-more-pages-Personal-Microsoft%E2%80%8B-Edge.png&w=3840&q=75)
Promisifying an event stream
It's a great setup I've used multiple times.
Doing an async task that's actually waiting for an event stream to fire, internally:
// data-fetcher.js
const deferred = new Deferred();
let dataDeferred;
function startListening() {
dataDeferred = new Deferred();
eventStream.on('data', (data) => {
dataDeferred.resolve(data);
});
}
async function getData() {
return await dataDeferred.promise;
}
class Deferred {
constructor() {
this.promise = new Promise((resolve, reject) => {
this.reject = reject;
this.resolve = resolve;
});
}
}
// client.js
const { startListening, getData } = require('./data-fetcher.js');
startListening();
const data = await getData();
Final thoughts
Resolving promises externally unlocks powerful patterns.
From user actions to event streams, it keeps your code clean and flexible. Consider libraries like ts-deferred
for even better handling.
See also
- Master JavaScript generators: 5 inspiring practical use cases
- Master JavaScript Mutation Observer: amazing real-world use cases
- ?? vs || in JavaScript: The little-known difference
- 95% of users will never use your app again without this JavaScript feature
- Never use magic numbers in your code: Do this instead
- How to console.log WITHOUT newlines in JavaScript