How to Resolve a Promise from Outside in JavaScript

To resolve a promise from outside in JavaScript, assign the resolve
callback to a variable defined outside the Promise
constructor scope, then call the variable to resolve the Promise
. For example:
let promiseResolve;
let promiseReject;
const promise = new Promise((resolve, reject) => {
promiseResolve = resolve;
promiseReject = reject;
});
promiseResolve();
Now why would we need to do something like this? Well, maybe we have an operation A currently in progress, and the user wants another operation B to happen, but B must wait for A to complete. Let's say we have a simple social app where users can create, save and publish posts.
index.html
<!DOCTYPE html>
<html>
<head>
<title>Resolving a Promise from Outside</title>
</head>
<body>
<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>
<script src="index.js"></script>
</body>
</html>

What if a post is currently being saved (operation A) and the user wants to publish the post (operation B) while saving is ongoing?. If we don't want to disable the "Publish" button when the save is happening, we'll need to ensure the post is saved before publish happens.
index.js
// Enable UI interactivity
const saveStatus = document.getElementById('save-status');
const saveButton = document.getElementById('save');
const publishStatus = document.getElementById(
'publish-status'
);
const publishButton = document.getElementById('publish');
saveButton.onclick = () => {
save();
};
publishButton.onclick = async () => {
await publish();
};
let saveResolve;
let hasSaved = false;
function save() {
hasSaved = false;
saveStatus.textContent = 'Saving...';
setTimeout(() => {
saveResolve();
hasSaved = true;
saveStatus.textContent = 'Saved';
}, 3000);
}
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;
}
The key parts of this code are the save()
and waitForSave()
functions. When the user clicks "Publish", waitForSave()
is called. If the post has already been saved, the Promise
returned from waitForSave()
resolves immediately, otherwise it assigns its resolve
callback to an external variable that will be called after the save. This makes publish()
wait for the timeout in save()
to expire before continuing.

We can create a Deferred
class to abstract and reuse this logic:
class Deferred {
constructor() {
this.promise = new Promise((resolve, reject) => {
this.reject = reject;
this.resolve = resolve;
});
}
}
const deferred = new Deferred();
// Resolve from outside
deferred.resolve();
Now the variables to resolve/reject a Promise
and the Promise
itself will be contained in the same Deferred
object.
We can refactor our code to use this class:
// Enable UI interactivity
// ...
const deferredSave = new Deferred();
let hasSaved = false;
function save() {
hasSaved = false;
saveStatus.textContent = 'Saving...';
setTimeout(() => {
deferredSave.resolve();
hasSaved = true;
saveStatus.textContent = 'Saved';
}, 3000);
}
async function waitForSave() {
if (!hasSaved) await deferredSave.promise;
}
async function publish() {
// ...
}
And the functionality will work as before:

11 Amazing New Features in ES13

See also
- 7 little-known but powerful array methods in JavaScript
- How to Simulate a Mouse Click in JavaScript
- This if statement alternative only works in JavaScript
- This is how functional try-catch transforms your JavaScript code
- Easy Endless Infinite Scroll With JavaScript
- structuredClone(): The Easiest Way to Copy Objects in JavaScript