Async discussion 9_29_15

39
Asynchronous Programming in ECMAScript 2015 Code: https://github.com/cheryly279/ AsyncDiscussion Presentation: http://www.slideshare.net/cherylyaeger/async-discussion- 92915 1

Transcript of Async discussion 9_29_15

Page 1: Async discussion 9_29_15

Asynchronous Programming in ECMAScript 2015Code: https://github.com/cheryly279/AsyncDiscussionPresentation: http://www.slideshare.net/cherylyaeger/async-discussion-92915

1

Page 2: Async discussion 9_29_15

In the beginning…

2

Page 3: Async discussion 9_29_15

Synchronous Coding• When we first start programming, we learn how to code

synchronously.• Every statement runs to completion, followed by the next

statement in the control flow, until the application completes.• What is the major downside of coding this way?• SLOW! “Blocks” the application• Poor interactivity and user experience

3

Page 4: Async discussion 9_29_15

Benefits of Async Code• Tasks are NOT necessarily run in their control flow order.• Non-blocking.• Makes the most of the system's processing power.• Event handling - the application can wait and respond to user

events, such as clicking a button.• Application can run several actions at once, allowing for a

richer experience even if some actions are slow.• Servers can handle more requests, and therefore more

customers, without being blocked by slow I/O calls, for example requests to a database.

4

Page 5: Async discussion 9_29_15

Implementation• Traditional methods: multithreading, multiprocessing.• JavaScript implementation: single thread using callbacks.• Asynchronous code is wrapped in a function.• Function takes arguments to perform it’s task, but also callback

functions. • When the asynchronous function is called:

• Execution continues with next statement in control flow• Once the asynchronous operations have completed, the callback

function will be called.

• We can now be assured that code will be executed at the proper time, without the rest of our app being slowed down.

• Under the hood, threads ARE used, but we don’t have to worry about this. 5

Page 6: Async discussion 9_29_15

Event Loop & Callback Queue• Function calls are added to the call stack. When a function

returns, it is popped off the stack.• Calls that involve asynchronous operations are also added to

the stack.• The asynchronous operation is then handed over to some

other process.• That function is then popped off the stack, as if it completed.• When the other process completes, a message is added to the

queue along with the callback.• Next time the call stack is empty, the event loop reads a

message from the callback queue.• The corresponding callback is added to the call stack and

processed as if it had been called normally.6

Page 7: Async discussion 9_29_15

Call Stack & Callback Queue

fun1();setTimeout(fun2, 2000);fun3();setTimeout(fun4, 1000);console.log('weeeeeeeee!');

Stack

Queue

fun1fun3setTimeoutconsole.logsetTimeout

fun4fun4

fun2Other Process

Other Process

7

Page 8: Async discussion 9_29_15

Event Loop

fun1();setTimeout(fun2, 2000);fun3();setTimeout(fun4, 1000);console.log('weeeeeeeee!');

Stack

Queue

fun4 fun4fun2 fun2

fun2

fun4fun2

fun2fun4

Event Loop8

Page 9: Async discussion 9_29_15

Promises - Introduction• But coding is always evolving. Using callbacks for

asynchronous code has it’s drawbacks:• Easier to read than code with multiple threads, but still not as

easy to read as synchronous code.• Not a standard format to passing callbacks• Callback functionality seems like “side-effects” of the application• Multiple nested callbacks can result in the “pyramid of doom.”

• Promises, which are new in ECMAScript 2015, are created to help solve these problems.

9

Page 10: Async discussion 9_29_15

Promises - Introduction• What are promises?• Cleaner way of writing the same single-threaded asynchronous

code we have been discussing. There’s no new “magic” on how JavaScript asynchronous code works.

• Allow us to treat the result of asynchronous code as a first-class citizen. We can treat the promise itself as the result, even if the result is not complete yet.

• Promises follow a standard, allowing us to be assured of what the callbacks are, and that only one will be called.

10

Page 11: Async discussion 9_29_15

11

Page 12: Async discussion 9_29_15

Promises – Handling• Where before we would pass callback(s) as an argument, we

now pass that code to handlers that are already attached to the promise - using the then function.

• If the asynchronous operation causes an error? The callback function to handle failures is passed to then as the second argument.

• For convenience, Promises also have a catch handler. This is simply a shorthand for then(null, function()..).

12

Page 13: Async discussion 9_29_15

Promises – Handling• At any given time, a promise is in one of three states:• Pending - the asynchronous code has not completed yet.• Fulfilled - the asynchronous code has completed successfully.• Rejected - the asynchronous code has failed.

• Once the promise is fulfilled or rejected and the correct handler has been called, the promise is considered complete - the handlers will not be called again.

13

Page 14: Async discussion 9_29_15

Promises (Notes)• When a call is made to the asynchronous function, that

operation will be performed whether the then/catch handlers are used or not.

• Only one of the handlers will be called (success or failure).• However, handlers can be added to the promise after it has

been fulfilled or rejected, and the proper handler will still execute.

14

Page 15: Async discussion 9_29_15

Promises - Chaining• Promises allow us to break up and order asynchronous code

that depends on earlier asynchronous code.• From a then or catch handler, we can execute other

asynchronous operations.• The results of this can then be handled in a subsequent then

or catch handler.• This is possible because the handlers themselves return a

promise.• If the return value is not a promise, it is wrapped in a promise

that resolves to that value.• If an error is thrown, it is wrapped in a promise that rejects with

that error.15

Page 16: Async discussion 9_29_15

Promises – Chaining (Notes)• Good practice to end with a catch handler, as errors will flow

down the chain like in a try/catch block. If we don’t, any uncaught errors are simply lost.

• The result of a catch that doesn’t throw another error is a resolved promise.

• Using then and catch is NOT the same as using then with two callbacks:• catch is a handler for the next iteration of the chain.• If we were instead to pass two callbacks to then, errors in the

first callback will not be handled by the second callback.

16

Page 17: Async discussion 9_29_15

Promises - Retrospective• Let’s take a step back - did this resolve the 4 issues above with

callbacks?• Easier to read. • Can pass separate callbacks for success and failure. • Return to a specific point of execution from a callback.

• We can never truly make this happen, but now we can better organize our callback code, and use return and throw statements to indicate a logical flow.

• Avoid “pyramid of doom” from nested callbacks.

17

Page 18: Async discussion 9_29_15

Promises – Define Your Own• Syntax new Promise(function(resolve, reject) {

// use resolve/reject});

• Call resolve to indicate work has completed successfully, pass result if there is one.

• Call reject to pass back an error that has occurred

18

Page 19: Async discussion 9_29_15

Promise.resolve & Promise.reject

• Promise.resolve, shorthand for: new Promise(function (resolve, reject) { resolve(value); }); • Promise.reject, shorthand for: new Promise(function (resolve, reject) { reject(error); }); • Converts code that is not asynchronous or doesn’t conform to

the promises standard• Handy for testing 19

Page 20: Async discussion 9_29_15

Promise.all• In synchronous code, if you have list of values to manipulate,

you can use a loop.• What if the handling of each value is an asynchronous call?

Remember that as each call is made, it begins executing in parallel to the rest of the code.

• If we were to call an asynchronous function in each iteration of a loop, we would have n number of parallel actions occurring, each needing it’s own then to access the result.

20

Page 21: Async discussion 9_29_15

Promise.all• Fortunately, promises provide a way to wait for several

concurrent promises to resolve - Promise.all.• Takes an array of promises and returns a promise that resolves

when all the promises have resolved. • Using the then handler with Promise.all will allow you

to define a callback function that receives an array of results from the promises passed to all.

• Resolves when/if all promises passed in are resolved - if one fails, the entire thing fails.

• The array of results will be in order.

21

Page 22: Async discussion 9_29_15

Promises - Terminology• What we have been discussing so far are promises defined for

ES6, fulfilling a specification known as the Promises/A+ specification: https://promisesaplus.com/.

• Some terminology clarifiers:• Thenable - an object (usually a promise) that has a then function,

allowing us to chain our handlers.• Future - simply an older term for promises.• Deferred - jQuery’s version of promises• jQuery Promise - A jQuery deferred object can be converted to a

jQuery promise (still not the same as ES6 promises). These objects are a subset of a deferred object, having fewer functions.

22

Page 23: Async discussion 9_29_15

jQuery Deferreds• Deferreds do NOT fully conform to the A+ spec. Where

possible, we should be using ES6-style promises instead.• Unfortunately for us, jQuery is a core part of using Backbone,

and deferred objects are returned from ajax calls, so for the time being they can be hard to avoid.

• Deferreds were made available first, and so have a different set of states and functions, which can make handling them confusing, especially when they end up being used with ES6 promises.

23

Page 24: Async discussion 9_29_15

jQuery Deferreds• Common methods:• then: takes callbacks for success, failure, or progress• done: takes callback for success• fail: takes callback for failure• always: takes callback for success or failure• resolve, resolveWith: resolve the deferred• reject, rejectWith: reject the deferred• when: converts one or more objects to a promise, works similarly

to Promise.all• promise: retrieve the deferred’s promise object

• Deferreds are thenables, BUT the then handler does not return another deferred, so we cannot do the same chaining that is available with ES6-style promises.

24

Page 25: Async discussion 9_29_15

Fetch API• ES6 also has a new API to replace XMLHttpRequest.• How does it work?• Revolves largely around the fetch method on the window

object. A simple way to use this method is to pass it a URL, which will execute a GET request.

• We can also pass a Request object, where you can specify the URL as well as configuration data, such as the request headers or the method of the request (GET, POST, etc.).

• Returns a Response object with helpful methods for getting at the returned data.

25

Page 26: Async discussion 9_29_15

Fetch API• Benefits• Returns promises - rather than having to remember to use the onload and onerror callbacks, we just use then/catch as we would with other promises.

• The Request object makes it easier to configure the request, and copy the request to create other requests (cloning).

• The response body can be streamed, rather than having the entire response in memory.

• Downsides• You cannot (yet) abort a promise, so you cannot abort a request.

You can, however, cancel a stream.• You cannot get progress events, to see how the request is going.

26

Page 27: Async discussion 9_29_15

Async and Await• Coming in ES7, we can make our code even cleaner using two

new keywords: async and await.• Allows us to write asynchronous code that looks even more

like synchronous code.• You can use this feature now via the Traceur transpiler which

converts ES6 to ES5 but has an option to add async/await. (Maintained by Google.)

• Note: Async functions used to be called defered functions. *sigh*

27

Page 28: Async discussion 9_29_15

Async and Await• async keyword• Precedes the function definition/declaration.• Wraps the function result in a promise.• We no longer need to call resolve and reject.

• Inside an async function, you can precede any asynchronous calls with await. This will force the function to wait until the asynchronous operation has completed. The resolved value or rejected error will be returned.

• Note: Nested functions will also need the async keyword if they are going to be calling asynchronous operations.

• Note: Using the await keyword before a call to synchronous code simply runs that code as it always would. 28

Page 29: Async discussion 9_29_15

Generators - Introduction• Generator function - A type of function whose execution can

be paused and resumed later.• This can happen several times in a generator - not limited to

one pause.• Why is this useful?• For iterating over a custom sequence.• Yet another way to write asynchronous code in a more

synchronous way, by hiding away the asynchronous details.• To make a generator function, declare/define the function

with an asterisk (function*).• Yield expressions indicate where the function can be paused.

29

Page 30: Async discussion 9_29_15

Generators - Execution• Call the function and assign the result to a variable. This

creates a generator.• We will use this object to run through the generator function.• Call next on the generator to begin execution.• When the function encounters a yield expression, execution

is paused and control is passed back to the caller.• The generator can continue to call next to iterate through

the generator function until it completes.• After the last yield, the generator must call next once more to

finish the method.

30

Page 31: Async discussion 9_29_15

Generators - Communication• Generators can communicate back and forth with their

generator function.• If the yield keyword is followed by a value, that value is sent

back to the generator.• The value sent back is wrapped in an object literal with two

properties: value (the value returned), and done, a boolean indicating if the function has returned because it completed.

• Communication goes both ways – values can also be sent back to the generator via next.

• This becomes the value that the yield expression evaluates to.

31

Page 32: Async discussion 9_29_15

Generators – for/of structure• Allows you to loop through a generator function very easily -

the details of creating the generator and extracting the value from it’s wrapped object are abstracted away for you.

• The loop completes when done is true.• When using for/of, any value returned from a return

statement at the end of the generator function is lost. Instead, the final value you receive is the return value from the final yield.

• Also, no values can be passed back - there is only one-way communication when using for/of.

32

Page 33: Async discussion 9_29_15

Generators – Delegation• Generators can “delegate” control to another iterator, where

you continue to iterate, but control passes to the other generator (and eventually back to the original generator when complete).• Fancy speak for: Using yield*, you can call another generator

function that itself will yield results to the original iterator.

33

Page 34: Async discussion 9_29_15

Generators – Async Example

• We can iterate through a generator function that makes an async call, and then use the same iterator to return the async result back to the generator function.

SURPRISE!It turns out, we can use generators and promises to simulate

async/await.

(Sure, we have async/await with Traceur, but it’s nice to understand how it works.)

34

Page 35: Async discussion 9_29_15

Testing Promises• Jasmine’s done function• The done function enables us to test a promise and it’s outcome

before continuing with the next test.• This function is passed to beforeEach, it and afterEach.• When the promise and resulting assertions are finished, calling done signals that the asynchronous work has completed.

• By default, Jasmine waits 5 seconds for an async call to complete or for done to be called.

• To test, call the promise as you normally would.• In the then handler, test for the result you would expect from

the resolve promise.• In the catch handler, test for the result you would expect from

the rejected promise. 35

Page 36: Async discussion 9_29_15

Testing Promises - Handlers• More often than not, you will want to test the after-effects of

a promise – that is, testing that the proper actions take place in the then/catch.

• Spy on the function that returns the promise, and have it return a promise that is always resolved (or always rejected).

• For jQuery deferreds, you can use a similar technique.

spyOn(view, ‘editFile’).and.returnValue( Promise.resolve(retVal));

spyOn(view.model, ‘save’).and.returnValue( $.Deferred().resolve().promise());

36

Page 37: Async discussion 9_29_15

Testing Promises• Tools for aiding in asynchronous testing• Mocha: can be used to make a test fail where a promise is

rejected – if you test returns a rejected promise, the test automatically fails. Otherwise, if you don’t explicitly test for the error it will be lost.

• Chai-as-promised: Allows for cleaner assertions with should.eventually.equal rather than using then and done.

• Sinon: Allows you to “fake” server responses, which aides in testing how your application responds when AJAX calls succeed or fail.

37

Page 38: Async discussion 9_29_15

Going Further…• Web Workers• A way for your web app to perform tasks using background

threads.• While these actions perform separately, you can communicate

with the main JavaScript thread.• Service Workers• Instead allows you to run scripts that are independent of the web

app entirely.• Will eventually allow for actions such as push notifications.

38

Page 39: Async discussion 9_29_15

Useful Links• Event loop and Callback Queue• MDN - Concurrency model and Event Loop• What the heck is the event loop anyway?

• Promises• We Have a Problem with Promises• Javascript Promises• Promises in JavaScript Unit Tests: the Definitive Guide

• Fetch• That's so fetch!• Introduction to the Fetch API

• Async/Await• ES7 async functions• Taming the asynchronous beast with ES7

• Generators• The Basics of ES6 Generators• MDN - Iterators and Generators

39