-
-
Save jbenet/11671be17cbe91c67a00d9638d99a45d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// this is a very rough draft idea of porting | |
// https://golang.org/pkg/context/ to javascript | |
class Context { | |
constructor() { | |
// _cancelled determines whether this context has | |
// been cancelled already | |
this._cancelled = false, | |
// _cancelconds is a set of cancel conditions, which | |
// when true will automatically cancel the context. | |
// these are checked every time the .done() function | |
// is called, so be careful with putting anything | |
// intensive there. (TODO: maybe this is a bad idea) | |
this._cancelConds = [] | |
// final error | |
this._err = null | |
} | |
// on attaches callback cb to happen when cancelled | |
onCancel(cb) { | |
this._on('cancel', cb) | |
} | |
addCancelCondition(fn) { | |
if (typeof(fn) !== 'function') { | |
return | |
} | |
this._cancelConds.push(fn) | |
} | |
// cancel cancels this context. | |
cancel(err) { | |
err = err || "cancel function called" | |
this._err = err | |
this._cancelled = true | |
this._trigger('cancel', this) | |
} | |
// done returns whether the context is "done" | |
done() { | |
if (this._cancelled) { | |
return true | |
} | |
// this may be super expensive. maybe dont do it | |
for (var i in this._cancelConds) { | |
if (this._cancelConds[i]()) { | |
this.cancel("cancel condition met") | |
return true | |
} | |
} | |
} | |
// err returns an error, if any. calling err | |
// before done() is true is undefined. | |
err() { | |
return this._err | |
} | |
} | |
var WithParent = (parentCtx) => { | |
var child = new Context() | |
parentCtx.on('cancel', child.cancel) | |
return child | |
} | |
var WithTimeout = (parentCtx, millis) => { | |
millis = (millis < 0 ? 0 : millis) | |
var ctx = WithParent(parentCtx) | |
setInterval(ctx.cancel, millis) | |
return ctx | |
} | |
var WithDeadline = (parentCtx, deadline) => { | |
var time = deadline - timeNow() | |
var ctx = WithTimeout(parentCtx, time) | |
ctx.addCancelCondition(() => { | |
return Date.now() > deadline | |
}) | |
// just in case the deadline is already passed. | |
process.nextTick(ctx.done) | |
return ctx | |
} | |
// calling code | |
var ctx = WithParent(Background) | |
doSomethingExpensive(ctx, cb) | |
ctx.cancel() | |
func doSomethingExpensive(ctx, cb) { | |
if (ctx.done()) { | |
return cb(ctx.err()) | |
} | |
doAnotherExpensiveThing((err, val) => { | |
if (err) { | |
return cb(err) | |
} | |
if (ctx.done()) { | |
return cb(ctx.err()) | |
} | |
doAnotherExpensiveThing((err, val) => { | |
if (err) { | |
return cb(err) | |
} | |
if (ctx.done()) { | |
return cb(ctx.err()) | |
} | |
cb(null, val) | |
}) | |
}) | |
} | |
doCancellableXHR(ctx, cb) | |
ctx.cancel() // cancels the something | |
func doCancellableXHR(ctx, cb) { | |
var done = false | |
var xhr = sendXHR(...) | |
xhr.on('error', cb) | |
xhr.on('success', () => { | |
done = true | |
}) | |
ctx.onCancel((err) => { | |
if (done || xhr.isDone()) return | |
xhr.abort() | |
cb(err) | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment