Skip to content

Instantly share code, notes, and snippets.

@GHolk
Last active May 22, 2018 12:59
Show Gist options
  • Save GHolk/202183dda46475cc43756080ed149871 to your computer and use it in GitHub Desktop.
Save GHolk/202183dda46475cc43756080ed149871 to your computer and use it in GitHub Desktop.
wrap wrap wrap!
function patchGeneratorFunction() {
const GeneratorFunction = function *(){}.constructor
GeneratorFunction.prototype.asyncCallback = function () {
return CallbackAsync.fromGeneratorToCallback(this, ...arguments)
}
GeneratorFunction.prototype.asyncPromise = function () {
return PromiseAsync.fromGeneratorToPromise(this, ...arguments)
}
GeneratorFunction.prototype.async = GeneratorFunction.prototype.asyncPromise
}
class AsyncRunner {
constructor(run) {
this.run = run
}
error(error) {}
return(value) {}
tryNext(value) {
this.tryRun('next', value)
}
tryThrow(error) {
this.tryRun('throw', error)
}
tryRun(method, value) {
let isError = false
let result
try {
result = this.run[method](value)
}
catch (generatorError) {
result = generatorError
isError = true
}
if (isError) this.error(result)
else this.dealAsync(result.value, result.done)
}
testAsync(value) {}
waitAsync(value) {}
dealAsync(value, done) {
if (done) this.return(value)
else if (this.testAsync(value)) {
this.waitAsync(value)
}
else this.tryNext(value)
}
}
class PromiseAsync extends AsyncRunner {
static fromGeneratorToPromise(generator, ...argument) {
const run = generator(...argument)
const promiseAsync = new this(run)
return promiseAsync.promise
}
constructor(run) {
super(run)
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve
this.reject = reject
})
this.tryNext()
}
error(error) {
this.reject(error)
}
return(value) {
this.resolve(value)
}
testAsync(promise) {
return promise && typeof promise.then == 'function'
}
waitAsync(promise) {
promise.then(value => this.tryNext(value))
.catch(error => this.tryThrow(error))
}
dealAsync(value, done) {
if (done) this.return(value)
else if (value && value.then) {
const promise = value
promise.then(resolveValue => this.tryNext(resolveValue))
.catch(error => this.tryThrow(error))
}
else this.tryNext(value)
}
}
class CallbackAsync extends AsyncRunner {
static fromGeneratorToCallback(generator, ...argument) {
return callback => {
const run = generator(...argument)
const callbackAsync = new this(run, callback)
}
}
constructor(run, callback) {
super(run)
this.callback = callback
this.tryNext()
}
error(error) {
this.callback(error)
}
return(value) {
const error = null
this.callback(error, value)
}
testAsync(value) {
return typeof value == 'function'
}
waitAsync(callback) {
callback((error, value) => {
if (error) this.tryThrow(error)
else this.tryNext(value)
})
}
}
class Deferer {
constructor() {
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve
this.reject = reject
})
}
}
function sleep(second) {
return new Promise(wake => setTimeout(wake, second * 1000))
}
function *fh() {
console.log('ehlo')
yield sleep(1)
console.log('helo')
yield sleep(2)
return 1
}
function *testError() {
try {
yield true
}
catch (er2) {
return er2
}
}
function fact(n) {
return factTail(n, 1)
async function factTail(n, p) {
if (n == 0) return p
else return await factTail(n-1, n*p)
}
return factAsync(n)
async function factAsync(n) {
let product = 1
for (let i=n; i>0; i--) {
product *= i
await sleep(0)
}
return product
}
const defer = Promise.defer()
return defer
function factRecur(n, p) {
if (n == 0) defer.resolve(p)
else setTimeout(() => factRecur(n-1, n*p), 0)
}
}
class Promise {
constructor(todo) {
this.onResolve = []
this.onReject = []
this.todo = todo
this.state = 'wait'
this.value = null
this.execute()
}
execute() {
const resolve = this.resolve.bind(this)
const reject = this.reject.bind(this)
try {
this.todo(resolve, reject)
}
catch (promiseError) {
if (this.state == 'wait') {
this.state = 'error'
this.value = promiseError
}
}
if (this.state == 'error') {
setTimeout(() => this.after(), 0)
}
}
resolve(value) {
if (this.state == 'wait') {
this.state = 'resolve'
this.value = value
this.after()
}
}
reject(error) {
if (this.state == 'wait') {
this.state = 'error'
this.value = error
this.after()
}
}
after() {
let list
if (this.state == 'resolve') list = this.onResolve
else {
list = this.onReject
if (list.length == 0) console.error('unhandle promise error')
}
for (const listener of list) {
setTimeout(() => listener(this.value), 0)
}
}
done(resolve, reject) {
if (this.state == 'wait') {
this.onResolve.push(resolve)
if (reject) this.onReject.push(reject)
}
else {
let thenTodo
if (this.state == 'resolve') thenTodo = resolve
else if (reject) thenTodo = reject
if (thenTodo) {
setTimeout(() => thenTodo(this.value), 0)
}
}
}
then(resolve, reject) {
return new Promise((thenResolve, thenReject) => {
this.done(
this.wrapThen(resolve, thenResolve, thenReject),
this.wrapThen(reject, thenReject, thenReject)
)
})
}
wrapThen(todo, resolve, reject) {
return value => this.tryCatch(
() => todo(value),
error => reject(error),
value => this.waitThen(value, resolve)
)
}
waitThen(value, next) {
if (value && value.then) value.then(next)
else next(value)
}
tryCatch(todo, ifError, ifOk) {
let value
let isError
try {
value = todo()
isError = false
}
catch (error) {
isError = true
}
if (isError) ifError(value)
else ifOk(value)
}
}
exports.Promise = Promise
exports.patchGeneratorFunction = patchGeneratorFunction
exports.AsyncRunner = AsyncRunner
exports.PromiseAsync = PromiseAsync
exports.CallbackAsync = CallbackAsync
exports.test = function test(filename) {
const fs = require('fs')
function log(error, value) {
if (error) console.error(error)
else console.log(value)
}
cb.asyncCallback()(log)
dthrow.asyncCallback()(log)
function *cb() {
const data = yield fs.readFile.bind(fs, '.bashrc', 'utf8')
let other
try {
other = yield fs.readFile.bind(fs, filename, 'utf8')
}
catch (error) {
console.error(error)
}
if (other) console.log(other)
return data
}
function *dthrow() {
throw new Error('just error')
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment