Explain Codes LogoExplain Codes Logo

Resolve Javascript Promise outside the Promise constructor scope

javascript
promise-engineering
async-await
deferred-patterns
Alex KataevbyAlex Kataev·Mar 5, 2025
TLDR
let externalResolve, externalReject; const myPromise = new Promise((resolve, reject) => { externalResolve = resolve; externalReject = reject; }); // Now we're gettin' spooky, resolving the promise from the outside! externalResolve('Resolved!'); // This can be done anywhere in your code // Usage with async logging myPromise.then(console.log); // Logs 'Resolved!'

We are essentially creating a deferred object, enabling resolution or rejection from any scope in your code.

Going deeper with deferred patterns

Rumor has it, deferred patterns run the world. They offer flexibility in managing promises, presenting an encapsulated promise object model with externally assignable resolve and reject functions. Let's dive in:

function Deferred() { this.promise = new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; }); } const deferred = new Deferred(); // Here's to hoping this promise doesn't break up with us for resolving externally 😅... deferred.promise.then(value => console.log(value)); deferred.resolve('External resolution for the win!'); // Logs 'External resolution for the win!'

In the code snippet, the Deferred class encloses the promise logic, making your code more readable. Want maintainable** and **clean code? Make deferred patterns your new best friend!

Properly handling promise resolution

Pulling strings from the outside lets you control when and how a promise is resolved. Use it when an external event might fulfil or reject the promise.

Heads up! Try avoiding the promise constructor within conditional statements. Here's a pro tip: Use a single promise and drive it based on conditions. This keeps your code consistent and shields it from rogue exceptions.

Master chaining and async/await

Thinking of resolving promises externally? First consider if chaining promises is an option. If 'maintaining an orderly flow' had a baby with 'handling asynchronous operations predictably', that would be promise chaining.

Combine async/await with a deferred pattern for clear code. Your teammates will thank you for this:

async function runDeferred(deferred) { const longAwaitedValue = await deferred.promise; // Logging the long-awaited value because good things come to those who wait! console.log(longAwaitedValue); } const deferred = new Deferred(); runDeferred(deferred); setTimeout(() => { // Because who doesn't love a surprise delayed resolution! deferred.resolve('Surprise resolution!'); }, 2000);

Remember Promise.prototype.finally for tidy cleanup, this one's a game-changer!

Incorporating deferred patterns in tasks

A task encapsulating a deferred promise adds more control over resolve/reject. This could be adapted to your specific requirements.

class Task { constructor(executor) { const deferred = new Deferred(); this.promise = deferred.promise; this.resolve = deferred.resolve; this.reject = deferred.reject; // Who needs a genie when we can grant our own wishes here! executor(this.resolve, this.reject); } } // Custom task, coming right up! const task = new Task((resolve, reject) => { // Let's wrap this up, task logic in here. }); task.promise.then(console.log); task.resolve('Task has finished, time for a tea break!');

Utilize well-established code patterns for creating such deferred objects and make external promise resolution your secret weapon.

Handling promises globally

Give resolve and reject to global variables for broader access. Maintain discretion though, give too many global variables and you risk namespace pollution.

Async/await combined with deferred for clarity

Make the relationship between async/await and promises clear by uniting them with a Deferred object. This approach helps in orchestrating asynchronous code.