Asychronicity in JavaScript - University of Wisconsin...

Preview:

Citation preview

Asynchrony in JavaScript

Alper Sarikaya (@yelperalp)CS 638 JavaScript & Web Programming

November 17th, 2015

Asynchrony in JavaScript

How we handle it in JavaScript (and jQuery, D3, etc.)

Requesting data in JavaScript (and jQuery, D3, etc.)

Event Handling (and jQuery, D3, etc.)

Reactive Programming

WebSockets/binary data/WebGL

WebWorkers -

JavaScript is single-threaded!

This means:

Asynchronous events can return and interrupt

Long processing work can block interrupts from occurring (page appears to hang)

Only one thing can be done at a time(except if you use WebWorkers; more later)

Handling Asynchronicity

Want to (without reloading page):

Get data from datastore on the webserver

Update state on webserver based on user action

Post a message, record a vote for others to see

Retrieve some video/binary data to display to client

Handling Asynchronicity

Make an XmlHttpRequest (XHR)

Ajax programming model

Asynchronous JavaScript and XML

var xhr = new XMLHttpRequest();xhr.open('GET', 'DoSomething.php', true);xhr.responseType = 'json';

xhr.addEventListener('load', function() {if (xhr.status == 200) {

loadBinaryData(xhr.response);} else {

console.warning("failed to load (status: %d)", xhr.status);

console.trace();}

});

xhr.send(null);

var xhr = new XMLHttpRequest();xhr.open('GET', 'DoSomething.php', true);xhr.responseType = 'json';

xhr.addEventListener('load', function() {if (xhr.status == 200) {

loadJsonData(xhr.response);} else {

console.warning("failed to load (status: %d)", xhr.status);

console.trace();}

});

xhr.send(null);

Webserver does a task

Type of returned data

Do something with the returned data

Send the request

https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest

$.get('DoSomething.php', loadJsonData, 'json');

https://api.jquery.com/jquery.get/

$.get('DoSomething.php', loadJsonData, 'json');

https://api.jquery.com/jquery.get/

One success

Implement JavaScript operations in a cross-browser way

Syntactic sugar (do the same thing in less lines of code)

JavaScript ECMA 5 (basically HTML5) standardized a lot of the ugly old stuff

Dealing with returned data

$.get('DoSomething.php', loadJsonData, 'json');

Do something with the returned data

Dealing with returned data

Do something with the returned data

var dataReady = false;var ds = {};

$.get('DoSomething.php', loadJsonData, 'json');var loadJsonData = function(text) {dataReady = false;ds.data = [];

// process some data, fill up ds.data with text

// Set flag to allow rendering to continue.dataReady = true;

};

Dealing with returned data

Do something with the returned data

$.get('DoSomething.php', loadJsonData, 'json');$.get('DoSomethingElse.php', loadJsonData, 'json');var loadJsonData = function(text) {dataReady = false;ds.data = [];

// process some data, fill up ds.data with text

// Set flag to allow rendering to continue.// ???dataReady = true;

};

Dealing with returned data

Do something with the returned data

var loadJsonFile = function(text) {// process the data

continueIfDone();};

var continueIfDone = function() {// check that all data is loaded, if not:return false;

// otherwise, continuenextStep();

}

Use promises for one-time callbacks (built into ES6: http://www.html5rocks.com/en/tutorials/es6/promises/)

Use WebSockets for real-time connections (e.g. chat)(built into newer browsers, see https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications for an example)

Promises!

getJSON('story.json').then(function(story) {return getJSON(story.chapterUrls[0]);

}).then(function(chapter1) {console.log("Got chapter 1!", chapter1);

});

http://www.html5rocks.com/en/tutorials/es6/promises/

Promises!function get(url) {// Return a new promise.return new Promise(function(resolve, reject) {// Do the usual XHR stuffvar req = new XMLHttpRequest();req.open('GET', url);

req.onload = function() {// This is called even on 404 etc// so check the statusif (req.status == 200) {// Resolve the promise with the response textresolve(req.response);

}else {// Otherwise reject with the status text// which will hopefully be a meaningful errorreject(Error(req.statusText));

}};

// Handle network errorsreq.onerror = function() {reject(Error("Network Error"));

};

// Make the requestreq.send();

});}

Processing Data

Very convenient to deal with JSON dataJust set returnType json

arraybuffer

{"VHA4_P11_F21_DPI3-ref": {"attenuation":

"readBreadth.dat","metrics": "conjProbDiff.dat","numReads": 258000,"reference": "VN1203-HA.fa.txt"

},"VHA3_P1_F991_DPI3-ref": {"attenuation":

"readBreadth.dat","metrics": "conjProbDiff.dat","numReads": 295000,"reference": "VN1203-HA.fa.txt"

}}

var xhr = new XMLHttpRequest();xhr.open('GET', 'DoSomething.php', true);xhr.responseType = 'json';

xhr.addEventListener('load', function() {if (xhr.status == 200) {

loadJsonData(xhr.response);} else {

console.warning("failed to load (status: %d)", xhr.status);

console.trace();}

});

xhr.send(null);

Bind to event!

Event Listeners

Event Listeners

Events can be:

Monitoring AJAX progress events (see Monitoring Progress on MDN)

User input (mouseover, mousemove, key-press)

Custom-built events (e.g. when a rendering pass finishes)

Applications?

Validating formsAre all parameters within acceptable values?

Navigating page with keystrokesEver typed in ? into Twitter or Gmail?

Capture user inputUser can drive a WebGL game, drag DOM elements, etc.

<button id="submit" type="submit">Submit Form</button>

Event Listeners

var submitButton = document.getElementById("#submit");

submitButton.addEventListener("click", parseForm);

HTML

JS

<button id="submit" type="submit">Submit Form</button>

Event Listeners (jQuery)

$("#submit").click(parseForm);

HTML

JS

<div id="container"><button id="submit" type="submit">Submit Form</button>

</div>

Multiple Event Listeners in DOM

$("#submit").click(parseForm);$("#container").click(doSomethingElse);

HTML

JS

parseForm() called

doSomethingElse() called

<div id="container"><button id="submit" type="submit">Submit Form</button>

</div>

Multiple Event Listeners in DOM

$("#submit").click(parseForm);$("#container").click(doSomethingElse);var parseForm = function(event) {event.stopPropagation();// pull data from the form, and parse

};

HTML

JS

Alternatives?

Reactive programming style (Rx)See https://gist.github.com/staltz/868e7e9bc2a7b8c1f754 for a great intro

Asynchrony Potpourrivar dataWorker = new Worker('worker.js');dataWorker.postMessage(numPoints);console.log("sent message");

dataWorker.onmessage = function(e) {console.log("received response");

};WebWorkers uses a message-passingsystem to offload computation intoa separate thread.This gets around the UI hanging issuewhen processing data!

onmessage = function(e) {var numPoints = e.data;// do some computation

postMessage(['databuf', returnData], returnData);}worker.js

Asynchrony Potpourrivar xhr = new XMLHttpRequest();xhr.open('GET', filename, true);xhr.responseType = 'arraybuffer';

xhr.addEventListener('progress', updateProgress(name), false);

xhr.addEventListener('load', function() {if (xhr.status == 200) {loadBinaryData(xhr.response, name);

} else {console.warning("failed to load (status: %d)", xhr.status);console.trace();

}});

xhr.send(null);

Binary data DataView

or natively with WebGL data buffers

Asynchrony Potpourri

function webGLStart() {var canvas = document.getElementById("lesson01-canvas");initGL(canvas);initShaders();initBuffers();

gl.clearColor(0.0, 0.0, 0.0, 1.0);gl.enable(gl.DEPTH_TEST);

drawScene();}

WebGL uses commands sent to the GPU, and eventually fires

Asynchrony Potpourri

function tick() {window.reqeustAnimationFrame(tick);handleKeys();drawScene();animate();

}

RequestAnimationFrame requeststhe browser to call the animationloop as soon as it is able

Asynchrony Potpourri

var exampleSocket = new WebSocket("ws://www.example.com/socketserver", ["protocolOne", "protocolTwo"]);

exampleSocket.send("Here's some text that the server wants!");

exampleSocket.onmessage = function (event) {console.log(event.data);

}

WebSockets use TCP connectionsto send and receive data(think chat applications, real-time connection to server applications...)

Recommended