create Manual Promise
Create a promise that you can resolve outside the Promise's executor function
(i.e. the callback argument passed to new Promise()
). This is mostly just
useful for making your code 1 level less nested, or if you're allergic to
async
and await
syntax.
typescriptjavascript
export type ManualPromise<T> = {
promise: Promise<T>
resolve: (value: T | PromiseLike<T>) => void
reject: (reason?: unknown) => void
}
export const createManualPromise = <T>(): ManualPromise<T> => {
let resolve: ManualPromise<T>['resolve'] | undefined
let reject: ManualPromise<T>['reject'] | undefined
const promise = new Promise<T>((res, rej) => {
resolve = res
reject = rej
})
return { promise, resolve: resolve!, reject: reject! }
}
export const createManualPromise = () => {
let resolve
let reject
const promise = new Promise((res, rej) => {
resolve = res
reject = rej
})
return { promise, resolve: resolve, reject: reject }
}
Examples
Wait for a message from a worker thread
main.ts
typescriptjavascript
export const getAnswerToTheUltimateQuestionOfLifeTheUniverseAndEverythingFromWorker =
(): Promise<any> => {
const { promise, resolve, reject } = createManualPromise<any>()
const worker = new Worker(
new URL('./deep-thought-worker.ts', import.meta.url)
)
worker.onmessage = event => {
resolve(event.data)
worker.terminate()
}
worker.onerror = error => {
reject(error)
worker.terminate()
}
worker.postMessage(null)
return promise
}
export const getAnswerToTheUltimateQuestionOfLifeTheUniverseAndEverythingFromWorker =
() => {
const { promise, resolve, reject } = createManualPromise()
const worker = new Worker(
new URL('./deep-thought-worker.ts', import.meta.url)
)
worker.onmessage = event => {
resolve(event.data)
worker.terminate()
}
worker.onerror = error => {
reject(error)
worker.terminate()
}
worker.postMessage(null)
return promise
}
deeper-thought-worker.ts
typescriptjavascript
/**
* Function that calculates the Answer to the Ultimate Question of Life, the
* Universe, and Everything waaaaaaay faster than Deep Thought's 7.5 million
* years.
*/
const calculateAnswerToTheUltimateQuestionOfLifeTheUniverseAndEverything =
(): number => {
const start = Date.now()
const end = start + 5_000
while (Date.now() < end);
return 0x6 * 0x9 === 0x2a ? 0x2a : 0x2a
}
self.onmessage = () => {
const answer =
calculateAnswerToTheUltimateQuestionOfLifeTheUniverseAndEverything()
self.postMessage(answer)
}
/**
* Function that calculates the Answer to the Ultimate Question of Life, the
* Universe, and Everything waaaaaaay faster than Deep Thought's 7.5 million
* years.
*/
const calculateAnswerToTheUltimateQuestionOfLifeTheUniverseAndEverything =
() => {
const start = Date.now()
const end = start + 5_000
while (Date.now() < end);
return 0x6 * 0x9 === 0x2a ? 0x2a : 0x2a
}
self.onmessage = () => {
const answer =
calculateAnswerToTheUltimateQuestionOfLifeTheUniverseAndEverything()
self.postMessage(answer)
}
Waiting for a DOM event to happen
typescriptjavascript
export const waitUntilClicked = (selector: string): Promise<void> => {
const { promise, resolve, reject } = createManualPromise<void>()
const element = document.querySelector(selector)
if (!element) {
reject(new Error(`Element not found for selector: ${selector}`))
return promise
}
element.addEventListener('click', () => {
resolve()
})
return promise
}
export const waitUntilClicked = selector => {
const { promise, resolve, reject } = createManualPromise()
const element = document.querySelector(selector)
if (!element) {
reject(new Error(`Element not found for selector: ${selector}`))
return promise
}
element.addEventListener('click', () => {
resolve()
})
return promise
}
Simple fetch
wrapper, but bad
typescriptjavascript
// Post from https://jsonplaceholder.typicode.com/
type Post = { userId: number; id: number; title: string; body: string }
export const fetchPosts = (): Promise<Post[]> => {
const { promise, resolve, reject } = createManualPromise<Post[]>()
fetch('https://jsonplaceholder.typicode.com/')
.then(response => {
if (res.ok) {
return response.json()
}
return response
.text()
.then(text => Promise.reject(new Error(`Fetch posts error: ${text}`)))
})
.then(json => resolve(json as Post[]))
.catch(error => reject(error))
return promise
}
// Post from https://jsonplaceholder.typicode.com/
export const fetchPosts = () => {
const { promise, resolve, reject } = createManualPromise()
fetch('https://jsonplaceholder.typicode.com/')
.then(response => {
if (res.ok) {
return response.json()
}
return response
.text()
.then(text => Promise.reject(new Error(`Fetch posts error: ${text}`)))
})
.then(json => resolve(json))
.catch(error => reject(error))
return promise
}