Promise
Promises, together with async/await, have become the go-to tools for handling concurrency in modern JavaScript development.
basics
Promise can be in one of three distinct states
- pending: The operation is still ongoing,
- fulfilled (resolved): The operation has completed successfully.
- rejected: The operation failed.
consuming a Promise
promise.then(value => console.log(value), err => console.error(err))
const main = async () => {
try {
console.log(await promise)
} catch (err) {
console.error(err)
}
}
const inc = x => x + 1
promise.then(inc).then(inc).then(console.log)
creating a Promise
an async function allways returns a Promise
const asyncValue = async <T>(val: T): Promise<T> => val
asyncValue(1).then(console.log)
creating a Promise using the constructor
const delay = (time: number): Promise<void> => {
return new Promise((resolve, reject) => setTimeout(() => resolve(), time))
}
await delay(1000)
// 1 seconds later
a simplified Promise implementation
type Callback<T=any> = (value: T) => any
function isPromsie(value: any): boolean {
return typeof value?.then === 'function'
}
export class SimplePromise<T> {
state: 'pending' | 'fulfilled' | 'rejected' = 'pending'
value: T
reason: any
onFulfilled: Array<Callback<T>> = []
onRejected: Array<Callback> = []
constructor(executor: (resolve: Callback<T>, reject: Callback) => void) {
const resolve = (value: T) => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value
this.onFulfilled.forEach(cb => cb(value))
}
}
const reject = (reason: any) => {
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = reason
this.onRejected.forEach(cb => cb(reason))
}
}
try {
executor(resolve, reject);
} catch (error) {
reject(error)
}
}
then(onFulfilled?: Callback<T>, onRejected?: Callback) {
return new SimplePromise((resolve, reject) => {
const handleFulfilled = (value: T) => {
if (onFulfilled) {
try {
const result = onFulfilled(value)
isPromsie(result) ? result.then(resolve, reject) : resolve(result)
} catch (error) {
reject(error)
}
} else {
resolve(value)
}
}
const handleRejected = (reason: any) => {
if (onRejected) {
try {
const result = onRejected(reason)
isPromsie(result) ? result.then(resolve, reject) : resolve(result)
} catch (error) {
reject(error)
}
} else {
reject(reason)
}
}
if (this.state === 'fulfilled') {
handleFulfilled(this.value)
} else if (this.state === 'rejected') {
handleRejected(this.reason)
} else if (this.state === 'pending') {
this.onFulfilled.push(handleFulfilled)
this.onRejected.push(handleRejected)
}
})
}
catch(onRejected: Callback) {
return this.then(undefined, onRejected)
}
}