├── .gitignore ├── browser ├── answers │ ├── assets │ │ └── main.css │ ├── blocking-with-promises.answer.html │ ├── fib-calculations.webworker.answer.html │ └── fib-worker.js ├── assets │ ├── main.css │ └── stroke.gif ├── blocking-with-set-timeout.html ├── blocking-with-while.html ├── fib-calculations.html ├── raf-vs-set-timeout.html ├── web-worker-example.html └── web-worker-example.js ├── node ├── async-await.md ├── async-await.quizz.answer.md ├── async-await.quizz.md ├── callbacks.md ├── callbacks.quizz.answer.md ├── callbacks.quizz.md ├── files │ ├── demofile.other.txt │ └── demofile.txt ├── generators.md ├── generators.quizz.answer.md ├── generators.quizz.md ├── node-loop-example.js ├── node-loop-quizz.answer.md ├── node-loop-quizz.md ├── promises.md ├── promises.quizz.answer.md └── promises.quizz.md ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | __OLD__ 2 | /node_modules 3 | working.js -------------------------------------------------------------------------------- /browser/answers/assets/main.css: -------------------------------------------------------------------------------- 1 | p { 2 | font-size: 30px; 3 | font-family: "Courier New", Courier, monospace; 4 | } 5 | button { 6 | font-size: 30px; 7 | } 8 | -------------------------------------------------------------------------------- /browser/answers/blocking-with-promises.answer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

10 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellat quia quidem voluptates quas nam, sunt aspernatur distinctio, 11 | hic, inventore quod esse? Aut error non incidunt a, dignissimos laboriosam id quo.

12 | 13 | 14 | 22 | 23 | -------------------------------------------------------------------------------- /browser/answers/fib-calculations.webworker.answer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

10 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellat quia quidem voluptates quas nam, sunt aspernatur distinctio, 11 | hic, inventore quod esse? Aut error non incidunt a, dignissimos laboriosam id quo.

12 | 13 | 14 | 23 | 24 | -------------------------------------------------------------------------------- /browser/answers/fib-worker.js: -------------------------------------------------------------------------------- 1 | function fib(x) { 2 | if (x <= 0) return 0; 3 | if (x == 1) return 1; 4 | return fib(x - 1) + fib(x - 2); 5 | } 6 | 7 | onmessage = function(e) { 8 | console.log("Worker recieved message", e); 9 | let num = parseInt(e.data); 10 | console.log(num); 11 | let res = fib(num); 12 | console.log("Worker posting message back"); 13 | postMessage(res); 14 | }; 15 | -------------------------------------------------------------------------------- /browser/assets/main.css: -------------------------------------------------------------------------------- 1 | p { 2 | font-size: 30px; 3 | font-family: "Courier New", Courier, monospace; 4 | } 5 | button { 6 | font-size: 30px; 7 | } 8 | -------------------------------------------------------------------------------- /browser/assets/stroke.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawache/async-javascript-workshop/1cdf623d663b66bc61503b21af08ce90b4880421/browser/assets/stroke.gif -------------------------------------------------------------------------------- /browser/blocking-with-set-timeout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

10 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellat quia quidem voluptates quas nam, sunt aspernatur distinctio, 11 | hic, inventore quod esse? Aut error non incidunt a, dignissimos laboriosam id quo.

12 | 13 | 14 | 23 | 24 | -------------------------------------------------------------------------------- /browser/blocking-with-while.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

10 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellat quia quidem voluptates quas nam, sunt aspernatur distinctio, 11 | hic, inventore quod esse? Aut error non incidunt a, dignissimos laboriosam id quo.

12 | 13 | 14 | 15 | 16 | 23 | 24 | -------------------------------------------------------------------------------- /browser/fib-calculations.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

10 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellat quia quidem voluptates quas nam, sunt aspernatur distinctio, 11 | hic, inventore quod esse? Aut error non incidunt a, dignissimos laboriosam id quo.

12 | 13 | 14 | 24 | 25 | -------------------------------------------------------------------------------- /browser/raf-vs-set-timeout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

requestAnimationFrame Counter: 10 | 0 11 |

12 | 13 |

setTimeout Counter: 14 | 0 15 |

16 | 17 | 18 | 50 | 51 | -------------------------------------------------------------------------------- /browser/web-worker-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

10 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellat quia quidem voluptates quas nam, sunt aspernatur distinctio, 11 | hic, inventore quod esse? Aut error non incidunt a, dignissimos laboriosam id quo.

12 | 13 | 14 | 23 | 24 | -------------------------------------------------------------------------------- /browser/web-worker-example.js: -------------------------------------------------------------------------------- 1 | onmessage = function(e) { 2 | console.log(e.data); 3 | postMessage("World"); 4 | }; 5 | -------------------------------------------------------------------------------- /node/async-await.md: -------------------------------------------------------------------------------- 1 | # Async/Await 2 | 3 | ## How to create 4 | 5 | ```js 6 | const doAsyncTask = () => Promise.resolve("done"); 7 | ``` 8 | 9 | When using promises we attach a then 10 | 11 | ```js 12 | const doAsyncTask = () => Promise.resolve("done"); 13 | doAsyncTask().then(val => console.log(val)); 14 | console.log("here"); // <-- this is called first! 15 | ``` 16 | 17 | When using async/await we don't need to attach a then 18 | 19 | ```js 20 | const doAsyncTask = () => Promise.resolve("done"); 21 | async function asim() { 22 | // <-- mark it as `async` 23 | let value = await doAsyncTask(); // <-- Don't need to call .then 24 | console.log(value); 25 | } 26 | asim(); 27 | ``` 28 | 29 | can write that as an IFFE 30 | 31 | ```js 32 | const doAsyncTask = () => Promise.resolve("done"); 33 | (async function() { 34 | // <-- IIFE, note the async 35 | let value = await doAsyncTask(); // <-- Don't need to call .then 36 | console.log(value); 37 | })(); 38 | ``` 39 | 40 | Also it blocks 41 | 42 | ```js 43 | const doAsyncTask = () => Promise.resolve("1"); 44 | (async function() { 45 | let value = await doAsyncTask(); 46 | console.log(value); 47 | console.log("2"); //----> This waits before it's printed 48 | })(); 49 | ``` 50 | 51 | vs. without the await, it prints the other way round 52 | 53 | ```js 54 | const doAsyncTask = () => Promise.resolve("1"); 55 | (async function() { 56 | doAsyncTask().then(console.log); 57 | console.log("2"); 58 | })(); 59 | ``` 60 | 61 | ## Async functions return a promise 62 | 63 | ```js 64 | const doAsyncTask = () => Promise.resolve("1"); 65 | let asyncFunction = async function() { 66 | let value = await doAsyncTask(); 67 | console.log(value); 68 | console.log("2"); 69 | return "3"; // Whatever we return is like a resolve 70 | }; 71 | asyncFunction().then(v => console.log(v)); // We can attach a then to it 72 | ``` 73 | 74 | ## Handling Errors 75 | 76 | - Because it's now sync we can use try/catch, the catch value is what was returned in the reject 77 | 78 | ```js 79 | const doAsyncTask = () => Promise.reject("error"); 80 | const asyncFunction = async function() { 81 | try { 82 | const value = await doAsyncTask(); 83 | } catch (e) { 84 | console.error("Moo: ", e); 85 | return; 86 | } 87 | }; 88 | asyncFunction(); 89 | ``` 90 | 91 | 92 | 93 | ## Blocking != Fast 94 | 95 | But since it's blocking, it can be inefficient, take for example the act of loading multiple files 96 | 97 | ```js 98 | const util = require("util"); 99 | const fs = require("fs"); 100 | const readFile = util.promisify(fs.readFile); 101 | 102 | const files = ["./files/demofile.txt", "./files/demofile.other.txt"]; 103 | 104 | (async () => { 105 | for (let name of files) { 106 | console.log(await readFile(name, "utf8")); // <-- One file loaded at a time, instead of all files at once 107 | } 108 | })(); 109 | ``` 110 | 111 | ## Async isn't magic 112 | 113 | What does the below code print? 114 | 115 | ```js 116 | async function printLine1() { 117 | console.log("1"); 118 | } 119 | 120 | async function printLine2() { 121 | console.log("2"); 122 | } 123 | 124 | async function main() { 125 | printLine1(); 126 | printLine2(); 127 | } 128 | main(); 129 | console.log("Finished"); 130 | ``` 131 | 132 | ## Async Iterators 133 | 134 | This feature is still in experimental phases, it hasn't been fully rolled out to all browser and it's only avaiable in node at least 9.1 behind a flag. 135 | 136 | It's a subtle difference, but now you can iterate over iterators that return promises, like so: 137 | 138 | `node --harmony-async-iteration working.js` 139 | 140 | ```js 141 | (async () => { 142 | const util = require("util"); 143 | const fs = require("fs"); 144 | const readFile = util.promisify(fs.readFile); 145 | 146 | const files = ["./files/demofile.txt", "./files/demofile.other.txt"]; 147 | const promises = files.map(name => readFile(name, "utf8")); 148 | for await (let content of promises) { 149 | //<-- See the await is on the for 150 | console.log(content); 151 | } 152 | })(); 153 | ``` 154 | 155 | ## Custom Iterators 156 | 157 | We can loop over a pre-built array of promises with `for-await-of`. 158 | 159 | An array is an iterator, which just means that it's an object that has a property with name `Symbol.iterator` that points to an object with a `next()` function that returns an object with `{ done: false, value: ? }` for each value. When you want the iterator to complete just return `done: true` instead. 160 | 161 | Then you can use it where you would use any iterator, like `for-of`. 162 | 163 | ```js 164 | const customIterator = () => ({ 165 | [Symbol.iterator]: () => ({ 166 | x: 0, 167 | next() { 168 | if (this.x > 100) { 169 | return { 170 | done: true, 171 | value: this.x 172 | }; 173 | } 174 | return { 175 | done: false, 176 | value: this.x++ 177 | }; 178 | } 179 | }) 180 | }); 181 | 182 | for (let x of customIterator()) { 183 | console.log(x); 184 | } 185 | ``` 186 | 187 | ## Custom Async Iterators 188 | 189 | We can also use custom iterators with the new `for-await-of` syntax by using `Symbol.asyncIterator` and making sure the value returned is a `Promise`, like so: 190 | 191 | ```js 192 | const customAsyncIterator = () => ({ 193 | [Symbol.asyncIterator]: () => ({ 194 | x: 0, 195 | next() { 196 | if (this.x > 100) { 197 | return Promise.resolve({ 198 | done: true, 199 | value: this.x 200 | }); 201 | } 202 | 203 | let y = this.x++; 204 | 205 | return Promise.resolve({ 206 | done: false, 207 | value: y 208 | }); 209 | } 210 | }) 211 | }); 212 | 213 | (async () => { 214 | for await (let x of customAsyncIterator()) { 215 | console.log(x); 216 | } 217 | })(); 218 | ``` 219 | 220 | 221 | -------------------------------------------------------------------------------- /node/async-await.quizz.answer.md: -------------------------------------------------------------------------------- 1 | # Answer 1 2 | 3 | This version answers the brief but is less efficient because each file is loaded in sequence, one at a time. 4 | 5 | ```js 6 | const util = require("util"); 7 | const fs = require("fs"); 8 | const readFile = util.promisify(fs.readFile); 9 | 10 | const files = ["./files/demofile.txt", "./files/demofile.other.txt"]; 11 | 12 | (async () => { 13 | for (let name of files) { 14 | console.log(await readFile(name, "utf8")); // <-- One file loaded at a time, instead of all files at once 15 | } 16 | })(); 17 | ``` 18 | 19 | This version is far more efficient, using `Promise.all` means we load both files in parralel. 20 | 21 | ```js 22 | const util = require("util"); 23 | const fs = require("fs"); 24 | const readFile = util.promisify(fs.readFile); 25 | 26 | const files = ["./files/demofile.txt", "./files/demofile.other.txt"]; 27 | 28 | (async () => { 29 | let promises = files.map(name => readFile(name, { encoding: "utf8" })); 30 | let values = await Promise.all(promises); 31 | console.log(values); 32 | })(); 33 | ``` 34 | 35 | # Answer 2 36 | 37 | ```js 38 | const util = require("util"); 39 | const fs = require("fs"); 40 | const readFile = util.promisify(fs.readFile); 41 | 42 | const fileIterator = files => ({ 43 | [Symbol.asyncIterator]: () => ({ 44 | x: 0, 45 | next() { 46 | if (this.x >= files.length) { 47 | return { 48 | done: true 49 | }; 50 | } 51 | let file = files[this.x++]; 52 | return readFile(file, "utf8").then(data => ({ 53 | done: false, 54 | value: data 55 | })); 56 | } 57 | }) 58 | }); 59 | 60 | const files = ["./files/demofile.txt", "./files/demofile.other.txt"]; 61 | 62 | (async () => { 63 | for await (let x of fileIterator(files)) { 64 | console.log(x); 65 | } 66 | })(); 67 | ``` 68 | -------------------------------------------------------------------------------- /node/async-await.quizz.md: -------------------------------------------------------------------------------- 1 | # Question 1 2 | 3 | Convert the promise version of the multi-file loader over to using async/await 4 | 5 | ```js 6 | const util = require("util"); 7 | const fs = require("fs"); 8 | const readFile = util.promisify(fs.readFile); 9 | 10 | const files = ["./files/demofile.txt", "./files/demofile.other.txt"]; 11 | 12 | let promises = files.map(name => readFile(name, { encoding: "utf8" })); 13 | Promise.all(promises).then(values => { 14 | // <-- Uses .all 15 | console.log(values); 16 | }); 17 | ``` 18 | 19 | # Question 2 20 | 21 | Again convert the promise version of the multi-file loader over to using async/await but using a custom async iterator with the following syntax 22 | 23 | node --harmony-async-iteration 24 | 25 | ```js 26 | const fileIterator = files => ({ 27 | [Symbol.asyncIterator]: () => ({ 28 | x: 0, 29 | next() { 30 | // TODO 31 | } 32 | }) 33 | }); 34 | 35 | (async () => { 36 | for await (let x of fileIterator([ 37 | "./files/demofile.txt", 38 | "./files/demofile.other.txt" 39 | ])) { 40 | console.log(x); 41 | } 42 | })(); 43 | ``` 44 | -------------------------------------------------------------------------------- /node/callbacks.md: -------------------------------------------------------------------------------- 1 | # Callbacks 2 | 3 | 4 | 5 | ## Handling errors, error first callbacks 6 | 7 | * Read the below code and then do quizz 2 and 3 8 | 9 | ```js 10 | const fs = require("fs"); 11 | 12 | fs.readFile("./files/demofile.txt", { encoding: "utf8" }, (err, data) => { 13 | if (err) { 14 | // next(err) <- can pass up the chain 15 | 16 | // console.error(err); <- can log and continue 17 | // return 18 | throw err; // <- can error and exit 19 | } else { 20 | console.log(data); 21 | } 22 | }); 23 | ``` 24 | 25 | 26 | 27 | ## Callback Hell 28 | 29 | ```js 30 | function doAsyncTask(cb) { 31 | setImmediate(() => { 32 | console.log("Async Task Calling Callback"); 33 | cb(); 34 | }); 35 | } 36 | 37 | doAsyncTask(() => { 38 | doAsyncTask(() => { 39 | doAsyncTask(() => { 40 | doAsyncTask(() => { 41 | doAsyncTask(() => { 42 | doAsyncTask(() => { 43 | doAsyncTask(() => { 44 | doAsyncTask(() => { 45 | doAsyncTask(() => { 46 | doAsyncTask(() => {}); 47 | }); 48 | }); 49 | }); 50 | }); 51 | }); 52 | }); 53 | }); 54 | }); 55 | }); 56 | ``` 57 | -------------------------------------------------------------------------------- /node/callbacks.quizz.answer.md: -------------------------------------------------------------------------------- 1 | # Answer 1 2 | 3 | This was a trick question. 4 | Callbacks are not async by default, this was a sync call so the message var was not defined by the time the callback was called. 5 | To make this work we need to wrap our call to `cb` in either a `setImmediate` or a `process.nextTick`. 6 | 7 | ```js 8 | function doAsyncTask(cb) { 9 | // cb(); 10 | 11 | // setImmediate(() => { 12 | // console.log("Async Task Calling Callback"); 13 | // cb(); 14 | // }); 15 | 16 | process.nextTick(() => { 17 | console.log("Async Task Calling Callback"); 18 | cb(); 19 | }); 20 | } 21 | 22 | doAsyncTask(() => console.log(message)); 23 | 24 | let message = "Callback Called"; 25 | ``` 26 | 27 | # Answer 2 28 | 29 | ```js 30 | const fs = require("fs"); 31 | 32 | function readFileThenDo(next) { 33 | fs.readFile("./blah.nofile", (err, data) => { 34 | if (err) { 35 | next(err); 36 | } else { 37 | next(null, data); 38 | } 39 | }); 40 | } 41 | 42 | readFileThenDo((err, data) => { 43 | if (err) { 44 | console.error(err); 45 | } else { 46 | console.log(data); 47 | } 48 | }); 49 | ``` 50 | 51 | # Answer 3 52 | 53 | Or if the error is serious, you can throw the error as soon as you see it. 54 | 55 | try..catch desn't work as you expect with callbacks, it only really works with synchronous code. 56 | 57 | By the time the callback throws the error we have moved on from the try..catch, the throw happens in the root scope and will just cause the program to exit. 58 | 59 | ```js 60 | const fs = require("fs"); 61 | 62 | function readFileThenDo(next) { 63 | fs.readFile("./blah.nofile", (err, data) => { 64 | if (err) throw err; 65 | next(null, data); 66 | }); 67 | } 68 | 69 | try { 70 | readFileThenDo((_, data) => console.log(data)); 71 | } catch (err) { 72 | console.error(err); 73 | } 74 | ``` 75 | -------------------------------------------------------------------------------- /node/callbacks.quizz.md: -------------------------------------------------------------------------------- 1 | # Question 1 2 | 3 | The below code errors when you run it. 4 | 5 | Make it run without errors but you cannot change the location of the `let` statement, that has to stay at the end. 6 | 7 | ```js 8 | function doAsyncTask(cb) { 9 | cb(); 10 | } 11 | doAsyncTask(_ => console.log(message)); 12 | 13 | let message = "Callback Called"; 14 | ``` 15 | 16 | # Question 2 17 | 18 | The below code swallows the error and doesn't pass it up the chain, make it pass the error up the stack using the next callback. 19 | 20 | ```js 21 | const fs = require("fs"); 22 | 23 | function readFileThenDo(next) { 24 | fs.readFile("./blah.nofile", (err, data) => { 25 | next(data); 26 | }); 27 | } 28 | 29 | readFileThenDo(data => { 30 | console.log(data); 31 | }); 32 | ``` 33 | 34 | # Question 3 35 | 36 | Instead of passing it up the stack throw it instead and try to catch it later on. 37 | 38 | ```js 39 | const fs = require("fs"); 40 | 41 | function readFileThenDo(next) { 42 | fs.readFile("./blah.nofile", (err, data) => { 43 | if (err) throw err; 44 | next(data); 45 | }); 46 | } 47 | // Hint use try..catch 48 | readFileThenDo(data => { 49 | console.log(data); 50 | }); 51 | ``` 52 | -------------------------------------------------------------------------------- /node/files/demofile.other.txt: -------------------------------------------------------------------------------- 1 | hello i am a another file -------------------------------------------------------------------------------- /node/files/demofile.txt: -------------------------------------------------------------------------------- 1 | hello i am a file -------------------------------------------------------------------------------- /node/generators.md: -------------------------------------------------------------------------------- 1 | # Generators 2 | 3 | ## RunToCompletion vs RunStopRun 4 | 5 | * We've assumed something fundamental, one a function starts running it will complete/error/return before any other JS code can run. 6 | * A _generator_ is a function that can be paused in the middle of running, let you do something else, and then resumed later on from exactly the point it was paused. 7 | * Nothing can pause a generator from the outside, only a generator can pause itself by using the `yield` keyword. 8 | * Once it's yielded though only the code it yielded to can resume it's function. 9 | 10 | ## Simple example to show how yield can pause execution midflow 11 | 12 | ```js 13 | function* demo() { 14 | console.log("1"); 15 | yield; 16 | console.log("2"); 17 | } 18 | console.log("start"); 19 | const it = demo(); // Doesn't execute the body of the function 20 | console.log("before iteration"); 21 | console.log(it.next()); // Executes generator and prints out whats yielded 22 | console.log(it.next()); // Returns done: true 23 | console.log(it.next()); // Returns same ended iterator 24 | console.log("after iteration"); 25 | ``` 26 | 27 | ## How to pass out data with yield 28 | 29 | ```js 30 | function* range() { 31 | for (let i = 0; i < 4; i++) { 32 | yield i; // <-- We can return data from yield 33 | } 34 | yield "moo"; 35 | return "foo"; 36 | } 37 | const it = range(); 38 | console.log(it.next()); // Prints the object 39 | console.log(it.next()); 40 | console.log(it.next()); 41 | console.log(it.next()); 42 | console.log(it.next()); 43 | console.log(it.next()); 44 | console.log(it.next()); 45 | ``` 46 | 47 | ## Use as an Iterator 48 | 49 | ```js 50 | function* range() { 51 | for (let i = 0; i < 10; i++) { 52 | yield i; 53 | } 54 | } 55 | 56 | for (let x of range()) { 57 | console.log(x); // Just prints the value 58 | } 59 | ``` 60 | 61 | ## yield can be used to communicate both ways 62 | 63 | ```js 64 | function* sayWhat() { 65 | console.log(yield); 66 | console.log("World"); 67 | } 68 | const it = sayWhat(); 69 | it.next(); // First yield, pauses 70 | it.next("Hello"); // Can pass in data again 71 | ``` 72 | 73 | ## Custom Async Generators 74 | 75 | We can combine `generators` and `for-await-of` into new interesting contructs like so: 76 | 77 | ```js 78 | function* range() { 79 | for (let i = 0; i < 10; i++) { 80 | yield Promise.resolve(i); 81 | } 82 | } 83 | 84 | (async () => { 85 | for (let x of range()) { 86 | console.log(x); // <-- This just prints out the promise 87 | } 88 | })(); 89 | ``` 90 | 91 | The above just prints out the promise, you can await it if you want but you can also await in the iterator itself 92 | 93 | ```js 94 | function* range() { 95 | for (let i = 0; i < 10; i++) { 96 | yield Promise.resolve(i); 97 | } 98 | } 99 | 100 | (async () => { 101 | for await (let x of range()) { 102 | // <-- Await in the iterator 103 | console.log(x); 104 | } 105 | })(); 106 | ``` 107 | 108 | 109 | -------------------------------------------------------------------------------- /node/generators.quizz.answer.md: -------------------------------------------------------------------------------- 1 | # Answer 1 2 | 3 | ```js 4 | const util = require("util"); 5 | const fs = require("fs"); 6 | const readFile = util.promisify(fs.readFile); 7 | 8 | function* fileLoader(files) { 9 | const promises = files.map(name => readFile(name, "utf8")); 10 | for (let promise of promises) { 11 | yield promise; 12 | } 13 | } 14 | 15 | (async () => { 16 | for await (let contents of fileLoader([ 17 | "./files/demofile.txt2", 18 | "./files/demofile.other.txt" 19 | ])) { 20 | console.log(contents); 21 | } 22 | })(); 23 | ``` 24 | -------------------------------------------------------------------------------- /node/generators.quizz.md: -------------------------------------------------------------------------------- 1 | # Question 1 2 | 3 | Create a custom async generator that loops over the files that are passed in. 4 | 5 | ```js 6 | const util = require("util"); 7 | const fs = require("fs"); 8 | const readFile = util.promisify(fs.readFile); 9 | 10 | function* fileLoader(files) {...} 11 | 12 | (async () => { 13 | for await (let contents of fileLoader([ 14 | "./files/demofile.txt", 15 | "./files/demofile.other.txt" 16 | ])) { 17 | console.log(contents); 18 | } 19 | })(); 20 | ``` 21 | -------------------------------------------------------------------------------- /node/node-loop-example.js: -------------------------------------------------------------------------------- 1 | console.log("start"); 2 | const interval = setInterval(() => { 3 | debugger; 4 | console.log("setInterval"); 5 | }, 0); 6 | 7 | setTimeout(() => { 8 | debugger; 9 | console.log("setTimeout 1"); 10 | Promise.resolve() 11 | .then(() => { 12 | debugger; 13 | console.log("promise 3"); 14 | }) 15 | .then(() => { 16 | debugger; 17 | console.log("promise 4"); 18 | }) 19 | .then(() => { 20 | setTimeout(() => { 21 | debugger; 22 | console.log("setTimeout 2"); 23 | Promise.resolve() 24 | .then(() => { 25 | debugger; 26 | console.log("promise 5"); 27 | }) 28 | .then(() => { 29 | debugger; 30 | console.log("promise 6"); 31 | }) 32 | .then(() => { 33 | debugger; 34 | clearInterval(interval); 35 | }); 36 | }); 37 | }); 38 | }); 39 | 40 | Promise.resolve() 41 | .then(() => { 42 | debugger; 43 | console.log("promise 1"); 44 | }) 45 | .then(() => { 46 | debugger; 47 | console.log("promise 2"); 48 | }); 49 | 50 | console.log("end"); 51 | -------------------------------------------------------------------------------- /node/node-loop-quizz.answer.md: -------------------------------------------------------------------------------- 1 | # Answer 1 2 | 3 | ```js 4 | console.log("start"); 5 | const interval = setInterval(() => { 6 | console.log("setInterval 1"); 7 | Promise.resolve() 8 | .then(() => { 9 | console.log("promise 1"); 10 | }) 11 | .then(() => { 12 | console.log("promise 2"); 13 | clearInterval(interval); 14 | }); 15 | }, 0); 16 | 17 | console.log("end"); 18 | ``` 19 | 20 | # Answer 2 21 | 22 | ```js 23 | console.log("start"); 24 | const interval = setInterval(() => { 25 | console.log("setInterval 1"); 26 | Promise.resolve() 27 | .then(() => { 28 | console.log("promise 1"); 29 | setImmediate(() => { 30 | console.log("setImmediate 1"); 31 | Promise.resolve() 32 | .then(() => { 33 | console.log("promise 3"); 34 | }) 35 | .then(() => { 36 | console.log("promise 4"); 37 | }) 38 | .then(() => { 39 | clearInterval(interval); 40 | }); 41 | }); 42 | process.nextTick(() => console.log("processNextTick 1")); 43 | }) 44 | .then(() => { 45 | console.log("promise 2"); 46 | }); 47 | }, 0); 48 | 49 | console.log("end"); 50 | ``` 51 | -------------------------------------------------------------------------------- /node/node-loop-quizz.md: -------------------------------------------------------------------------------- 1 | # Quizz 1 2 | 3 | Using you're knowledge of the event loop, create a program which prints out the below. If the log line mentions a `setInterval` it must be printed inside a `setInterval` etc.. 4 | 5 | start 6 | end 7 | setInterval 1 8 | promise 1 9 | promise 2 10 | 11 | ```js 12 | console.log("start"); 13 | const interval = setInterval(() => { 14 | console.log("setInterval 1"); 15 | clearInterval(interval); 16 | }, 0); 17 | console.log("end"); 18 | ``` 19 | 20 | # Quizz 2 21 | 22 | Extend the previous example to print out the following log lines, use `process.nextTick` and `setImmediate` 23 | 24 | start 25 | end 26 | setInterval 1 27 | promise 1 28 | promise 2 29 | processNextTick 1 30 | setImmediate 1 31 | promise 3 32 | promise 4 33 | -------------------------------------------------------------------------------- /node/promises.md: -------------------------------------------------------------------------------- 1 | # Promise API 2 | 3 | In ES6 we have an alternative mechanism built into the language called a _promise_. 4 | 5 | A _promise_ is a _placeholder_ for a future value. 6 | 7 | It serves the same function as callbacks but has a nicer syntax and makes it easier to handle errors. 8 | 9 | ## Creating a Promise 10 | 11 | We create an instance of a promise by calling `new` on the `Promise` class, like so: 12 | 13 | ```js 14 | const promise = new Promise((resolve, reject) => { 15 | // resolve? reject? 16 | }); 17 | ``` 18 | 19 | Inside this inner function we perform our asynchronous processing and then when we are ready we call `resolve()`, like so: 20 | 21 | ```js 22 | const promise = new Promise((resolve, reject) => { 23 | setTimeout(() => { 24 | console.log("Async Work Complete"); 25 | resolve(); // <-- Resolving 26 | }, 1000); 27 | }); 28 | ``` 29 | 30 | We usually return this promise from a function, like so: 31 | 32 | ```js 33 | function doAsyncTask() { 34 | // <-- NOTE: Not passing in a callback 35 | 36 | const promise = new Promise((resolve, reject) => { 37 | setTimeout(() => { 38 | console.log("Async Work Complete"); 39 | resolve(); // <-- Resolving 40 | }, 1000); 41 | }); 42 | return promise; // <-- Return the promise 43 | } 44 | ``` 45 | 46 | If there was an error in the async task then we call the `reject()` function like so: 47 | 48 | ```js 49 | function doAsyncTask() { 50 | let error = false; 51 | const promise = new Promise((resolve, reject) => { 52 | setTimeout(() => { 53 | console.log("Async Work Complete"); 54 | if (error) { 55 | reject(); // <-- Rejecting 56 | } else { 57 | resolve(); 58 | } 59 | }, 1000); 60 | }); 61 | return promise; 62 | } 63 | ``` 64 | 65 | ## Promise Notifications 66 | 67 | We can get notified when a promise `resolves` by attaching a _success_ handler to its `then` function, like so: 68 | 69 | ```js 70 | doAsyncTask().then(() => console.log("Task Complete!")); 71 | ``` 72 | 73 | `then` can take two arguments, the second argument is a _error_ handler that gets called if the promise is `rejected`, like so: 74 | 75 | ```js 76 | doAsyncTask().then( 77 | () => console.log("Task Complete!"), 78 | () => console.log("Task Errored!") 79 | ); 80 | ``` 81 | 82 | Any values we pass to the `resolve` and `reject` functions are passed along to the _error_ and _success_ handlers, like so: 83 | 84 | ```js 85 | function doAsyncTask() { 86 | let error = false; 87 | return new Promise((resolve, reject) => { 88 | setTimeout(() => { 89 | if (error) { 90 | reject("error"); // pass values 91 | } else { 92 | resolve("done"); // pass values 93 | } 94 | }, 1000); 95 | }); 96 | } 97 | 98 | doAsyncTask().then(val => console.log(val), err => console.error(err)); 99 | ``` 100 | 101 | 102 | 103 | ## Immediate Resolution or Rejection 104 | 105 | We can create an immediately _resolved_ Promise by using the `Promise.resolve()` method, like so: 106 | 107 | ```js 108 | let promise = Promise.resolve("done"); 109 | ``` 110 | 111 | And an immediately _rejected_ Promise by using the `Promise.reject()` method, like so: 112 | 113 | ```js 114 | let promise = Promise.reject("fail"); 115 | ``` 116 | 117 | One of the nice things about Promises is that if we add a `then` handler _after_ the promise resolves or rejects the handler _still_ gets called. 118 | 119 | ```js 120 | let promise = Promise.resolve("done"); 121 | 122 | let promise = Promise.resolve("done"); 123 | promise.then(val => console.log(val)); // 'done' 124 | ``` 125 | 126 | In the above example, even though the Promise has resolved _before_ we added the success handler, the promise framework still calls the success handler. 127 | 128 | ## Promise is really async 129 | 130 | ```js 131 | function doAsyncTask() { 132 | return Promise.resolve(); 133 | } 134 | 135 | doAsyncTask().then(_ => console.log(message)); // <-- Unlike callbacks, promises are always async 136 | let message = "Promise Resolved"; 137 | ``` 138 | 139 | ## Chaining 140 | 141 | We can also connect a series of `then` handlers together in a chain, like so: 142 | 143 | ```js 144 | const prom = Promise.resolve("done"); 145 | prom 146 | .then(val => { 147 | console.log(val); 148 | return "done2"; // <-- !NOTE: We have to return something, otherwise it doesn't get passed 149 | }) 150 | .then(val => console.log(val)); 151 | // 'done' 152 | // 'done2' 153 | ``` 154 | 155 | * We **have** to return something from each `then`, otherwise it doesn't get passed to the next `then` 156 | 157 | ```js 158 | const prom = Promise.resolve("done"); 159 | prom 160 | .then(val => { 161 | console.log(val); 162 | }) 163 | .then(val => console.log(val)); 164 | // 'done' 165 | // 'undefined' 166 | ``` 167 | 168 | * This is different to forking a promise chain 169 | 170 | ```js 171 | const prom = Promise.resolve("done"); 172 | prom.then(val => { 173 | console.log(val); 174 | return "done2"; 175 | }); 176 | 177 | prom.then(val => console.log(val)); // <-- Doesn't get passed the result of the previous then 178 | // 'done' 179 | // 'done' 180 | ``` 181 | 182 | 183 | 184 | We can also pause execution waiting for another promise to resolve 185 | 186 | ```js 187 | Promise.resolve("done") 188 | .then(val => { 189 | console.log(val); 190 | 191 | return new Promise(resolve => { 192 | setTimeout(() => resolve("done2"), 1000); 193 | }); 194 | 195 | // The next then waits for this promise to resolve before continueing 196 | }) 197 | .then(val => console.log(val)); 198 | ``` 199 | 200 | 201 | 202 | ## Error Handling 203 | 204 | Promises pass an error along the chain till it finds an error handler. So we don't need to define an error handler for each `then` function, we can just add one at the end like so: 205 | 206 | ```js 207 | Promise.reject("fail") 208 | .then(val => console.log(val)) // <-- Note we dont have an error handler here! 209 | .then(val => console.log(val), err => console.error(err)); 210 | ``` 211 | 212 | If we _throw_ an exception from our promise function or one of the success handlers, the promise gets rejected and the error handler is called, like so: 213 | 214 | ```js 215 | new Promise((resolve, reject) => { 216 | throw "fail"; 217 | }) 218 | .then(val => { 219 | console.log(val); 220 | }) 221 | .then(val => console.log(val), err => console.error(err)); 222 | // [Error: fail] 223 | ``` 224 | 225 | ```js 226 | Promise.resolve("done") 227 | .then(val => { 228 | throw "fail"; 229 | }) 230 | .then(val => console.log(val), err => console.error(err)); 231 | // [Error: fail] 232 | ``` 233 | 234 | The `catch` function works exactly the same way as the `then` error handler, it's just clearer and more explicitly describes our intent to handle errors. 235 | 236 | ```js 237 | Promise.resolve("done") 238 | .then(val => { 239 | throw "fail"; 240 | }) 241 | .then(val => console.log(val)) 242 | .catch(err => console.error(err)); 243 | ``` 244 | 245 | 246 | 247 | ## Finally 248 | 249 | In preview 250 | 251 | https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally 252 | 253 | ```js 254 | Promise.resolve("done") 255 | .then(val => { 256 | throw new Error("fail"); 257 | }) 258 | .then(val => console.log(val)) 259 | .catch(err => console.error(err)) 260 | .finally(_ => console.log("Cleaning Up")); // <-- Comming soon! 261 | ``` 262 | 263 | ## Multiple Promises 264 | 265 | ### Promise.all 266 | 267 | ```js 268 | const util = require("util"); 269 | const fs = require("fs"); 270 | const readFile = util.promisify(fs.readFile); 271 | 272 | const files = ["./files/demofile.txt", "./files/demofile.other.txt"]; 273 | 274 | let promises = files.map(name => readFile(name, "utf8")); 275 | Promise.all(promises) 276 | .then(values => { 277 | // <-- Uses .all 278 | console.log(values); 279 | }) 280 | .catch(err => console.error("Error: ", err)); 281 | ``` 282 | 283 | ### Promise.race 284 | 285 | Resolves or rejects when the first promise in the array resolved or rejects 286 | 287 | ```js 288 | let car1 = new Promise(resolve => setTimeout(resolve, 1000, "Car 1.")); 289 | let car2 = new Promise(resolve => setTimeout(resolve, 2000, "Car 2.")); 290 | let car3 = new Promise(resolve => setTimeout(resolve, 3000, "Car 3.")); 291 | 292 | Promise.race([car1, car2, car3]).then(value => { 293 | console.log("Promise Resolved", value); 294 | }); 295 | ``` 296 | 297 | 298 | 299 | ```js 300 | let car1 = new Promise((_, reject) => 301 | setTimeout(reject, 3000, "Car 1 Crashed.") 302 | ); 303 | let car2 = new Promise(resolve => setTimeout(resolve, 1000, "Car 2.")); 304 | let car3 = new Promise(resolve => setTimeout(resolve, 3000, "Car 3.")); 305 | 306 | Promise.race([car1, car2, car3]) 307 | .then(value => { 308 | console.log("Promise Resolved", value); 309 | }) 310 | .catch(err => { 311 | console.log("Promise Rejected", err); 312 | }); 313 | ``` 314 | 315 | 316 | -------------------------------------------------------------------------------- /node/promises.quizz.answer.md: -------------------------------------------------------------------------------- 1 | # Answer 1 2 | 3 | Create a promise version of the async readFile 4 | 5 | ```js 6 | const fs = require("fs"); 7 | // const util = require("util"); 8 | 9 | // const readFile = util.promisify(fs.readFile); 10 | 11 | function readFile(filename, encoding) { 12 | return new Promise((resolve, reject) => { 13 | fs.readFile(filename, encoding, (err, data) => { 14 | if (err) reject(err); 15 | resolve(data); 16 | }); 17 | }); 18 | } 19 | readFile("./files/demofile.txt", "utf-8").then( 20 | data => console.log("File Read", data), 21 | err => console.error("Failed To Read File", err) 22 | ); 23 | ``` 24 | 25 | # Answer 2 26 | 27 | ```js 28 | const fs = require("fs"); 29 | const zlib = require("zlib"); 30 | const util = require("util"); 31 | 32 | // const readFile = util.promisify(fs.readFile); 33 | // const gzip = util.promisify(zlib.gzip); 34 | 35 | function gzip(data) { 36 | return new Promise((resolve, reject) => { 37 | zlib.gzip(data, (err, result) => { 38 | if (err) return reject(err); 39 | resolve(result); 40 | }); 41 | }); 42 | } 43 | 44 | function readFile(filename, encoding) { 45 | return new Promise((resolve, reject) => { 46 | fs.readFile(filename, encoding, (err, data) => { 47 | if (err) return reject(err); 48 | resolve(data); 49 | }); 50 | }); 51 | } 52 | 53 | // Starting to look like callback hell? 54 | readFile("./files/demofile.txt", "utf-8").then( 55 | data => { 56 | gzip(data).then( 57 | res => console.log(res), 58 | err => console.error("Failed To Zip", err) 59 | ); 60 | }, 61 | err => console.error("Failed To Read", err) 62 | ); 63 | ``` 64 | 65 | # Answer 3 66 | 67 | ```js 68 | const fs = require("fs"); 69 | const zlib = require("zlib"); 70 | const util = require("util"); 71 | 72 | const readFile = util.promisify(fs.readFile); 73 | const gzip = util.promisify(zlib.gzip); 74 | 75 | readFile("./files/demofile.txt", "utf-8") 76 | .then( 77 | data => { 78 | return gzip(data); 79 | }, 80 | err => { 81 | console.error("Failed To Read", err); 82 | } 83 | ) 84 | .then( 85 | data => { 86 | console.log(data); 87 | }, 88 | err => { 89 | console.error("Failed To Zip", err); 90 | } 91 | ); 92 | ``` 93 | 94 | # Answer 4 95 | 96 | ```js 97 | const fs = require("fs"); 98 | const zlib = require("zlib"); 99 | const util = require("util"); 100 | 101 | function gzip(data) { 102 | return new Promise((resolve, reject) => { 103 | zlib.gzip(data, (err, result) => { 104 | if (err) return reject(err); 105 | resolve(result); 106 | }); 107 | }); 108 | } 109 | 110 | function readFile(filename, encoding) { 111 | return new Promise((resolve, reject) => { 112 | fs.readFile(filename, encoding, (err, data) => { 113 | if (err) return reject(err); 114 | resolve(data); 115 | }); 116 | }); 117 | } 118 | 119 | readFile("./demofile.txt2", "utf-8") 120 | .then(data => { 121 | return gzip(data); 122 | }) 123 | .then(data => { 124 | console.log(data); 125 | }) 126 | .catch(err => { 127 | console.error("Failed", err); 128 | }); 129 | ``` 130 | 131 | - Again, throw doesn't work as you expect. 132 | 133 | ```js 134 | const fs = require("fs"); 135 | const zlib = require("zlib"); 136 | const util = require("util"); 137 | 138 | function gzip(data) { 139 | return new Promise((resolve, reject) => { 140 | zlib.gzip(data, (err, result) => { 141 | if (err) return reject(err); 142 | resolve(result); 143 | }); 144 | }); 145 | } 146 | 147 | function readFile(filename, encoding) { 148 | return new Promise((resolve, reject) => { 149 | fs.readFile(filename, encoding, (err, data) => { 150 | if (err) throw err; // <-- Same as before this doesn't work, it's async 151 | resolve(data); 152 | }); 153 | }); 154 | } 155 | 156 | readFile("./demofile.txt2", "utf-8") 157 | .then(data => { 158 | return gzip(data); 159 | }) 160 | .then(data => { 161 | console.log(data); 162 | }) 163 | .catch(err => { 164 | console.error("Failed", err); 165 | }); 166 | ``` 167 | 168 | # Answer 5 169 | 170 | ```js 171 | function readFileFake(sleep) { 172 | return new Promise(resolve => setTimeout(resolve, sleep, "read")); 173 | } 174 | 175 | function timeout(sleep) { 176 | return new Promise((_, reject) => setTimeout(reject, sleep, "timeout")); 177 | } 178 | 179 | Promise.race([readFileFake(5000), timeout(1000)]) 180 | .then(data => console.log(data)) 181 | .catch(err => console.error(err)); 182 | ``` 183 | 184 | # Answer 6 185 | 186 | ```js 187 | function authenticate() { 188 | console.log("Authenticating"); 189 | return new Promise(resolve => setTimeout(resolve, 2000, { status: 200 })); 190 | } 191 | 192 | function publish() { 193 | console.log("Publishing"); 194 | return new Promise(resolve => setTimeout(resolve, 2000, { status: 403 })); 195 | } 196 | 197 | function timeout(sleep) { 198 | return new Promise((resolve, reject) => setTimeout(reject, sleep, "timeout")); 199 | } 200 | 201 | Promise.race([publish(), timeout(1000)]) 202 | .then(res => { 203 | if (res.status === 403) { 204 | return authenticate(); 205 | } 206 | return res; 207 | }) 208 | .then(res => { 209 | // Process save responce 210 | console.log("Published"); 211 | }) 212 | .catch(err => { 213 | if (err === "timeout") { 214 | console.error("Request timed out"); 215 | } else { 216 | console.error(err); 217 | } 218 | }); 219 | ``` 220 | 221 | Alternative answer with safePublish returning a publish promise 222 | 223 | ```js 224 | function authenticate() { 225 | console.log("Authenticating"); 226 | return new Promise(resolve => setTimeout(resolve, 2000, { status: 200 })); 227 | } 228 | 229 | function publish() { 230 | console.log("Publishing"); 231 | return new Promise(resolve => setTimeout(resolve, 2000, { status: 403 })); 232 | } 233 | 234 | function timeout(sleep) { 235 | return new Promise((resolve, reject) => setTimeout(reject, sleep, "timeout")); 236 | } 237 | 238 | function safePublish() { 239 | return publish().then(res => { 240 | if (res.status === 403) { 241 | return authenticate(); 242 | } 243 | return res; 244 | }); 245 | } 246 | 247 | Promise.race([safePublish(), timeout(1000)]) 248 | .then(res => { 249 | // Process save responce 250 | console.log("Published"); 251 | }) 252 | .catch(err => { 253 | if (err === "timeout") { 254 | console.error("Request timed out"); 255 | } else { 256 | console.error(err); 257 | } 258 | }); 259 | ``` 260 | -------------------------------------------------------------------------------- /node/promises.quizz.md: -------------------------------------------------------------------------------- 1 | # Question 1 - (10min) 2 | 3 | Create a promise version of the async readFile function 4 | 5 | ```js 6 | const fs = require("fs"); 7 | 8 | function readFile(filename, encoding) { 9 | fs.readFile(filename, encoding, (err, data) => { 10 | //TODO 11 | }); 12 | } 13 | readFile("./files/demofile.txt", "utf-8") 14 | .then(...) 15 | }); 16 | ``` 17 | 18 | # Question 2 19 | 20 | Load a file from disk using readFile and then compress it using the async zlib node library, use a promise chain to process this work. 21 | 22 | ```js 23 | const fs = require("fs"); 24 | const zlib = require("zlib"); 25 | 26 | function zlibPromise(data) { 27 | zlib.gzip(data, (error, result) => { 28 | //TODO 29 | }); 30 | } 31 | 32 | function readFile(filename, encoding) { 33 | return new Promise((resolve, reject) => { 34 | fs.readFile(filename, encoding, (err, data) => { 35 | if (err) reject(err); 36 | resolve(data); 37 | }); 38 | }); 39 | } 40 | 41 | readFile("./files/demofile.txt", "utf-8") 42 | .then(...) // --> Load it then zip it and then print it to screen 43 | }); 44 | ``` 45 | 46 | # Question 3 47 | 48 | Convert the previous code so that it now chains the promise as well. 49 | 50 | # Question 4 51 | 52 | Convert the previous code so that it now handles errors using the catch handler 53 | 54 | # Question 5 55 | 56 | Create some code that tries to read from disk a file and times out if it takes longer than 1 seconds, use `Promise.race` 57 | 58 | ```js 59 | function readFileFake(sleep) { 60 | return new Promise(resolve => setTimeout(resolve, sleep)); 61 | } 62 | 63 | readFileFake(5000); // This resolves a promise after 5 seconds, pretend it's a large file being read from disk 64 | ``` 65 | 66 | # Question 6 67 | 68 | Create a process flow which publishes a file from a server, then realises the user needs to login, then makes a login request, the whole chain should error out if it takes longer than 1 seconds. Use `catch` to handle errors and timeouts. 69 | 70 | ```js 71 | function authenticate() { 72 | console.log("Authenticating"); 73 | return new Promise(resolve => setTimeout(resolve, 2000, { status: 200 })); 74 | } 75 | 76 | function publish() { 77 | console.log("Publishing"); 78 | return new Promise(resolve => setTimeout(resolve, 2000, { status: 403 })); 79 | } 80 | 81 | function timeout(sleep) { 82 | return new Promise((resolve, reject) => setTimeout(reject, sleep, "timeout")); 83 | } 84 | 85 | Promise.race( [publish(), timeout(3000)]) 86 | .then(...) 87 | .then(...) 88 | .catch(...); 89 | ``` 90 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workshop", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1 5 | } 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workshop", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": {}, 12 | "devDependencies": {} 13 | } 14 | --------------------------------------------------------------------------------