Skip to content

Instantly share code, notes, and snippets.

@julien-f
Last active February 15, 2018 00:08
Show Gist options
  • Select an option

  • Save julien-f/09bf4bdbae3d08111a2242bb7db81856 to your computer and use it in GitHub Desktop.

Select an option

Save julien-f/09bf4bdbae3d08111a2242bb7db81856 to your computer and use it in GitHub Desktop.
// Composable, lazy task operation constructor
class Async {
constructor(resolver) {
this._resolver = resolver
}
then(onFulfilment, onRejection) {
return new SyncAsyncPromise(this._resolver).then(onFulfilment, onRejection)
}
map(onFulfilment, onRejection) {
const resolver = this._resolver
return new Async((resolve, reject) => {
resolver(
typeof onFulfilment === 'function'
? value => resolve(onFulfilment(value))
: resolve,
typeof onRejection === 'function'
? reason => resolve(onRejection(reason))
: reject
)
})
}
}
exports.Async = Async
// Basic Promise implementation which is sync-if-possible.
const INTERNAL = {}
class SyncAsyncPromise {
static reject(reason) {
const p = new SyncAsyncPromise(INTERNAL)
p._reject(reason)
return p
}
static resolve(value) {
const p = new SyncAsyncPromise(INTERNAL)
p._resolve(value)
return p
}
constructor(resolver) {
const reject = (this._reject = this._reject.bind(this))
const resolve = (this._resolve = this._resolve.bind(this))
this._data = undefined
this._fulfilmentListeners = undefined
this._rejectionListeners = undefined
this._state = 'pending'
if (resolver === INTERNAL) {
return
}
try {
resolver(resolve, reject)
} catch (reason) {
this._data = reason
this._state = 'rejected'
}
}
catch(onRejection) {
return this.then(undefined, onRejection)
}
then(onFulfilment, onRejection) {
const state = this._state
if (state === 'pending') {
if (typeof onFulfilment === 'function') {
;(this._fulfilmentListeners || (this._fulfilmentListeners = [])).push(
onFulfilment
)
}
if (typeof onRejection === 'function') {
;(this._rejectionListeners || (this._rejectionListeners = [])).push(
onRejection
)
}
} else if (state === 'fulfilled') {
if (typeof onFulfilment === 'function') {
return new SyncAsyncPromise(resolve =>
resolve(onFulfilment(this._data))
)
}
} else if (state === 'rejected') {
if (typeof onRejection === 'function') {
return new SyncAsyncPromise(resolve => resolve(onRejection(this._data)))
}
}
return this
}
_reject(reason) {
this._data = reason
this._state = 'rejected'
const listeners = this._rejectionListeners
if (listeners !== undefined) {
this._rejectionListeners = undefined
listeners.forEach(listener => listener(reason))
}
}
_resolve(value) {
let then
if (value != null && typeof (then = value.then) === 'function') {
return then.call(value, this._resolve, this._reject)
}
this._data = value
this._state = 'fulfilled'
const listeners = this._fulfilmentListeners
if (listeners !== undefined) {
this._fulfilmentListeners = undefined
listeners.forEach(listener => listener(reason))
}
}
}
exports.SyncAsyncPromise = SyncAsyncPromise
const { Async } = require('./async')
const a = new Async(resolve => resolve(Date.now()))
a.then(console.log)
// 1518652134661
a.then(console.log)
// 1518652177220
const b = a.map(value => new Date(value).toISOString())
b.then(console.log)
// 2018-02-14T23:49:38.938Z
b.then(console.log)
// 2018-02-14T23:49:40.689Z
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment