MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation...
Transcript of MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation...
![Page 1: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/1.jpg)
An Application Perspective
MANAGING ASYNCHRONY
![Page 2: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/2.jpg)
TYPES OF ASYNCHRONY
![Page 3: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/3.jpg)
DETERMINISTIC ORDER.
![Page 4: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/4.jpg)
(I/O)
![Page 5: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/5.jpg)
NON-DETERMINISTIC ORDER.
![Page 6: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/6.jpg)
(USER EVENTS)
![Page 7: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/7.jpg)
THESE ARE DIFFERENT.Because both are "events", we reach for the same tools when building abstractions.
![Page 8: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/8.jpg)
APPLICATION CODE SHOULD USUALLY NOT BE ASYNC.
In some cases, apps will define their own abstractions for the asynchrony. In a few cases (chat servers), it may be appropriate. In most cases, we can abstract away callbacks from application logic through abstraction.
![Page 9: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/9.jpg)
CONTEXT.
![Page 10: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/10.jpg)
fs.readFile("/etc/passwd", function(err, data) { if (err) { throw err; } console.log(data); });
ASYNC CODE.
SchedulerInitial ProgramPrimitive StatusPrimitive ValueError ConditionApplication Callback
![Page 11: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/11.jpg)
var value;callbacks = new Map;callbacks.set(function() { return true; }, program); while(callbacks.length) { callbacks.forEach(function(pollStatus, callback) { if (value = pollStatus()) { callback(value); } }); sleep(0.1);}
SCHEDULER.
SchedulerInitial ProgramPrimitive StatusPrimitive ValueError ConditionApplication Callback
![Page 12: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/12.jpg)
SCHEDULER.
true, program
callbacks
![Page 13: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/13.jpg)
SCHEDULER.callbacks
poll, callback
poll, callback
poll, callback
poll, callback
poll, callback
![Page 14: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/14.jpg)
SCHEDULER.
![Page 15: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/15.jpg)
fs.readFile("/etc/passwd", function(err, data) { if (err) { throw err; } console.log(data); });
ASYNC CODE.
SchedulerInitial ProgramPrimitive StatusPrimitive ValueError ConditionApplication Callback
true, program
![Page 16: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/16.jpg)
fs.readFile("/etc/passwd", function(err, data) { if (err) { throw err; } console.log(data); });
ASYNC CODE.
SchedulerInitial ProgramPrimitive StatusPrimitive ValueError ConditionApplication Callback
fileReady, callback
![Page 17: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/17.jpg)
fs.readFile("/etc/passwd", function(err, data) { if (err) { throw err; } console.log(data); });
ASYNC CODE.
SchedulerInitial ProgramPrimitive StatusPrimitive ValueError ConditionApplication Callback
![Page 18: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/18.jpg)
$ man select
<snip>Select() examines the I/O descriptor sets whose addresses are passed in readfds, writefds, and errorfds to see if some of their descriptors are ready for reading, ...<snip>
PRIMITIVE STATUS.
SchedulerInitial ProgramPrimitive StatusPrimitive ValueError ConditionApplication Callback
![Page 19: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/19.jpg)
fcntl(fd, F_SETFL, flags | O_NONBLOCK);read(fd, buffer, 100);
PRIMITIVE VALUE.
SchedulerInitial ProgramPrimitive StatusPrimitive ValueError ConditionApplication Callback
![Page 20: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/20.jpg)
fcntl(fd, F_SETFL, flags | O_NONBLOCK);error = read(fd, buffer, 100);
if (error === -1) { callback(errorFrom(errno));} else { callback(null, buffer);}
RESULT.
SchedulerInitial ProgramPrimitive StatusPrimitive ValueError ConditionApplication Callback
![Page 21: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/21.jpg)
SCHEDULER.start program
receive callbacks: [ io, callback ]
program finishes
select on the list of all passed IOs
IO is ready for a callback
invoke callback
![Page 22: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/22.jpg)
WHEN ORDER IS DETERMINISTIC, WE WANT A SEQUENTIAL ABSTRACTION.
![Page 23: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/23.jpg)
THREADS.
![Page 24: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/24.jpg)
fs.readFile("/etc/passwd", function(err, data) { console.log(data); }); fs.readFile("/etc/sudoers", function(err, data) { console.log(data); });
ASYNC.
![Page 25: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/25.jpg)
CALLBACK.
function body
visible variables
![Page 26: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/26.jpg)
new Thread(function() { var data = fs.readFile("/etc/passwd"); console.log(data);}); new Thread(function() { var data = fs.readFile("/etc/sudoers"); console.log(data);});
sleep();
THREADS.
![Page 27: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/27.jpg)
new Thread(function() { var data = fs.readFile("/etc/passwd"); console.log(data);}); new Thread(function() { var data = fs.readFile("/etc/sudoers"); console.log(data);});
sleep();
THREADS.
SchedulerInitial ProgramPrimitive StatusPrimitive ValueError ConditionApplication Callback
![Page 28: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/28.jpg)
new Thread(function() { var data = fs.readFile("/etc/passwd"); console.log(data);}); new Thread(function() { var data = fs.readFile("/etc/sudoers"); console.log(data);});
sleep();
THREADS.
SchedulerInitial ProgramPrimitive StatusPrimitive ValueError ConditionApplication Callback
The scheduler does essentially the same thing when using threads.
![Page 29: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/29.jpg)
new Thread(function() { var data = fs.readFile("/etc/passwd"); console.log(data);}); new Thread(function() { var data = fs.readFile("/etc/sudoers"); console.log(data);});
sleep();
THREADS.
SchedulerInitial ProgramPrimitive StatusPrimitive ValueError ConditionApplication Callback
Same with initial program.
![Page 30: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/30.jpg)
STACK.
entry stack frame
stack frame
stack frame
current stack frame
stack frame
local variable values+ current position
The main difference with threads is that the callback structure is more complicated.
callback
resume thread
![Page 31: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/31.jpg)
SELECT + READ.SchedulerInitial ProgramPrimitive StatusPrimitive ValueError ConditionApplication Callback
![Page 32: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/32.jpg)
new Thread(function() { var data = fs.readFile("/etc/passwd"); console.log(data);}); new Thread(function() { var data = fs.readFile("/etc/sudoers"); console.log(data);});
sleep();
THREADS.
SchedulerInitial ProgramPrimitive StatusPrimitive ValueError ConditionApplication Callback
When data is ready, resume the associated thread.
![Page 33: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/33.jpg)
SCHEDULER.start program and sub-threads
receive callbacks: [ io, thread ]
main program pauses
select on the list of all passed IOs
IO is ready
invoke callback (resume associated thread)
![Page 34: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/34.jpg)
■ Possible simultaneous code
■ Can eliminate if desired via a GIL
■ Unexpected interleaved code and context switching overhead
■ Can eliminate if desired by disabling pre-emptive scheduling
■ More memory required for callback structure
DIFFERENCES.
The thread abstraction, which is useful to manage asynchronous events with deterministic order, has varying implementation-defined characteristics. It will always require more memory.
![Page 35: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/35.jpg)
fs.readFile("/etc/passwd", function(err, data) { console.log(data); }); fs.readFile("/etc/sudoers", function(err, data) { console.log(data); });
ASYNC.
Async code can and usually does still have global state and interleaved execution.
![Page 36: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/36.jpg)
CONFUSION.
![Page 37: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/37.jpg)
"FIBERS ARE LIKE THREADS WITHOUT THE PROBLEMS"
![Page 38: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/38.jpg)
new Fiber(function() { var data = fs.readFile("/etc/passwd"); console.log(data);}); new Fiber(function() { var data = fs.readFile("/etc/sudoers"); console.log(data);});
sleep();
FIBERS.
SchedulerInitial ProgramPrimitive StatusPrimitive ValueError ConditionApplication Callback
Same with initial program.
![Page 39: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/39.jpg)
fs.readFile = function(filename) { var fiber = Fiber.current;
fs.readAsync(filename, function(err, data) { fiber.resume(data); });
return Fiber.yield();};
IMPLEMENTATION.
SchedulerInitial ProgramPrimitive StatusPrimitive ValueError ConditionApplication Callback
Fibers implement the status and value parts of the scheduler in the language, but fundamentally have the same data structures as threads.
![Page 40: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/40.jpg)
CALLBACK.
entry stack frame
stack frame
stack frame
current stack frame
stack frame
local variable values+ current position
callback
resume thread
The fact that fibers are "lighter" is an implementation detail. Having to implement manual yielding is a pain.
![Page 41: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/41.jpg)
Threads are useful when working with asynchronous events that arrive in a deterministic order.“
TO RECAP.
![Page 42: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/42.jpg)
WHEN TO USE CALLBACKS.
![Page 43: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/43.jpg)
var Stats = Ember.View.extend({ templateName: 'stats', didInsertElement: function() { this.$().flot(); }}); Stats.create().append();
// vs. var Stats = Ember.View.extend({ templateName: 'stats'}); var view = yield Stats.create().append();view.$().flot();
APPROACHES.
![Page 44: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/44.jpg)
■ Encapsulation: The caller needs to know about the call to flot()
■ Resiliance to Errors: All callers needs to remember to call flot()
■ Composability: The framework can no longer simply ask for a view and render it as needed
PROBLEMS.
![Page 45: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/45.jpg)
LIFECYCLE HOOKS SHOULD BE USED TO AID ENCAPSULATION.
![Page 46: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/46.jpg)
JAVASCRIPT.
![Page 47: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/47.jpg)
INVARIANT: TWO ADJACENT STATEMENTS MUST RUN TOGETHER.
This is a core guarantee of the JavaScript programming model and cannot be changed without breaking existing code.
![Page 48: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/48.jpg)
new Thread(function() { var data = fs.readFile("/etc/passwd"); console.log(data);});
PROBLEM.
In this case, readFile implicitly halts execution and allows other code to run. This violates guarantees made by JS.
![Page 49: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/49.jpg)
BUT SEQUENTIAL ABSTRACTIONS ARE STILL USEFUL!
![Page 50: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/50.jpg)
"GENERATORS"
![Page 51: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/51.jpg)
var task = function*() { var json = yield jQuery.getJSON(url); var element = $(template(json)).appendTo('body');
yield requestAnimationFrame(); element.fadeIn();}; var scheduler = new Scheduler;scheduler.schedule(task);
YIELDING.
![Page 52: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/52.jpg)
var task = function*() { var json = yield jQuery.getJSON(url); var element = $(template(json)).appendTo('body');
yield requestAnimationFrame(); element.fadeIn();}; var scheduler = new Scheduler;scheduler.schedule(task);
YIELDING.
SchedulerInitial ProgramPrimitive StatusPrimitive ValueError ConditionApplication Callback
Here, the task is yielding an object that knows how to be resumed.
![Page 53: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/53.jpg)
STATUS AND VALUE.Thus far, we have limited status and value to built-in primitives that the VM knows how to figure out.
In order for generators to be useful, we will need to expose those concepts to userland.
![Page 54: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/54.jpg)
PROMISES.
![Page 55: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/55.jpg)
file.read = function(filename) { var promise = new Promise(); fs.waitRead(filename, function(err, file) { if (err) { promise.error(err); } else { promise.resolve(file.read()); } }); return promise;}
PROMISES.
SchedulerInitial ProgramPrimitive StatusPrimitive ValueError ConditionApplication Callback
Here, we are still allowing the VM to let us know when the file is ready, but we control the callback manually.
![Page 56: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/56.jpg)
var prompt = function() { var promise = new Promise(); $("input#confirm") .show() .one('keypress', function(e) { if (e.which === 13) { promise.resolve(this.value); } }); return promise();}; spawn(function*() { var entry = yield prompt(); console.log(entry);});
BETTER PROMPT.
SchedulerInitial ProgramPrimitive StatusPrimitive ValueError ConditionApplication Callback
In this case, we are in control of both the status information and the value of the status.
![Page 57: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/57.jpg)
PROMISES ARE A PRIMITIVE.
Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give us a way to use a sequential abstraction for asynchronous events with deterministic order.
In a UI app, a small part of the total interface may have deterministic order, and we can use this abstraction on demand.
In general, I would prefer to use promises together with a higher level sequential code abstraction than use promises directly in application code.
![Page 58: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/58.jpg)
var spawn = function(generator, value) { try { promise = generator.send(value); promise.success(function(value) { spawn(generator, value); }) promise.fail(function(err) { generator.throw(err); }) } catch(e) { if (!isStopIteration(e)) { generator.throw(err); } }};
PSEUDOCODE.
SchedulerInitial ProgramPrimitive StatusPrimitive ValueError ConditionApplication Callback
![Page 59: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/59.jpg)
GENERATORS ARE EXPLICIT AND SHALLOW.
Because generators are explicit and shallow, they don't have a parent stack to store, so their memory usage is more like callbacks. Generators can be explicitly chained though.
![Page 60: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/60.jpg)
OTHER LANGUAGES?
![Page 61: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/61.jpg)
class File def read promise = Promise.new async_read do |string| promise.resolve(string) end Thread.yield promise endend
BETTER PRIMITIVES.
In languages that already have threads, promises can be used to provide more power in the existing scheduler. This can be implemented in terms of sync primitives.
![Page 62: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/62.jpg)
EXTERNAL EVENTS.These are events that are external to the application or application framework.
They are typically handled as asynchronous events. If they have deterministic order, the above techniques may work.
![Page 63: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/63.jpg)
EXTERNAL EVENTS.
However, you may not want to wait to do anything until the first Ajax request is returned, so you typically will not try to use sequential abstractions even for network I/O.
![Page 64: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/64.jpg)
■ External events
■ I/O responses (IndexedDB, Ajax)
■ setTimeout and setInterval
■ DOM Mutation Observers
■ User Interaction
■ click
■ swipe
■ unload
TWO TYPES.
![Page 65: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/65.jpg)
INTERNAL EVENTS.
These are events generated by the app or app framework for another part of the app or app framework.
![Page 66: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/66.jpg)
var EventedObject = function(properties) { for (var property in properties) { this[property] = properties[property]; }}; Object.prototype.set = function(key, value) { EventEmitter.fire(this, key + ':will-change');
this[key] = value;
EventEmitter.fire(this, key + ':did-change');};
EVENTED MODELS.
![Page 67: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/67.jpg)
var person = new EventedObject({ firstName: "Yehuda", lastName: "Katz"}); $("#person").html("<p><span>" + person.firstName + '</span><span>' + person.lastName + "</span></p>"); EventEmitter.on(person, 'firstName:did-change', function() { $("#person span:first").html(person.firstName); }); EventEmitter.on(person, 'lastName:did-change', function() { $("#person span:last").html(person.lastName); });
DECOUPLING.
![Page 68: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/68.jpg)
$.getJSON("/person/me", function(json) { person.set('firstName', json.firstName); person.set('lastName', json.lastName);});
NETWORK.
![Page 69: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/69.jpg)
DECOUPLING.
network
model
rendering
![Page 70: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/70.jpg)
var person = new EventedObject({ firstName: "Yehuda", lastName: "Katz", fullName: function() { return [this.firstName, this.lastName].join(' '); }}); $("#person").html("<p>" + person.fullName() + "</p>"); EventEmitter.on(person, 'firstName:did-change', function() { $("#person p").html(person.fullName()); }); EventEmitter.on(person, 'lastName:did-change', function() { $("#person p").html(person.fullName()); });
PROBLEMS.
![Page 71: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/71.jpg)
$.getJSON("/person/me", function(json) { person.set('firstName', json.firstName); person.set('lastName', json.lastName);});
IF WE DO THIS:
![Page 72: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/72.jpg)
DOUBLE RENDER!
![Page 73: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/73.jpg)
TRANSACTIONS.
![Page 74: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/74.jpg)
var person = new EventedObject({ firstName: "Yehuda", lastName: "Katz", fullName: function() { return [this.firstName, this.lastName].join(' '); }}); EventEmitter.on(person, 'firstName:did-change', function() { UniqueEmitter.fire(person, 'fullName:did-change'); }); EventEmitter.on(person, 'lastName:did-change', function() { UniqueEmitter.fire(person, 'fullName:did-change'); }); UniqueEmitter.on(person, 'fullName:did-change', function() { $("#person p").html(person.fullName()); });
COALESCING.
![Page 75: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/75.jpg)
$.getJSON("/person/me", function(json) { UniqueEmitter.begin(); person.set('firstName', json.firstName); person.set('lastName', json.lastName); UniqueEmitter.commit();});
NETWORK.
![Page 76: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/76.jpg)
GETS COMPLICATED.
This type of solution is not a very good user-facing abstraction, but it is a good primitive.
![Page 77: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/77.jpg)
DATA FLOW.
What we want is an abstraction that describes the data flow via data bindings.
![Page 78: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/78.jpg)
var person = Ember.Object.extend({ firstName: "Yehuda", lastName: "Katz",
fullName: function() { return [this.get('firstName'), this.get('lastName')].join(' '); }.property('firstName', 'lastName')}); $("#person").html("<p>" + person.get('fullName') + "</p>"); person.addObserver('fullName', function() { $("#person p").html(person.get('fullName'));});
DECLARATIVE.The .property is the way we describe that changes to firstName and lastName affect a single output.
![Page 79: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/79.jpg)
$.getJSON("/person/me", function(json) { person.set('firstName', json.firstName); person.set('lastName', json.lastName);});
NETWORK.
Behind the scenes, Ember defers the propagation of the changes until the turn of the browser's event loop.
Because we know the dependencies of fullName, we can also coalesce the changes to firstName and lastName and only trigger the fullName observer once.
![Page 80: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/80.jpg)
BOUNDARIES.A good data binding system can provide an abstraction for data flow for objects that implement observability.
For external objects and events, you start with asynchronous observers (either out from the binding system or in from the outside world).
![Page 81: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/81.jpg)
BETTER ABSTRACTIONS.
For common cases, we can do better.
![Page 82: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/82.jpg)
var person = Ember.Object.create({ firstName: "Yehuda", lastName: "Katz",
fullName: function() { return [this.get('firstName'), this.get('lastName')].join(' '); }.property('firstName', 'lastName')}); var personView = Ember.Object.create({ person: person, fullNameBinding: 'person.fullName', template: compile("<p>{{fullName}}</p>")}); person.append(); $.getJSON("/person/me", function(json) { person.set('firstName', json.firstName); person.set('lastName', json.lastName);});
EXTENDED REACH.For common boundary cases, like DOM, we can wrap the external objects in an API that understands data-binding.
This means that we won't need to write async code to deal with that boundary.
![Page 83: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/83.jpg)
SAME SEMANTICS.If you extend an async abstraction to an external system, make sure that the abstraction has the same semantics when dealing with the outside world.
In Ember's case, the same coalescing guarantees apply to DOM bindings.
![Page 84: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/84.jpg)
CONCLUSION.
![Page 85: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/85.jpg)
LIMITED ASYNC CODE IN APPLICATIONS.
In general, applications should not have large amounts of async code.
In many cases, an application will want to expose abstractions for its own async concerns that allow the bulk of the application code to proceed without worrying about it.
One common pattern is using an async reactor for open connections but threads for the actual work performed for a particular open connection.
![Page 86: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/86.jpg)
DIFFERENT KINDS OF ASYNC CODE.Asynchronous code that arrives in a strict deterministic order can make use of different abstractions that async code that arrives in non-deterministic order.
Internal events can make use of different abstractions than external events.
![Page 87: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/87.jpg)
LAYERS.Promises are a nice abstraction on top of async code, but they're not the end of the story.
Building task.js on top of promises gives us three levels of abstraction for appropriate use as needed.
![Page 88: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/88.jpg)
THANK YOU.
![Page 89: MANAGING ASYNCHRONY - USENIX · PROMISES ARE A PRIMITIVE. Promises provide a shared implementation of status/value that a scheduler can use. In JavaScript, generators + promises give](https://reader034.fdocuments.us/reader034/viewer/2022042303/5ece4120d9590e4d3f32770a/html5/thumbnails/89.jpg)
QUESTIONS?