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.
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.
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.
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.
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.
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 |
--------------------------------------------------------------------------------