Skip to content

Instantly share code, notes, and snippets.

@sergeysova
Last active October 4, 2017 15:42
Show Gist options
  • Select an option

  • Save sergeysova/1a5105cb1823c51511a5bda7c25aac5f to your computer and use it in GitHub Desktop.

Select an option

Save sergeysova/1a5105cb1823c51511a5bda7c25aac5f to your computer and use it in GitHub Desktop.
const isThenable = p =>
p && typeof p.then === 'function'
const isPromisable = p =>
isThenable(p) && typeof p.catch === 'function'
class Prom {
/**
*
* @param {(resolve: (data) => void, reject: (error) => void) => void} fn
*/
constructor(fn) {
this._resolvedChain = []
this._rejectedChain = []
this._status = 0 // 0 — pending, 1 — resolved, 2 — rejected
this._lastResult = undefined
this._lastError = undefined
const resolver = (data) => {
// console.log('resolver()', this._status, data)
if (!this._isPending) return
if (isThenable(data)) {
data.then(result => {
this._status = 1
this._lastResult = result
this._resolvedChain.forEach(resolve => resolve(result))
})
}
else {
this._status = 1
this._lastResult = data
this._resolvedChain.forEach(resolve => resolve(data))
}
}
const rejector = (error) => {
// console.log('rejector()', this._status, error, this._rejectedChain.length)
if (!this._isPending) return
this._status = 2
this._lastError = error
this._rejectedChain.forEach(reject => reject(error))
if (!this._rejectedChain.length) {
throw new Error(this._lastError)
}
}
fn(resolver, rejector)
}
get _isPending() {
return this._status === 0
}
get _isResolved() {
return this._status === 1
}
get _isRejected() {
return this._status === 2
}
_invoke() {
setTimeout(() => {
if (this._isResolved) {
this._resolvedChain.forEach(fn => fn(this._lastResult))
}
else if (this._isRejected) {
this._rejectedChain.forEach(fn => fn(this._lastError))
}
}, 0)
}
then(handler, catcher) {
return new Prom((resolve, reject) => {
if (catcher) {
this._rejectedChain.push(data => {
try {
const result = catcher(data)
if (isPromisable(result)) {
result.then(resolve).catch(reject)
} else {
resolve(result)
}
} catch (error) {
reject(error)
}
})
}
else {
this._rejectedChain.push(reject)
}
this._resolvedChain.push(data => {
try {
const result = handler(data)
if (isPromisable(result)) {
result.then(resolve).catch(reject)
}
else {
resolve(result)
}
}
catch (error) {
reject(error)
}
})
this._invoke()
})
}
/**
* @param {Function} catcher
* @return {Prom}
*/
catch(catcher) {
return new Prom((resolve, reject) => {
this._resolvedChain.push(resolve)
this._rejectedChain.push(data => {
try {
const result = catcher(data)
if (isPromisable(result)) {
result.then(resolve).catch(reject)
}
else {
resolve(result)
}
}
catch (error) {
reject(error)
}
})
this._invoke()
})
}
static resolve(data) {
const inst = new Prom(() => {})
inst._status = 1
inst._lastResult = data
return inst
}
static reject(error) {
const inst = new Prom(() => {})
inst._status = 2
inst._lastError = error
return inst
}
}
const wait = (ms) => new Prom(resolve => {
setTimeout(() => resolve(ms), ms)
})
const start = new Prom((resolve, reject) => {
resolve(new Prom(resolve => resolve(10)).then(e => wait(100)))
})
start.then(data => {
console.log('resolved', data)
return 1000
}).then(console.log)
Prom.reject(100)
.catch(err => {
console.error('Catched error', { err })
})
const wait = (ms) => new Prom(resolve => {
setTimeout(() => resolve(ms), ms)
})
wait(100)
.catch(err => {
throw new TypeError('Dooom')
})
.then(data => (console.log('resolved', { data }), 12))
.then(twelve => console.log('12', { twelve }))
.then(undef => {
throw new Error('argh')
})
.catch(foo => {
console.log({ foo: foo.message })
return 900
})
.then(nine => console.log({ nine }))
const wait = (ms) => new Prom(resolve => {
setTimeout(() => resolve(ms), ms)
})
wait(100)
.then(first => {
console.log({ first })
return wait(50).then(second => {
console.log({ second })
throw 900
})
})
.catch(third => {
console.log({ third })
throw 450
})
.then(skip => console.log({ skip })) // Should be skipped
.catch(four => {
console.log({ four })
return Prom.resolve(300)
})
.then(fivth => {
console.log({ fivth })
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment