Skip to content

Commit

Permalink
Redo SimplePromise, so it'd be chainable and wouldn't overflow stack.
Browse files Browse the repository at this point in the history
  • Loading branch information
rokbar-nosto committed Oct 24, 2023
1 parent 3e70911 commit 294cca2
Showing 1 changed file with 80 additions and 48 deletions.
128 changes: 80 additions & 48 deletions src/utils/promise.ts
Original file line number Diff line number Diff line change
@@ -1,70 +1,102 @@
class SimplePromise<T> implements PromiseLike<T> {
private value: T | undefined
private error: any
private fulfilled = false
private doneCallback: (() => void) | undefined = undefined
private status: string
private value: T
private onFulfilledCallbacks: Array<(value: T) => void>
private onRejectedCallbacks: Array<(value: any) => void>

constructor(
callback: (
resolve: (value: T) => void,
reject: (value: any) => void,
) => void,
handler: (resolve: (value: T) => void, reject: (reason?: any) => void) => void
) {
callback(
(value) => {
if (!this.fulfilled) {
this.value = value
this.fulfilled = true
this.value = null as T
this.status = 'pending'
this.onFulfilledCallbacks = []
this.onRejectedCallbacks = []

if (this.doneCallback) {
this.doneCallback()
}
}
},
(error) => {
if (!this.fulfilled) {
this.error = error
this.fulfilled = true
const resolve = (value: T) => {
if (this.status === 'pending') {
this.status = 'fulfilled'
this.value = value
this.onFulfilledCallbacks.forEach((fn) => fn(value))
}
}

if (this.doneCallback) {
this.doneCallback()
}
}
},
)
const reject = (value: T) => {
if (this.status === 'pending') {
this.status = 'rejected'
this.value = value
this.onRejectedCallbacks.forEach((fn) => fn(value))
}
}

try {
handler(resolve, reject)
} catch (err: any) {
reject(err)
}
}

then<TResult1 = T, TResult2 = never>(
onfulfilled?:
| ((value: T) => TResult1 | PromiseLike<TResult1>)
| null
| undefined,
| undefined
| null,
onrejected?:
| ((reason: any) => TResult2 | PromiseLike<TResult2>)
| null
| undefined,
| undefined
| null,
): PromiseLike<TResult1 | TResult2> {
return new SimplePromise((resolve, reject) => {
const doneCallback = () => {
if (this.error) {
if (onrejected) {
SimplePromise.resolve(onrejected(this.error)).then(
resolve,
reject,
)
if (this.status === 'pending') {
this.onFulfilledCallbacks.push(() => {
try {
const fulfilledFromLastPromise = onfulfilled?.(this.value)
if (fulfilledFromLastPromise && typeof fulfilledFromLastPromise === 'object' && 'then' in fulfilledFromLastPromise) {
fulfilledFromLastPromise.then(resolve, reject)
} else {
resolve(fulfilledFromLastPromise as TResult1)
}
} catch (err) {
reject(err)
}
})
this.onRejectedCallbacks.push(() => {
try {
const rejectedFromLastPromise = onrejected?.(this.value)
if (rejectedFromLastPromise && typeof rejectedFromLastPromise === 'object' && 'then' in rejectedFromLastPromise) {
rejectedFromLastPromise.then(resolve, reject)
} else {
reject(rejectedFromLastPromise)
}
} catch (err) {
reject(err)
}
} else {
if (onfulfilled) {
SimplePromise.resolve(
onfulfilled(this.value as T),
).then(resolve, reject)
})
}

if (this.status === 'fulfilled') {
try {
const fulfilledFromLastPromise = onfulfilled?.(this.value)
if (fulfilledFromLastPromise && typeof fulfilledFromLastPromise === 'object' && 'then' in fulfilledFromLastPromise) {
fulfilledFromLastPromise.then(resolve, reject)
} else {
resolve(fulfilledFromLastPromise as TResult1)
}
} catch (err) {
reject(err)
}
}
if (this.fulfilled) {
doneCallback()
} else {
this.doneCallback = doneCallback

if (this.status === 'rejected') {
try {
const rejectedFromLastPromise = onrejected?.(this.value)
if (rejectedFromLastPromise && typeof rejectedFromLastPromise === 'object' && 'then' in rejectedFromLastPromise) {
rejectedFromLastPromise.then(resolve, reject)
} else {
reject(rejectedFromLastPromise)
}
} catch (err) {
reject(err)
}
}
})
}
Expand Down

0 comments on commit 294cca2

Please sign in to comment.