Asynchronicity and concurrency in general

21 बार देखा गया
नहीं पढ़े गए पहले मैसेज पर जाएं

Kevin Dangoor

नहीं पढ़ी गई,
29 मई 2009, 9:35:52 am29/5/09
ईमेल पाने वाला serv...@googlegroups.com
Following up from the async module thread... As far as I can tell (and
someone please correct me if I'm wrong), JavaScript doesn't really
have a way to make asynchronous operations look synchronous. *Some* JS
interpreters have yield-based coroutines that allow you to make async
operations more pleasant without completely ignoring the fact that
they're async. Unfortunately, yield is not part of the ECMAScript
standard (not even ES5).

Given that you can't hide away asynchronicity, that means there has to
be a conscious choice about whether the APIs we make are async or not.
A function cannot reasonably be both. This has come up before, but I
wanted to bring it up again in the broader context to see if there are
other ideas lurking. Promises/Deferreds are great and all, but I don't
want to hear about how E has a great syntax for handling them... if
you can get ES-Harmony to add nice syntax, then let's talk :)

I am inclined to think that the ServerJS API will not be competitive
if it is purely async, as it will be competing with Python, Ruby, PHP
and Java which don't make people jump through hurdles. We've largely
been talking about require() and simple stub APIs. When you get into
database driven webapps, it gets nasty:

var d = require("mysql");
d.addCallback(function(mysql) {
d = mysql.connect("localhost");
d.addCallback(function(connection) {
d = connection.execute("select * from users where id=?", 27);
function handleResult(d, row) {
// do something with the row
if (d) {
d.addCallback(handleResult);
return d;
}
}
d.addCallback(handleResult);
});
});

vs. something more like:

new Worker(function() {
var mysql = require("mysql");
var connection = mysql.connect("localhost");
var resultset = connection.execute("select * from users where id=?", 27);
resultset.forEach(function (row) {
// do something with the row
});
});

While this example is contrived, it's not that far off given the
syntax available in ECMAScript.

Given how we've been proceeding thus far, I think most people here are
in agreement that a synchronous API is the way to go. Does it make
sense to offer a standard, clearly marked async API for many
operations? If not that, does it make sense to at least add a
Promise/Deferred sort of API? Or do we push everything toward using
workers? (Do we care about the implications on browser access to
modules?)

Concurrency is a big deal (at least I think so), so I just want to get
some opinions on how people think the API should evolve given the
constraints we have with the language.

Kevin

--
Kevin Dangoor

work: http://labs.mozilla.com/
email: k...@blazingthings.com
blog: http://www.BlueSkyOnMars.com

Kris Zyp

नहीं पढ़ी गई,
29 मई 2009, 10:00:12 am29/5/09
ईमेल पाने वाला serv...@googlegroups.com

Kevin Dangoor wrote:
> Following up from the async module thread... As far as I can tell (and
> someone please correct me if I'm wrong), JavaScript doesn't really
> have a way to make asynchronous operations look synchronous. *Some* JS
> interpreters have yield-based coroutines that allow you to make async
> operations more pleasant without completely ignoring the fact that
> they're async. Unfortunately, yield is not part of the ECMAScript
> standard (not even ES5).
>

We certainly can't rely on generators (at least not right now) for
ServerJS API specs, but we shouldn't ignore the fact that probably well
over 80% ssjs developers are on platforms that do support generators and
can leverage that if we build our APIs intelligently. This isn't the
client side, when you develop an app, you only need to develop for one
platform, so the majority of ssjs devs can certainly leverage generators.

If this is in a request handler how are you going to get the response
back to the server? You'll still end up needing a callback in the end
and having someway to deliver the response the HTTP handler.
Furthermore, with workers you are now stuck with only passing strings.
Asynchronicity and concurrency are radically different concepts, you
don't "solve" asynchronicity by throwing concurrency at it.


> While this example is contrived, it's not that far off given the
> syntax available in ECMAScript.
>
> Given how we've been proceeding thus far, I think most people here are
> in agreement that a synchronous API is the way to go.

Yes, in general we should provide sync APIs and we should be designing
with the understanding that the sync APIs will be the most commonly used
form.

> Does it make
> sense to offer a standard, clearly marked async API for many
> operations?

Yes, the asynchronous APIs are vary valuable for efficient handling of
operations on long-running IO interactions, when needed. They certainly
will be used less frequently, but are important nonetheless.

> If not that, does it make sense to at least add a
> Promise/Deferred sort of API?

Yes, of course :).


> Or do we push everything toward using
> workers?

I like workers, but workers and promises are radically different. The
use cases are completely different. We need both, and I don't think
ServerJS should try to dictate the use of one over the other.

Kris

Tyler Close

नहीं पढ़ी गई,
29 मई 2009, 10:26:38 am29/5/09
ईमेल पाने वाला serv...@googlegroups.com
On Fri, May 29, 2009 at 6:35 AM, Kevin Dangoor <dan...@gmail.com> wrote:
> Given that you can't hide away asynchronicity, that means there has to
> be a conscious choice about whether the APIs we make are async or not.
> A function cannot reasonably be both.

Well, you might be able to have your cake and eat it too. For example:

> new Worker(function() {
> var mysql = require("mysql");
> var connection = mysql.connect("localhost");
> var resultset = connection.execute("select * from users where id=?", 27);
> resultset.forEach(function (row) {
> // do something with the row
> });
> });

becomes:

var mysql = require("mysql");
var connection = mysql.connect("localhost");

var resultset = mysql.execute(connection, "select * from users where id=?", 27);
mysql.forEach(resultset, function (row) {


// do something with the row
});

The 'mysql' object is a local facade object that does all the
asynchronous messaging for the client. This facade generates promises,
such as the 'connection' and 'resultset', but the client just treats
these as opaque tokens to be passed back to methods of the 'mysql'
facade object. If the 'mysql' object has synchronous access to its
dataset, then this code can run synchronously, with all callbacks to
the row handler being done immediately; otherwise, the row handler
gets its rows in drips as they arrive.

--Tyler

--
"Waterken News: Capability security on the Web"
http://waterken.sourceforge.net/recent.html

Donny Viszneki

नहीं पढ़ी गई,
31 मई 2009, 4:16:32 pm31/5/09
ईमेल पाने वाला serv...@googlegroups.com
On Fri, May 29, 2009 at 9:35 AM, Kevin Dangoor <dan...@gmail.com> wrote:
> As far as I can tell (and
> someone please correct me if I'm wrong), JavaScript doesn't really
> have a way to make asynchronous operations look synchronous. *Some* JS
> interpreters have yield-based coroutines that allow you to make async
> operations more pleasant without completely ignoring the fact that
> they're async. Unfortunately, yield is not part of the ECMAScript
> standard (not even ES5).

The point is moot. Generators are in fact syntactical sugar for
coroutines, and coroutines in turn are sugar for Objects but with the
added caveat that local variables are invisible to the outside world.
(If you are picky, you may also point out that the underlying
implementation in any particular Javascript interpreter is probably
not built on top of a slim "kernel language," but rather really do
provide native implementations of these objects. Personally I am a fan
of elegance and code consolidation, so I would have built these
features on top of a slim kernel language, but depending on the
particular technical circumstances each development team has to work
with, idealism may be trumped by practical concerns like performance.)

I have just written up a rather thorough demonstration of implementing
coroutines and generators for any Javascript interpreter which doesn't
support them at the language level.


/* Form 1: the most convenient syntax */
function range1(start, stop, step) {
step = step || 1;
while (start < stop) {
yield start;
start += step;
}
}
/* Demonstrate */
print("range1");
for (var i in range1(0, 3))
print(i);

/* Form 2: no fancy iterator syntax */
/* Demonstrate */
print("\nrange1 again");
var g = range1(0, 3);
try {
while (1)
print(g.next());
} catch (ex) {
if (!ex instanceof StopIteration)
throw e;
}

/* Form 3: Generators are really just closures with a few extras that closures
don't have (next() member function.) Closures are really just Objects where
local variables are like hidden member variables, and some complex control flow.
*/
function range2(start, stop, step) {
this.start = start;
this.stop = stop;
this.step = step || 1;
}
range2.prototype.next = function() {
if (this.start < this.stop) {
var rval = this.start;
this.start += this.step;
return rval;
}
throw StopIteration;
}
/* Demonstrate */
print("\nrange2");
var g = new range2(0, 3);
try {
while (1)
print(g.next());
} catch (ex) {
if (!ex instanceof StopIteration)
throw e;
}

/* Complex control flow: The "range" example is really too simple to showcase
the potential for complex control flow statements. The absence of a switch()
with cases that can flow into each other, or goto statements, makes the control
flow logic even more convoluted. Check out a C lib called "protothreads" if
you're interested in seeing what light-weight coroutines look like in C without
Apple's "Block" work which gives LLVM's C compiler proper coroutines. To touch
on the surface of complex control flow, but not get too deep into it, here is a
generator I used for some image processing code I wrote in Python. I love these
kind of coroutines because they're highly composable and very expressive. */
print("\nperimeter1");
function perimeter1(x1, y1, x2, y2) {
for (var x=x1; x<=x2; x++) yield [x, y1];
for (var y=y1+1; y<=y2; y++) yield [x2, y];
for (var x=x2-1; x>=x1; x--) yield [x, y2];
for (var y=y2-1; y>y1; y--) yield [x1, y];
}
/* Demonstrate */
for (var pos in perimeter1(0, 0, 2, 2)) {
print(pos);
}

/* Here is the more convoluted version that doesn't have language-level support
for coroutines. */
function perimeter2(x1, y1, x2, y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.phase = 0;
}
perimeter2.prototype.next = function() {
while (1)
switch (this.phase) {
case 0:
this.x = this.x1;
if (this.x <= this.x2) {
this.phase++;
return [this.x, this.y1];
}
case 1:
if (++this.x <= this.x2)
return [this.x, this.y1];
this.phase++;
this.y = this.y1+1;
return [this.x2, this.y];
case 2:
if (++this.y <= this.y2)
return [this.x2, this.y];
this.phase++;
this.x = this.x2-1;
return [this.x, this.y2];
case 3:
if (--this.x >= this.x1)
return [this.x, this.y2];
this.phase++;
this.y = this.x2-1;
return [this.x1, this.y];
case 4:
if (--this.y > this.y1)
return [this.x1, this.y];
this.phase++;
case 5:
throw StopIteration;
}
}
/* Demonstrate */
print("\nperimeter2");
var g = new perimeter2(0, 0, 2, 2);
try {
while (1)
print(g.next());
} catch (ex) {
if (!ex instanceof StopIteration)
throw e;
}

I'll probably post this on my blog in a bit, so if you see any
mistakes, feel free to point them out to me, but running this program
under a recent build of SM seems to yield the results I want:

donny@teamspace:~$ ./tracemonkey/js/src/js yield-impl.js
range1
0
1
2

range1 again
0
1
2

range2
0
1
2

perimeter1
0,0
1,0
2,0
2,1
2,2
1,2
0,2
0,1

perimeter2
0,0
1,0
2,0
2,1
2,2
1,2
0,2
0,1

Ciao!

--
http://codebad.com/

Daniel Friesen

नहीं पढ़ी गई,
31 मई 2009, 4:55:53 pm31/5/09
ईमेल पाने वाला serv...@googlegroups.com
Donny Viszneki wrote:
> On Fri, May 29, 2009 at 9:35 AM, Kevin Dangoor <dan...@gmail.com> wrote:
>
>> As far as I can tell (and
>> someone please correct me if I'm wrong), JavaScript doesn't really
>> have a way to make asynchronous operations look synchronous. *Some* JS
>> interpreters have yield-based coroutines that allow you to make async
>> operations more pleasant without completely ignoring the fact that
>> they're async. Unfortunately, yield is not part of the ECMAScript
>> standard (not even ES5).
>>
>
> The point is moot. Generators are in fact syntactical sugar for
> coroutines, and coroutines in turn are sugar for Objects but with the
> added caveat that local variables are invisible to the outside world.
> (If you are picky, you may also point out that the underlying
> implementation in any particular Javascript interpreter is probably
> not built on top of a slim "kernel language," but rather really do
> provide native implementations of these objects. Personally I am a fan
> of elegance and code consolidation, so I would have built these
> features on top of a slim kernel language, but depending on the
> particular technical circumstances each development team has to work
> with, idealism may be trumped by practical concerns like performance.)
>
> ...
> Ciao!
>
>
This makes me think of a bit in the JSGI/Jack thread.

Do we really want to stick with forEach, why not .next()? The thing is
that generators have a .next() on them. And as Donny notes, they can be
used whether the platform supports the syntax sugar or not.
We can put a next on an array, or we could conditionally use .next or
forEach in JSGI (so as to not modify the Array's prototype). But we
can't put forEach on a generator. Not that I know of with Generator not
being a global object.

~Daniel Friesen (Dantman, Nadir-Seen-Fire) [http://daniel.friesen.name]

Kris Kowal

नहीं पढ़ी गई,
31 मई 2009, 5:16:58 pm31/5/09
ईमेल पाने वाला serv...@googlegroups.com
On Sun, May 31, 2009 at 1:55 PM, Daniel Friesen
<nadir.s...@gmail.com> wrote:
> This makes me think of a bit in the JSGI/Jack thread.
>
> Do we really want to stick with forEach, why not .next()? The thing is
> that generators have a .next() on them. And as Donny notes, they can be
> used whether the platform supports the syntax sugar or not.
> We can put a next on an array, or we could conditionally use .next or
> forEach in JSGI (so as to not modify the Array's prototype). But we
> can't put forEach on a generator. Not that I know of with Generator not
> being a global object.

This is true. In Chiron, I've built an entire system around iterators
that are implemented entirely in terms of the "next" function and
"StopIteration". That being said, "forEach" can be implemented in
terms of "next", as it is in Python.

This is Chiron's forEach loop. It supports both continue and break
semantics using StopIteration and SkipIteration, and handles outer
loop continue and break using iteration-specific overrides begotten of
those exceptions.

exports.forEach = function (values, relation, collect) {
var results;
if (collect)
results = list.List();
var iteration = exports.iter(values);
try {
while (true) {
try {
var result = relation.call(this, iteration.next());
if (collect) {
results.push(result);
}
} catch (exception) {
if (
type.isInstance(exception, boot.SkipIteration) ||
iteration.SkipIteration &&
exception === iteration.SkipIteration()
) {
} else {
throw exception;
}
}
}
} catch (exception) {
if (
type.isInstance(exception, boot.StopIteration) ||
iteration.StopIteration &&
exception === iteration.StopIteration()
) {
} else {
throw exception;
}
}
if (collect)
return results;
return values;
};

Traditionally, the third argument would be used for the "this" object,
but I just pass it through, so you can override the call context with
either bind or exports.forEach.call(context, iterable, relation); The
"collect" term is used so that "map" or "each" can be implemented
trivially in terms of this method.

In any case, forEach is provided as a convenience on all iterables in
Chiron, so Chiron iterations and lists both work fine in Jack.

Kris Kowal

Ash Berlin

नहीं पढ़ी गई,
31 मई 2009, 5:58:59 pm31/5/09
ईमेल पाने वाला serv...@googlegroups.com

On 31 May 2009, at 22:16, Kris Kowal wrote:

> } catch (exception) {
> if (
> type.isInstance(exception, boot.StopIteration) ||
> iteration.StopIteration &&
> exception === iteration.StopIteration()
> ) {
> } else {
> throw exception;
> }

Btw, not sure how this behaves, but in SpiderMonkey you do:

throw StopIteration

not

throw new StopIteration();


Will this mess up your isInstance checks?

Kris Kowal

नहीं पढ़ी गई,
31 मई 2009, 6:24:20 pm31/5/09
ईमेल पाने वाला serv...@googlegroups.com
On Sun, May 31, 2009 at 2:58 PM, Ash Berlin <ash_g...@firemirror.com> wrote:
> Btw, not sure how this behaves, but in SpiderMonkey you do:
>
>     throw StopIteration
>
> not
>
>     throw new StopIteration();

I haven't revised it for compatibility with new ES features. It works
in old browsers, but doesn't interact yet with the new StopIteration
construct. It's on my todo list.

Kris Kowal

Daniel Friesen

नहीं पढ़ी गई,
31 मई 2009, 6:24:37 pm31/5/09
ईमेल पाने वाला serv...@googlegroups.com
Is that not throw StopIteraton();? remember there is a difference. Error
and it's subclasses follow a pattern where `new ErrorError` `new
ErrorError();` and `ErrorError();` all do the same thing, returning a
new instance.

side note; I actually kind of like this pattern, frankly a lot of the
MonkeyScript classes are going to follow this same pattern, namely File
so that the quick one liners are possible without extra surrounding
syntax. And it solves the age old issue of forgetting a new.

~Daniel Friesen (Dantman, Nadir-Seen-Fire) [http://daniel.friesen.name]

Ash Berlin

नहीं पढ़ी गई,
31 मई 2009, 6:36:21 pm31/5/09
ईमेल पाने वाला serv...@googlegroups.com

On 31 May 2009, at 23:24, Daniel Friesen wrote:

>
> Is that not throw StopIteraton();? remember there is a difference.
> Error
> and it's subclasses follow a pattern where `new ErrorError` `new
> ErrorError();` and `ErrorError();` all do the same thing, returning a
> new instance.

No, its definitely 'throw StopIteration'.

> StopIteration
[object StopIteration]
> StopIteration()
ERROR: Could not evaluate script: exception `TypeError: StopIteration
is not a function' at [typein]:2
>

(Not that this is in the ES spec anymore, but when it was this was
what the spec said)

Daniel Friesen

नहीं पढ़ी गई,
31 मई 2009, 6:50:30 pm31/5/09
ईमेल पाने वाला serv...@googlegroups.com
Ok, it looks like StopIteration is yet another funky new pattern.
Or rather, not a pattern. I don't see how it's possible to do with a
JavaScript trick.

SpiderMonkey:
new StopIteration; // fails
StopIteration(); // fails
StopIteration === StopIteration; // true
StopIteration instanceof StopIteration; // true

StopIteration is some funky constant which equals itself, cannot be
instanced or called, but is an instance of itself despite not being a class.

~Daniel Friesen (Dantman, Nadir-Seen-Fire) [http://daniel.friesen.name]

Ash Berlin wrote:

Tom Robinson

नहीं पढ़ी गई,
31 मई 2009, 7:17:50 pm31/5/09
ईमेल पाने वाला serv...@googlegroups.com


Allowing two different APIs is not ideal, since every piece of
middleware would have to check for both.

As for next() vs. forEach(), I'm not sure, I'll have to think about it.

-Tom

Kris Kowal

नहीं पढ़ी गई,
31 मई 2009, 7:42:56 pm31/5/09
ईमेल पाने वाला serv...@googlegroups.com
On Sun, May 31, 2009 at 4:17 PM, Tom Robinson <tlrob...@gmail.com> wrote:
> As for next() vs. forEach(), I'm not sure, I'll have to think about it.

I don't think it's a choice between next() and forEach(), but rather
iterator() and forEach(). It needs to support Array as a basis, and
Arrays are conceptually "iterable", not "iterators". Iterators, on
the other hand, should be iterable, so we should support the
"iterable" interface for content. Presently these interfaces are in
flux, so we should take a position of leadership.

In the following notation "<" means "is a duck-type of this interface".

Iterable
forEach(block)
iterator()
Iterator < Iterable
next()
Array < Iterable
TextReaderStream < Iterator

Kris Kowal

Donny Viszneki

नहीं पढ़ी गई,
31 मई 2009, 11:50:58 pm31/5/09
ईमेल पाने वाला serv...@googlegroups.com
On Sun, May 31, 2009 at 4:55 PM, Daniel Friesen
<nadir.s...@gmail.com> wrote:
> Do we really want to stick with forEach, why not .next()? The thing is
> that generators have a .next() on them. And as Donny notes, they can be
> used whether the platform supports the syntax sugar or not.
> We can put a next on an array, or we could conditionally use .next or
> forEach in JSGI (so as to not modify the Array's prototype). But we
> can't put forEach on a generator. Not that I know of with Generator not
> being a global object.

For what it's worth, on Spidermonkey:

donny@teamspace:~$ ./tracemonkey/js/src/js
js> function range(end){ for(var i=0; i<end; i++) yield i }
js> two = range(2)
[object Generator]
js> two.__proto__.sweet = function(){return 'girls'}
function () {
return "girls";
}
js> two.sweet()
girls

I wouldn't advise doing this in production code ;)

Even if you could *not* do that, however, you can easily wrap
generators, arrays, and objects, giving them both generator *and*
forEach() powers! (So long as the user is willing to "decorate" his
quasi-generator instantiations with the generator wrapper.) Here is an
example implementation:

function myGenerator() {
yield 4;
yield 1;
yield 2;
}

print("== GENERATORS ==");

/* Make anything into a generator */
function $G(x) {
if (x instanceof Array) {
/* For arrays */
for each(var value in x) {
yield value;
}
} else {
/* For all other objects */
for(var value in x)
yield value;
}
}

/* Demonstrate arrays */
print("\nArray");
for(var i in $G([4,1,2]))
print(i);

/* Demonstrate generators */
print("\nGenerator");
for(var i in $G(myGenerator()))
print(i);

/* Demonstrate objects */
print("\nObject");
for(var i in $G({'four':0, 'one':1, 'two':2}))
print(i);

print("\n== FOR EACH ==");

/* Give anything .forEach() */
function $FE(x) {
/* Already has forEach */
if ('function' == typeof x.forEach)
return x;

/* Add forEach to generator */
if ('function' == typeof x.next) {
var realized_array = [];
var index = 0;
for (var value in x)
realized_array.push(value);
return new Object({'forEach': function(f) {
for (var index in realized_array)
f(realized_array[index], index, realized_array);
}});
}

/* Add forEach to Array */
if (x instanceof Array) {
return new Object({'forEach': function(f) {
Array.prototype.forEach.apply(x, [f]);
}});
}

/* Add forEach to object */
var realized_array = [];
var index = 0;
for each(var value in x)
realized_array.push(value);
return new Object({'forEach': function(f) {
for (var index in realized_array)
f(realized_array[index], index, realized_array);
}});
}

/* Demonstrate arrays */
print("\nArrays");
[4,1,2].forEach(print);
print("");
$FE([4,1,2]).forEach(print);

/* Demonstrate generators */
print("\nGenerators");
$FE(myGenerator()).forEach(print);

/* Demonstrate objects */
print("\nObjects");
$FE({'four': 4, 'one': 1, 'two': 2}).forEach(print);

FWIW, I see no reason why both $G() and $FE() could both be combined
into the same method, but for simplicity I kept them separate. I'm not
sure what the forEach() member method should do for objects, so I had
it iterator over objects' member values rather than their
corresponding keys/indexes/property-names.

Here is the output:

donny@teamspace:~$ ./tracemonkey/js/src/js generator-wrapper.js;
== GENERATORS ==

Array
4
1
2

Generator
4
1
2

Object
four
one
two

== FOR EACH ==

Arrays
4 0 4,1,2
1 1 4,1,2
2 2 4,1,2

4 0 4,1,2
1 1 4,1,2
2 2 4,1,2

Generators
4 0 4,1,2
1 1 4,1,2
2 2 4,1,2

Objects
4 0 4,1,2
1 1 4,1,2
2 2 4,1,2


--
http://codebad.com/

सभी प्रषकों को उत्तर दें
लेखक को उत्तर दें
आगे भेजें
0 नया मैसेज