byvoid / continuation Goto Github PK
View Code? Open in Web Editor NEWJavaScript asynchronous Continuation-Passing Style transformation (deprecated).
License: Other
JavaScript asynchronous Continuation-Passing Style transformation (deprecated).
License: Other
a={}
a.b=(cb)->
console.log this
try
console.log this
this.b cont()
catch err
console.error err
a.b()
the output code is
(function () {
var a;
a = {};
a.b = function (cb) {
var err;
console.log(this);
(function (_$cont) {
try {
console.log(this);
return this.b(function (arguments) {
try {
_$cont();
} catch (_$err) {
_$cont(_$err);
}
}.bind(this, arguments));
} catch (_$err) {
_$cont(_$err);
}
}(function (_error) {
if (_error !== undefined) {
err = _error;
return console.error(err);
}
}));
};
a.b();
}.call(this));
/* Generated by Continuation.js v0.1.7 */
Apparently, this
get lost in the structure
(function (_$cont) {
//...
})(function (_error) {
})
function test() {
'use strict';
f(obtain());
}
Actual
function test() {
var _$err;
'use strict';
f(function () {
_$err = arguments[0];
if (_$err)
throw _$err;
});
}
var test = function(callback) {
console.log('called');
callback(null);
}
function main() {
try {
test(obtain(a));
} catch (e) {
console.log('error by obtain', e);
}
throw 'a';
}
try {
main();
} catch (e) {
console.log('error by main', e);
}
Expected:
called
error by main a
Actual:
called
error by obtain a
error by obtain a
error by main a
When mix using obtain
and return
in one function, it seems it can't work properly.
For an example code like
'use continuation'
getElement = (callback) ->
callback null, 1
checkElement = (number) ->
getElement obtain standard
number is standard
console.log checkElement 1
console.log checkElement 2
Which is aimed to check if an element is 1
After just
$ coffee -c a.coffee
it produce
// Generated by CoffeeScript 1.6.2
(function() {
'use continuation';
var checkElement, getElement;
getElement = function(callback) {
return callback(null, 1);
};
checkElement = function(number) {
getElement(obtain(standard));
return number === standard;
};
console.log(checkElement(1));
console.log(checkElement(2));
}).call(this);
The code seems shall work though. However, after using
$ continuation a.coffee -o abc.js
It produce
(function () {
var checkElement, getElement;
'use continuation';
getElement = function (callback) {
return callback(null, 1);
};
checkElement = function (number) {
var _$err, standard;
getElement(function () {
_$err = arguments[0];
standard = arguments[1];
if (_$err)
throw _$err;
return number === standard;
});
};
console.log(checkElement(1));
console.log(checkElement(2));
}.call(this));
/* Generated by Continuation.js v0.1.2 */
//@ sourceMappingURL=abc.js.map
Which seems wrong for that return
is in the function getElement
's callback. Thus checkElement
returns nothing.
More, when use
node abc.js
it gives
undefined
undefined
other than expected
true
false
Even if another exception is triggered
suppose the source tree is
/path/to/root
|
+--sub
| |
| +--main.js
|
+--node_modules
|
+--aaa
|
+--index.js
and main.js
is
require('aaa');
console.log('sub/main.js');
while index.js
is
console.log("this is aaa");
and below is the issue
% pwd
/path/to/root
% continuation sub/main.js
module.js:340
throw err;
^
Error: Cannot find module 'aaa'
at Function.Module._resolveFilename (module.js:338:15)
at Function.Module._load (module.js:280:25)
at Module.require (module.js:364:17)
at require (module.js:380:17)
at Object.<anonymous> (sub/main.js:1:69)
at Module._compile (module.js:456:26)
at executeTopModule (/usr/local/lib/node_modules/continuation/lib/cli.js:131:14)
at Object.exports.main (/usr/local/lib/node_modules/continuation/lib/cli.js:86:7)
at Object.<anonymous> (/usr/local/lib/node_modules/continuation/bin/continuation:3:5)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:906:3
% continuation ./sub/main.js
this is aaa
sub/main.js
% continuation --version
0.1.5
% node -v
v0.10.32
js code:
var read = function() {
var content = 'abc';
fs.readFile('a.txt', 'utf-8', cont(err, content));
};
will be compiled to
var read;
read = function () {
var content, err;
content = 'abc';
fs.readFile('a.txt', 'utf-8', function (arguments, _$param1, _$param2) {
err = _$param1;
content = _$param2;
}.bind(this, arguments));
};
the variable content
is changed.
So, the correct compiled result is that:
var read;
read = function () {
var content;
content = 'abc';
fs.readFile('a.txt', 'utf-8', function (arguments, _$param1, _$param2) {
var content, err;
err = _$param1;
content = _$param2;
}.bind(this, arguments));
};
What do you think about it?
continuation/examples/clock/clock.c.js
Line 19 in 73b9552
text = (h >= 10 ? h : '0' + h) + ':' + (h >= 10 ? m : '0' + m) + ':' + (h >= 10 ? s : '0' + s);
should be:
text = (h >= 10 ? h : '0' + h) + ':' + (m >= 10 ? m : '0' + m) + ':' + (s >= 10 ? s : '0' + s);
both CoffeeScript and LessCSS has such "all-in-one" js file, runnable in any enviromement (not only node-js)
Cool project! I really like how readable the generated code is (especially compared to Streamline, Tame etc). It'd be nice if it also supported source maps. Those are files that map locations in the source to locations in the generated code to make debugging easier.
Escodegen already supports source maps, so most of the hard work should hopefully be already done. But when the parser converts the AST nodes using syntax.factory, their "loc" property is lost. They need to keep it for source maps to work. (As for new nodes that weren't in the original AST, I guess their "loc" should just be null.)
Perhaps that's the only required change (well, plus adding the CLI option itself), but I'm not sure.
Hi, great job there,
is it possible to implement a trasformation which makes it possible to pass return values on the left side of the expression?
example:
var value = async_call(arg1, arg2, obtain);
Do you think it's hard to implement? Any suggestion about that?
Thank you very much!
The code in this function:
function t() {
var csvPath = '/tmp/contacts.csv';
parser = new ContactDocParser(csvPath, cont());
}
gets compiled to
function t() {
var csvPath = '/tmp/contacts.csv';
parser = new ContactDocParser(csvPath, cont());
}
/* Generated by Continuation.js v0.1.4 */
It just ignores it.
When I remove the new
statement, it does replace the cont()
continuation/examples/clock/clock.js
Line 19 in 73b9552
var text =
((h >= 10) ? h : "0" + h) + ":" +
((h >= 10) ? m : "0" + m) + ":" +
((h >= 10) ? s : "0" + s);
should be:
var text =
((h >= 10) ? h : "0" + h) + ":" +
((m >= 10) ? m : "0" + m) + ":" +
((s >= 10) ? s : "0" + s);
this.name = 'outter';
function inner() {
if (true) {
setTimeout(cont(), 100);
// Correct context here.
console.log(this.name);
}
// Wrong context here.
console.log(this.name);
};
var o = {
name: 'inner',
inner: inner
};
console.log(this.name);
o.inner();
Which compiles to
var o;
this.name = 'outter';
function inner() {
(function (_$cont) {
if (true) {
setTimeout(function (arguments) {
console.log(this.name);
_$cont();
}.bind(this, arguments), 100);
} else {
_$cont();
}
}.bind(this)(function (_$err) {
if (_$err !== undefined)
return _$cont(_$err);
console.log(this.name);
}));
}
o = {
name: 'inner',
inner: inner
};
console.log(this.name);
o.inner();
/* Generated by Continuation.js v0.1.5 */
This part of the compiled result got the wrong context:
function (_$err) {
if (_$err !== undefined)
return _$cont(_$err);
console.log(this.name);
}
this.name = 'outter';
function inner() {
if (true) {
setTimeout(cont(), 100);
console.log(this.name);
}
};
var o = {
name: 'inner',
inner: inner
};
console.log(this.name);
o.inner();
The output result shoud be
outter
inner
But we got
outter
undefined
var o;
this.name = 'outter';
function inner() {
(function (_$cont) {
if (true) {
setTimeout(function (arguments) {
console.log(this.name);
_$cont();
}.bind(this, arguments), 100);
} else {
_$cont();
}
}(function (_$err) {
if (_$err !== undefined)
return _$cont(_$err);
}));
}
o = {
name: 'inner',
inner: inner
};
o.inner();
console.log(this.name);
/* Generated by Continuation.js v0.1.4 */
I think the wrong context of function (_$cont)
causes this bug, I will fix it soon.
this.name = 'outter';
function inner() {
var a = "a";
switch (a) {
case "a":
setTimeout(cont(), 100);
// wrong content here
console.log(this.name);
break;
}
};
var o = {
name: 'inner',
inner: inner
};
console.log(this.name);
o.inner();
var o;
this.name = 'outter';
function inner() {
var a;
a = 'a';
(function (_$cont) {
function case_0(_$cont) {
setTimeout(function (arguments) {
console.log(this.name);
_$cont();
}.bind(this, arguments), 100);
}
switch (a) {
case 'a':
return case_0(_$cont);
}
}.bind(this)(function () {
}));
}
o = {
name: 'inner',
inner: inner
};
console.log(this.name);
o.inner();
/* Generated by Continuation.js v0.1.5 */
As you can see it we defined a function called case_0
here, but we haven't bind this pointer correctly.
I think we should add case_0 = case_0.bind(this);
to make case_0(_$cont);
get the correct context.
In other words, the compiler does not check whether the arguments to cont() etc.
are assignable. Here is an example:
// Source
async(stuff, cont(err, result+4));
bar(result+1);
// Output
var err;
async(stuff, function () {
err = arguments[0];
(result + 4) = arguments[1];
bar(result + 1);
});
/* Generated by Continuation.js v0.1.3 */
This is always an error. You cannot assign to (result + 4). Actually, you can also
write 4
instead result + 4
and you will get 4 = arguments[1];
!
hello can you answer me?
My project does not use a module loading system such as RequireJS or AMD, so I am a bit of a loss at how to run the compiler from the browser.
Can the continuation compiler be run from the browser? If so, is there an example for that?
test();
setTimeout(cont(), 100);
function test() {
console.log('test');
}
Which compiles to
test();
setTimeout(function (arguments) {
function test() {
console.log('test');
}
}.bind(this, arguments), 100);
This is incorrect.
I can't seem to work out how to do the equivalent of
async.each https://github.com/caolan/async#eacharr-iterator-callback
or
async.map https://github.com/caolan/async#maparr-iterator-callback
Is there any chance of an example of something like that? These are the only use cases keeping my project dependant on the async library and I would love to remove that dependency if possible as I prefer the simplicity of the syntax when using your library and the robust error trapping.
Block statements disappear.
{ x++; }
alert(cont());
is compiled to:
alert(function () {
});
/* Generated by Continuation.js v0.1.1 */
I'm working on a fix (and refactoring). A pull request should be ready soon.
Are continuations portable? Specifically, could a continuation be serialized and sent across the network to be resumed elsewhere?
If that were possible you could potentially do some very interesting stuff with breaking down the barrier between client and server. I experimented with this idea a few years ago using Scala (which has a compiler plugin that does something similar to this project), you can learn more about these ideas here: http://swarmframework.org/
// use continuation to create a function which can synced waiting for 1 second
var stop = function() {
setTimeout(continuation(), 1000);
};
console.log("start");
stop();
console.log("end");
// Readme:
// Due to this, I suggest to consider implement coroutine for javascript.
// we can implement "continuation" by using yield initiatively and resume by callback function.
foo(cont());
d = (new a.b).c();
and the result is
foo(function (arguments) {
d = new a.b().c();
}.bind(this, arguments));
/* Generated by Continuation.js v0.1.7 */
var console = require('console');
function async (ret) {
ret(null, [1, 2, 3, 4, 5, 6]);
}
try {
async(obtain(a));
for (var i in a)
if (i % 2 == 0) continue;
else console.log(a[i]);
} catch (err) {
console.log(err);
}
The iterator doesn't go forward with continue
.
For loop should not be converted into while.
The following code doesn't work:
function callme(cb) {
cb();
}
function main(cb) {
try {
callme(cont());
var result = 0;
[10, 20, 30].forEach(function (i) {
result += i;
throw Error('oh noes');
});
}
catch (e) {
cb(e);
return;
}
cb(null, result);
}
main(function (e,r) {
console.log('main() finished with', e, r);
})
Output:
main() finished with [Error: oh noes] undefined
main() finished with [Error: oh noes] undefined
main() finished with [Error: oh noes] undefined
main() finished with null 60
main() "finishes" multiple times (i.e. the console.log callback is called more than one time), which should never happen.
It seems continuation.js messes with the forEach callback and tries to "callback-ize" it even though forEach is always serial (and the function body doesn't contain "cont" nor "obtain"). Perhaps the correct behavior with nested functions is to leave them alone (and transform them separately, depending on whether they use cont) -- though that's just a guess and I'm not sure if that wouldn't break other stuff.
This project is still alive? Can you please add support for new version JS languege.
Now, if continuation meet token like () => {} it obtain error:
Error: Line 15: Unexpected token => in undefined
at Object.parse (G:\ShepherdProject\cont8doo\node_modules\continuation\lib\parser.js:18:11)
at Object.exports.compile (G:\ShepherdProject\cont8doo\node_modules\continuation\continuation.js:29:20)
at compile (G:\ShepherdProject\cont8doo\node_modules\continuation\lib\cli.js:166:25)
The following example:
errorinAsync(null, obtain());
function errorinAsync(options, callback){
options = options || {};
setTimeout(function(){
return callback(new Error("This could be caught"));
},1000);
}
Does not run, the errorInAsync function is included in the wrong scope in the compiled output:
var _$err;
errorinAsync(null, function () {
_$err = arguments[0];
if (_$err)
throw _$err;
function errorinAsync(options, callback) {
options = options || {};
setTimeout(function () {
return callback(new Error('This could be caught'));
}, 1000);
}
});
Pseudocode:
parallel (
while(var i=0;i<files.length;i++){
fs.readFile(files[i],obtain(bf))
});
function test(ret) {
throw undefined;
}
try {
test();
} catch(e) {
console.log('Exception1: ', e); // prints Exception1: undefined
}
try {
test(cont(res));
} catch(e) {
console.log('Excpetion2: ',e); // Never prints!
}
Substituting undefined
for 42
will print 42
in both places.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.