├── 1. Hello World ├── solution │ └── solution.js └── walkthrough.md ├── 2. Baby Steps ├── solution │ └── solution.js └── walkthrough.md ├── 3. My First IO ├── solution │ └── solution.js └── walkthrough.md ├── 4. My First Asynchronous IO ├── solution │ └── solution.js └── walkthrough.md ├── 5. Filtered ls ├── solution │ └── solution.js └── walkthrough.md ├── 6. Make it Modular ├── solution │ ├── solution.js │ └── solution_filter.js └── walkthrough.md ├── 7. HTTP Client ├── solution │ └── solution.js └── walkthrough.md ├── 8. HTTP Collect ├── solution │ └── solution.js └── walkthrough.md ├── 9. Juggling Async ├── solution │ └── solution.js └── walkthrough.md └── README.md /1. Hello World/solution/solution.js: -------------------------------------------------------------------------------- 1 | console.log("HELLO WORLD") -------------------------------------------------------------------------------- /1. Hello World/walkthrough.md: -------------------------------------------------------------------------------- 1 | # Exercise 1 - Hello World 2 | 3 | Ok, so you have probably seen a few tutorials showing you how to get the message Hello World to display somewhere. You are now going to see at least one more. 4 | 5 | # Problem 6 | 7 | > Write a program that prints the text "HELLO WORLD" to the console (stdout). 8 | 9 | This is an easy task just to get you started on your quest for node mastery. It allows me to also demostrate the general format of each walthrough. Lets start by looking at the hints given to us by the program. 10 | 11 | # Hints 12 | 13 | > To make Node.js program, create a new file with a `.js` extension and start writing JavaScript! Execute your program by running it with the 14 | `node` command. e.g.: 15 | 16 | ```sh 17 | $ node program.js 18 | ``` 19 | 20 | So this first part tells you how to make a node script and then run it in node. So go ahead and make your file. I called mine helloworld.js 21 | 22 | > You can write to the console in the same way as in the browser: 23 | 24 | ```js 25 | console.log("text") 26 | ``` 27 | This here is basically giving us the answer. The console.log() function will take the object inside the brackets and print it out to the console. So in your file you can type `console.log("HELLO WORLD")`. Save it. 28 | 29 | > When you are done, you must run: 30 | 31 | ```sh 32 | $ {appname} verify program.js 33 | ``` 34 | 35 | So for your app you will type: 36 | 37 | ```sh 38 | $ learnyounode verify helloworld.js 39 | ``` 40 | 41 | > to proceed. Your program will be tested, a report will be generated, and the lesson will be marked 'completed' if you are successful. 42 | 43 | # Recap 44 | 45 | 1. Node programs are javascript files. 46 | 2. You run your program using `$ node programmename.js` 47 | 3. Your can write to the console the same as in the browser. 48 | 4. `console.log()` prints the contents of the brackets to the console. 49 | 5. You verify a program using `$ learnyounode verify programname.js` 50 | 51 | That's all for this tutorial. 52 | -------------------------------------------------------------------------------- /2. Baby Steps/solution/solution.js: -------------------------------------------------------------------------------- 1 | var result = 0 2 | 3 | for (var i = 2; i < process.argv.length; i++) 4 | result += Number(process.argv[i]) 5 | 6 | console.log(result) -------------------------------------------------------------------------------- /2. Baby Steps/walkthrough.md: -------------------------------------------------------------------------------- 1 | # Exercise 2 - Baby Steps 2 | 3 | This can be a fun exercise to introduce using command line arguments and arrays. 4 | 5 | # Problem 6 | 7 | > Write a program that accepts one or more numbers as command-line arguments and prints the sum of those numbers to the console (stdout). 8 | 9 | So, lets consider what we need for this exercise. We need to be able to access data in the command line. Then we need to select parts of that data and perform a mathematical sum. Then we can `console.log()` the answer to that sum. Now we know what we need, let's look at the hints given to us. 10 | 11 | # Hints 12 | 13 | > You can access command-line arguments via the global `process` object. The `process` object has an `argv` property which is an array containing the complete command-line. i.e. `process.argv`. 14 | 15 | Let's go through the `process.argv` command in greater detail. 16 | 17 | ## process.argv 18 | 19 | An array containing the command line arguments. The first element will be 'node', the second element will be the name of the JavaScript file and directory. The next elements will be any additional command line arguments. 20 | 21 | // print process.argv 22 | process.argv.forEach(function(val, index, array) { 23 | console.log(index + ': ' + val); 24 | }); 25 | 26 | This will generate: 27 | 28 | $ node process-2.js one two=three four 29 | 0: node 30 | 1: /Users/mjr/work/node/process-2.js 31 | 2: one 32 | 3: two=three 33 | 4: four 34 | 35 | So that might be a bit confusing, but thats whats in the official documentation at [nodejs.org](http://nodejs.org/api/process.html#process_process). 36 | 37 | > To get started, write a program that simply contains: 38 | 39 | ```js 40 | console.log(process.argv) 41 | ``` 42 | 43 | > Run it with `node program.js` and some numbers as arguments. e.g: 44 | 45 | ```sh 46 | $ node program.js 1 2 3 47 | ``` 48 | 49 | > In which case the output would be an array looking something like: 50 | 51 | ```js 52 | [ 'node', '/path/to/your/program.js', '1', '2', '3' ] 53 | ``` 54 | 55 | ## For Loops 56 | 57 | > You'll need to think about how to loop through the number arguments so you can output just their sum. The first element of the process.argv array is always 'node', and the second element is always the path to your program.js file, so you need to start at the 3rd element (index 2), adding each item to the total until you reach the end of the array. 58 | 59 | So in simple terms, we don't want to use the first two objects in the array. Let's look at the For loop. 60 | 61 | The For loop is structured as follows: 62 | 63 | for (statement1; statement2; statement3) { 64 | doSomething; 65 | } 66 | 67 | Statement1 is a variable declaration. Statement2 is a condition to run the script. Statement3 is something to be done after the script runs. doSomething is the script to run if the condition is true. 68 | 69 | Our For loop will look like this: 70 | 71 | for (var i = 2; i < process.argv.length; i++) { 72 | result += Number(process.argv[i]); 73 | } 74 | 75 | So allow me to explain. 76 | 77 | 1. Statement1 `var i = 2`: we have created the variable i which has the starting value of 2. We start at value 2 because we want to ignore the first 2 objects in our array (node and the file name). 78 | 2. Statement2 `i < process.argv.length`: The .length function gives us the length of our array. So if we had the array `[ 'node', '/path/to/your/program.js', '1', '2', '3' ]` the length would be 5. So our statement means only run the script if i is less than 5. This has 2 effects. The first thing is does is ensure that the code only runs when we have passed arguments through the command line. If we only type `$ node program.js` there will be no numbers and so no need to run the script. The second thing this does is give us the ability to stop the For loop. 79 | 3. Statement3 `i++`: This actually closes the for loop. If you don't close a For loop you can get an infinite loop. Worst case scenario, you crash your server. `i++` means add 1 to the value of i and reassign this to i. This is a shortened version of `i = i + 1`. So if the condition is true, the script is run, then statement3 runs. When the final number is added the value of i will be the same as the length of the array, meaning the condition in statement2 is no longer true and the loop stops. 80 | 81 | > Also be aware that all elements of `process.argv` are strings and you may need to *coerce* them into numbers. You can do this by prefixing the property with `+` or passing it to `Number()`. e.g. `+process.argv[2]` or `Number(process.argv[2])`. 82 | 83 | doSomething `result += Number(process.argv[i])` I'm going to break this down: 84 | 85 | - `result`: an undefined variable 86 | - `+=`: add and reassign. So the number on the right hand side is added to the value of the variable on the left hand side. Then the new value is given to the variable. For example: if my variable `shoes` has a value of 3, then the line `shoes += 4` will change the value to 7. 87 | - `Number()`: converts the contents of the brackets into its number equivelant. For example `Number("4")` gives me the value 4, `Number(false)` gives me the value 0. This is necessary as the arguments of process.argv are not numbers, they are strings. 88 | - `process.argv[i]`: The object in position i of the array. This means that depending on how many times the For loop runs, the value of i will reflect a different object inside the array. 89 | 90 | So pulling all this together means, add the number value of current object in the array to our variable result. By looking back with your new found knowledge you should be able to understand the logic of the For loop and what it is doing. In short, the loop is selecting number arguments in the array and adding it to a running total. 91 | 92 | We are not quite finished. The result variable is still undefined. Logically, the result variale should start at 0, as if there are no number arguments given, the answer is 0. So At the top of your script: 93 | 94 | var result = 0; 95 | 96 | And then we use our `console.log()` to print out the result variable. 97 | 98 | console.log(result); 99 | 100 | > {appname} will be supplying arguments to your program when you run `{appname} verify program.js` so you don't need to supply them yourself. To test your program without verifying it, you can invoke it with `{appname} run program.js`. When you use `run`, you are invoking the test environment that {appname} sets up for each exercise. 101 | 102 | So to run with some arguments try: 103 | 104 | $ learnyounode program.js 1 2 3 4 105 | 106 | Which should give you the result 10. And verify with: 107 | 108 | ``` sh 109 | $ learnyounode verify program.js 110 | ``` 111 | # Recap 112 | 113 | So to recap: 114 | 115 | 1. `process.argv` gives an array of the command line arguments. 116 | 2. A For loop consists of a variable declaration, a condition, a task to run if the condition is true and another task to run after this to close the loop. 117 | 3. You can declare variables using var {variablename} = {value} 118 | 4. You can use inequality symbols for your conditional statement 119 | 5. `++` adds 1 to the value of the variable and reassigns this new value to the variable. 120 | 6. `+=` adds the value on the left to the value of the variable on the right and reassigns this. 121 | 7. `Number()` returns the number value of the contents fo the brackets. 122 | 8. Using the square brackets [ ] you can select a specific item in an array. 123 | 124 | That's all for this tutorial. -------------------------------------------------------------------------------- /3. My First IO/solution/solution.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | 3 | var contents = fs.readFileSync(process.argv[2]) 4 | var lines = contents.toString().split('\n').length - 1 5 | console.log(lines) 6 | 7 | // note you can avoid the .toString() by passing 'utf8' as the 8 | // second argument to readFileSync, then you'll get a String! 9 | // 10 | // fs.readFileSync(process.argv[2], 'utf8').split('\n').length - 1 -------------------------------------------------------------------------------- /3. My First IO/walkthrough.md: -------------------------------------------------------------------------------- 1 | # Exercise 3 - My First I/O 2 | 3 | In this exercise you are going to be using a sychronous program to access a file system. 4 | 5 | # Problem 6 | 7 | > Write a program that uses a single **synchronous** filesystem operation to read a file and print the number of newlines it contains to the console (stdout), similar to running `cat file | wc -l`. 8 | 9 | > The full path to the file to read will be provided as the first command-line argument. 10 | 11 | Before we dive in, lets understand the word synchronous. Anyone who has done some background reading on node.js knows that the beauty of node is the ability to run asynchronous functions. So why are we making a program that doesn’t take advantage of this? Just to make a point, thats why! 12 | 13 | So, we need to be able to access the command line arguments. We need to be able to access the contents of a file. We need to be able to search the file for how many newlines it has. Lets take a look at hints given to us. 14 | 15 | # Hints 16 | 17 | Node has a simple module loading system. In Node, files and modules are in one-to-one correspondence. 18 | 19 | > To perform a filesystem operation you are going to need the `fs` module from the Node core library. To load this kind of module, or any other "global" module, use the following incantation: 20 | 21 | ```js 22 | var fs = require('fs') 23 | ``` 24 | 25 | So this is the first line of our script. Sometimes they just give it away! In short, this module loads all the required dependencies the program needs for filesystem functions. 26 | 27 | > Now you have the full `fs` module available in a variable named `fs`. 28 | 29 | > All synchronous (or blocking) filesystem methods in the `fs` module end with 'Sync'. To read a file, you'll need to use `fs.readFileSync('/path/to/file')`. This method will *return* a `Buffer` object containing the complete contents of the file. 30 | 31 | Seeing as we are reading a file that has been given as an argument in the command line, we can use our `process.argv` function that we used in the previous exercise. 32 | 33 | var contents = fs.readFileSync(process.argv[2]) 34 | 35 | So, accessing the filesystem module, use the command `readFileSync()` on the 3rd item argument in our command line which should be a file directory. Then assign the contents of this file to the variable `contents`. 36 | 37 | > Documentation on the `fs` module can be found by pointing your browser here: 38 | {rootdir:/node_apidoc/fs.html} 39 | 40 | > `Buffer` objects are Node's way of efficiently representing arbitrary arrays of data, whether it be ascii, binary or some other format. 41 | > `Buffer` objects can be converted to strings by simply calling the `toString()` method on them. e.g. `var str = buf.toString()`. 42 | 43 | > Documentation on `Buffer`s can be found by pointing your browser here: 44 | {rootdir:/node_apidoc/buffer.html} 45 | 46 | Now we need to search the contents for new lines. The hints gives us a good tip on that. 47 | 48 | > If you're looking for an easy way to count the number of newlines in a string, recall that a JavaScript `String` can be `.split()` into an array of substrings and that '\n' can be used as a delimiter. Note that the test file does not have a newline character ('\n') at the end of the last line, so using this method you'll end up with an array that has one more element than the number of newlines. 49 | 50 | Using this information we can write our next line of code: 51 | 52 | var lines = contents.toString().split('\n').length - 1 53 | 54 | Let's go through this. 55 | 56 | 1. `var lines`: new variable. 57 | 2. `contents.toString()`: converts the array contents into a string. 58 | 3. `.split('\n')`: Split this string back into an array, each split should be at a newline (using the newline character '\n'). 59 | 4. `.length`: Return the length of the array. 60 | 5. `-1`: Deduct 1 from the result as the length will be 1 more than the number f newline characters as stated in the hints. 61 | 62 | Finally you can `console.log(lines)`. 63 | 64 | You can now run your program on any file in your directory using: 65 | 66 | $ node program.js {filename} 67 | 68 | And you can verify using: 69 | 70 | $ learnyounode verify program.js 71 | 72 | Completing the excersie. 73 | 74 | # Recap 75 | 76 | 1. `require` lets the machine know to use a specific module for your code to work 77 | 2. `readFileSync()` returns the contents of a file as a buffer/array object. 78 | 3. `toString` converts an array/buffer into a string 79 | 4. `split()` splits a string into an array, splitting up objects based on the contents of the brackets (). 80 | 5. `\n ` is the newline character in a file -------------------------------------------------------------------------------- /4. My First Asynchronous IO/solution/solution.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var file = process.argv[2] 3 | 4 | fs.readFile(file, function (err, contents) { 5 | // fs.readFile(file, 'utf8', callback) can also be used 6 | var lines = contents.toString().split('\n').length - 1 7 | console.log(lines) 8 | }) -------------------------------------------------------------------------------- /4. My First Asynchronous IO/walkthrough.md: -------------------------------------------------------------------------------- 1 | # Exercise 4 - My First Asynchronous I/O 2 | 3 | This exercise is the same as the previous exercise, except we are going to do it far more efficiently. In the previous exercise, the script is read and executed 1 line at a time. What this means is that if there is another function to be performed after the readFileSync, this new function will have to wait for that to finish. If the document being read is very large, this could take quite some time. 4 | 5 | In an asynchronous app, an event loop runs, which means that the readFile function will not block other functions from happening, it will just continue working whilst the rest of the script is loaded. 6 | 7 | # Problem 8 | 9 | > Write a program that uses a single **asynchronous** filesystem operation to read a file and print the number of newlines it contains to the console (stdout), similar to running `cat file | wc -l`. 10 | 11 | > The full path to the file to read will be provided as the first command-line argument. 12 | 13 | Easy peasy because you've already done this. SO what do we need? We need everything in the previous exercise, except we want to use an asynchronous process instead of readFileSync. Lets look at the hints given to us. 14 | 15 | # Hints 16 | 17 | > The solution to this problem is *almost* the same as the previous problem except you must now do it **the Node.js way**: asynchronous. 18 | 19 | > Instead of `fs.readFileSync()` you will want to use `fs.readFile()` and instead of using the return value of this method you need to collect the value from a callback function that you pass in as the second argument. 20 | 21 | So use `readFile(process.argv[2])` instead of `readFileSync(process.argv[2])`. But then we need a callback function to pass a second argument through. 22 | 23 | > Remember that idiomatic Node.js callbacks normally have the signature: 24 | 25 | ```js 26 | function callback (err, data) { /* ... */ } 27 | ``` 28 | 29 | Thats not really giving us much. Lets look at the documentation. 30 | 31 | ## fs.readFile(filename, [options], callback) 32 | 33 | - `filename` String 34 | - `options` Object 35 | - encoding `String | Null` default = `null` 36 | - flag `String` default = `'r'` 37 | - callback `Function` 38 | 39 | Asynchronously reads the entire contents of a file. Example: 40 | 41 | fs.readFile('/etc/passwd', function (err, data) { 42 | if (err) throw err; 43 | console.log(data); 44 | }); 45 | 46 | The callback is passed two arguments `(err, data)`, where `data` is the contents of the file. 47 | 48 | If no encoding is specified, then the raw buffer is returned. 49 | 50 | And thats the documentation. By the way, the first argument `err`, is the error handler. 51 | 52 | So, we can try: 53 | 54 | ```js 55 | fs.readFile(process.argv[2], function (err, contents) { 56 | doSomethingWithThisData; 57 | }) 58 | ``` 59 | 60 | We are getting closer. What is it that we want to do with the data after it has been read? We want to find out how many newlines are in it. So using our code from the previous exercise we can put in: 61 | 62 | ```js 63 | fs.readFile(process.argv[2], function (err, contents) { 64 | var lines = contents.toString().split('\n').length - 1; 65 | console.log(lines); 66 | }) 67 | ``` 68 | 69 | Now, we have an asychronous process doing the same thing as before. If you have a look at the official solution, you will see that `process.argv[2]` has been assigned to the variable `file`. This is good for reading the code and easily reusing the file name. 70 | 71 | If you are having problems, make sure you delcared your fs variable. You still need the file system module. 72 | 73 | # Recap 74 | 75 | 1. An asynchronous process allows the script to continue running. 76 | 2. The `readFile()` function is the asynchronous version of `readFileSync()`. 77 | 3. The `readFile()` takes the file name as its first parameter and a callback function as its last parameter. (You can have an options parameter but this is beyond the scope of this walkthrough). 78 | 4. The callback function takes 2 parameters, the error handler `err` and the data parameter which is your file contents. 79 | 80 | That's all for this walkthrough. -------------------------------------------------------------------------------- /5. Filtered ls/solution/solution.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var path = require('path') 3 | 4 | fs.readdir(process.argv[2], function (err, list) { 5 | list.forEach(function (file) { 6 | if (path.extname(file) === '.' + process.argv[3]) 7 | console.log(file) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /5. Filtered ls/walkthrough.md: -------------------------------------------------------------------------------- 1 | # Exercise 5 - Filtered ls 2 | 3 | This exercise will continue to use asynchronous methods. Directory manipulation will be a part of this. 4 | 5 | # Problem 6 | 7 | > Create a program that prints a list of files in a given directory, filtered by the extension of the files. You will be provided a directory name as the first argument to your program (e.g. '/path/to/dir/') and a file extension to filter by as the second argument. 8 | 9 | > For example, if you get 'txt' as the second argument then you will need to filter the list to only files that **end with .txt**. 10 | 11 | > The list of files should be printed to the console, one file per line. You **must** use asynchronous I/O. 12 | 13 | So for this we need to access command line arguments... again. Then we need to be able o read the contents of a directory and distinguish between files based on the extension. Shall we have a look at those lovely hints? 14 | 15 | # Hints 16 | 17 | > The `fs.readdir()` method takes a pathname as its first argument and a callback as its second. The callback signature is: 18 | 19 | ```js 20 | function callback (err, list) { /* ... */ } 21 | ``` 22 | 23 | > where `list` is an array of filename strings. 24 | 25 | > Documentation on the `fs` module can be found by pointing your browser here: 26 | {rootdir:/node_apidoc/fs.html} 27 | 28 | > You may also find node's `path` module helpful, particularly the `extname` method. 29 | 30 | > Documentation on the `path` module can be found by pointing your browser here: 31 | {rootdir:/node_apidoc/path.html} 32 | 33 | So the hints have introduced a few new ideas that we need to research. First off is the `readdir()` process. Not dissimilar to `readFile`, it can read a directory using the filesystem module. Using the standard method of node we have a callback function, which the second argument, our data being `list` which is an array of filenames as strings. 34 | 35 | We really need to take a look at the documentation on `path.extname`. 36 | 37 | ## path.extname(p) 38 | 39 | > Return the extension of the path, from the last '.' to end of string in the last portion of the path. If there is no '.' in the last portion of the path or the first character of it is '.', then it returns an empty string. Examples: 40 | 41 | ```js 42 | path.extname('index.html') 43 | // returns 44 | '.html' 45 | 46 | path.extname('index.coffee.md') 47 | // returns 48 | '.md' 49 | 50 | path.extname('index.') 51 | // returns 52 | '.' 53 | 54 | path.extname('index') 55 | // returns 56 | '' 57 | ``` 58 | 59 | So this allows us to look at the extension of each file in our `fs.readdir`. 60 | 61 | Lets have a go at writing the script. First create our modules as variables: 62 | 63 | ```js 64 | var fs = require('fs') 65 | var path = require('path') 66 | ``` 67 | 68 | Then use the asynchronous process `fs.readdir`: 69 | 70 | ```js 71 | fs.readdir(pocess.argv[2], function(err, list) { 72 | doSomething; 73 | }) 74 | ``` 75 | 76 | Now we want to read each file name for it's extension. So we can use a `forEach`: 77 | 78 | ```js 79 | fs.readdir(process.argv[2], function (err, list) { 80 | list.forEach(function (file) { 81 | doSomething; 82 | }) 83 | }) 84 | ``` 85 | 86 | Let me explain `forEach`. The `forEach()` method executes a provided function once per array element. The sytax to follow is: 87 | 88 | ```js 89 | array.forEach(callbackFunction(, argument) {}) 90 | ``` 91 | 92 | Full documentation can be found at [Mozilla.org](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach). 93 | 94 | So for each filename in the directory we want to check for the file extension that we specify in the command line. 95 | 96 | ```js 97 | fs.readdir(process.argv[2], function (err, list) { 98 | list.forEach(function (file) { 99 | if (path.extname(file) === '.' + process.argv[3]) 100 | console.log(file) 101 | }) 102 | }) 103 | ``` 104 | 105 | Enter the `if` statement. 106 | 107 | ## if statements 108 | 109 | I don't really need a subheading for this. 110 | 111 | ```js 112 | if (condition) 113 | doSomething 114 | ``` 115 | 116 | So if the condition is true, do something. If not... don't. `if` statements can be more complicated by providing `else` or `else if` but that is beyond the scope of this exercise. 117 | 118 | So our code reads, if the `path.extname()` of our `file` is equal to (`===`) the extension specified in our console, `console.log` the file name. There is also an extra bit: `'.' +` is simply adding the full stop for our file extension. Essentially you could give the full stop in your command line and omit this part of the code. So if the extension is the same as `.(process.argv[3])` then log it. 119 | 120 | # Recap 121 | 122 | 1. `readdir()`: reads the contents of a directory (the file names). 123 | 2. The `path` module allows gives access to file directory structure and manipulation. 124 | 3. `extname`: returns the part of a string that comes after the final period (fullstop), including the period itself. 125 | 4. `forEach`: executes a specified function for each element in an array. 126 | 5. an `if` statement consists of a condition to be met and a function to execute should the condition be true. 127 | 6. `===`: operator to say 'equal in every way'. This is not used for assigning values. This is used in place of inequalities. `===` specifically means to be entirely the same. Not to be confused with `==` or `=`. Its opposite is `!==` to mean 'not equal'. 128 | 7. `+`: prefixes the string on the right hand side with the string on the left hand side. Also used in math. 129 | 130 | That's all for this tutorial. 131 | -------------------------------------------------------------------------------- /6. Make it Modular/solution/solution.js: -------------------------------------------------------------------------------- 1 | var filterFn = require('./solution_filter.js') 2 | var dir = process.argv[2] 3 | var filterStr = process.argv[3] 4 | 5 | filterFn(dir, filterStr, function (err, list) { 6 | if (err) 7 | return console.error('There was an error:', err) 8 | 9 | list.forEach(function (file) { 10 | console.log(file) 11 | }) 12 | }) -------------------------------------------------------------------------------- /6. Make it Modular/solution/solution_filter.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var path = require('path') 3 | 4 | module.exports = function (dir, filterStr, callback) { 5 | 6 | fs.readdir(dir, function (err, list) { 7 | if (err) 8 | return callback(err) 9 | 10 | list = list.filter(function (file) { 11 | return path.extname(file) === '.' + filterStr 12 | }) 13 | 14 | callback(null, list) 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /6. Make it Modular/walkthrough.md: -------------------------------------------------------------------------------- 1 | # Exercise 6 - Make it Modular 2 | 3 | It is at this point in learnyounode that things can get very tricky. The task requires a huge leap in understanding of node. Using modules is a large part of software development, and writing your own is a prerequisite of being a *developer*. So far you have had the pleasure of using modules incorporated in node, but someone had to make those modules for you to use. Time to step into their shoes. 4 | 5 | # Problem 6 | 7 | This is quite a long script, but read through all of it and don't get hung up on parts you don't understand. We will go over everything in due course. 8 | 9 | > This problem is the same as the previous but introduces the concept of **modules**. You will need to create two files to solve this. 10 | 11 | > Create a program that prints a list of files in a given directory, filtered by the extension of the files. The first argument is the directory name and the second argument is the extension filter. Print the list of files (one file per line) to the console. You **must** use asynchronous I/O. 12 | 13 | > You must write a *module* file to do most of the work. The module must *export* a single function that takes **three** arguments: the directory name, the filename extension string and a callback function, in that order. The filename extension argument must be the same as was passed to your program. i.e. don't turn it into a RegExp or prefix with "." or do anything else but pass it to your module where you can do what you need to make your filter work. 14 | 15 | > The callback function must be called using the idiomatic node(err, data) convention. This convention stipulates that unless there's an error, the first argument passed to the callback will be null, and the second will be your data. In this case, the data will be your filtered list of files, as an Array. If you receive an error, e.g. from your call to `fs.readdir()`, the callback must be called with the error, and only the error, as the first argument. 16 | 17 | > You **must** not print directly to the console from your module file, only from your original program. 18 | 19 | > In the case of an error bubbling up to your original program file, simply check for it and print an informative message to the console. 20 | 21 | > These four things is the contract that your module must follow. 22 | 23 | > 1. Export a single function that takes exactly the arguments described. 24 | > 2. Call the callback exactly once with an error or some data as described. 25 | > 3. Don't change anything else, like global variables or stdout. 26 | > 4. Handle all the errors that may occur and pass them to the callback. 27 | 28 | > The benefit of having a contract is that your module can be used by anyone who expects this contract. So your module could be used by anyone else who does learnyounode, or the verifier, and just work. 29 | 30 | What we need then is to replicate the previous exercise, but putting our script into a module. This can be quite scary, and looking at the official solution is very unlikely to help you understanding at this point, so please don't be tempted just yet. Let's take a look at the hints. 31 | 32 | # Hints 33 | 34 | > Create a new module by creating a new file that just contains your directory reading and filtering function. To define a *single function* *export*, you assign your function to the `module.exports` object, overwriting what is already there: 35 | 36 | ```js 37 | module.exports = function (args) { /* ... */ } 38 | ``` 39 | 40 | > Or you can use a named function and assign the name. 41 | 42 | > To use your new module in your original program file, use the `require()` call in the same way that you `require('fs')` to load the `fs` module. The only difference is that for local modules must be prefixed with './'. So, if your file is named mymodule.js then: 43 | 44 | ```js 45 | var mymodule = require('./mymodule.js') 46 | ``` 47 | 48 | > The '.js' is optional here and you will often see it omitted. 49 | 50 | > You now have the `module.exports` object in your module assigned to the `mymodule` variable. Since you are exporting a single function, `mymodule` is a function you can call! 51 | 52 | > Also keep in mind that it is idiomatic to check for errors and do early-returns within callback functions: 53 | 54 | ```js 55 | function bar (callback) { 56 | foo(function (err, data) { 57 | if (err) 58 | return callback(err) // early return 59 | 60 | // ... no error, continue doing cool things with `data` 61 | 62 | // all went well, call callback with `null` for the error argument 63 | 64 | callback(null, data) 65 | }) 66 | } 67 | ``` 68 | 69 | The best way to aproach this would be to write you program assuming your module has already been written to work. Then create your module. For those who are interested in testing (and you all should be!) this method makes it easy to write a test from the start. Then you just have to get the test to pass. So starting off our program: 70 | 71 | ```js 72 | var filterFn = require('./program_filter.js') 73 | ``` 74 | 75 | For the purpose of clarification, filterFn is shorthand for filter Function. So first thing we have done is require our own module that we still haven't written yet. As per the hints, I have prefixed the filename with './'. 76 | 77 | We now need to use our module on our command line arguments as per the instructions. Taking the directory name, the extension and the callback function as our 3 arguments. 78 | 79 | ```js 80 | filterFn(process.argv[2], process.argv[3], function (err, list) { 81 | doSomething; 82 | }) 83 | ``` 84 | 85 | If we are writing a test, our test will be checking if this works. Of course it doesn't, the module doesn't exist. Next we need to use our error handler properly. So, if statement: 86 | 87 | ```js 88 | filterFn(process.argv[2], process.argv[3], function (err, list) { 89 | if (err) 90 | return console.error('There was an error:', err) 91 | }) 92 | ``` 93 | 94 | So, if we have an error we should get back an error log. Now we need to `console.log` the filenames that have the correct extension. By refering to the previous exercise we want a `forEach` to `console.log` all the filenames. But we aren't allowed to put any other manipulation here. So just log our variable `file`: 95 | 96 | ```js 97 | filterFn(process.argv[2], process.argv[3], function (err, list) { 98 | if (err) 99 | return console.error('There was an error:', err) 100 | 101 | list.forEach(function (file) { 102 | console.log(file) 103 | }) 104 | }) 105 | ``` 106 | 107 | This is still pretty useless without our module, but it does provide some clarity in terms of what is supposed to be happening. Comparing this to the official solution, `process.argv[2]` and `process.argv[3]` have been assigned to the variables `dir` and `filterStr` respectively. This serves the purpose of providing our arguments for the `module.export` function in our module. 108 | 109 | We now need to write our module. Straight away, for our module to work we know we need the filesystem module and path module so: 110 | 111 | ```js 112 | var fs = require('fs') 113 | var path = require('path') 114 | ``` 115 | 116 | Yes, our module requires modules. I can see an inception reference on the horizon. Easy bit over. Following the rules of our export we can write `module.export` with 3 arguments that exports our function for use in our program. 117 | 118 | ```js 119 | module.exports = function (dir, filterStr, callback) { 120 | doSomething; 121 | } 122 | ``` 123 | 124 | Assigning the command line arguments to variables made this step relatively straight forward. We have our 3 arguments that we had to have, the directory, the extension and the callback function. Next up we want to do something... and we have already done it before, in the previous exercise. 125 | 126 | ```js 127 | fs.readdir(dir, function (err, list) { 128 | list = list.filter(function (file) { 129 | return path.extname(file) === '.' + filterStr 130 | }) 131 | }) 132 | ``` 133 | 134 | So we can `readdir` our directory stored in the variable `dir`. We have our function with the `err` argument and data argument `list`. We've now introduced a new function `.filter()`. The `.filter()` method creates a new array with all elements that pass the test implemented by the provided function. So our test is the same as our previous if condition `path.extname(file) === '.' + filterStr`. 135 | 136 | Full documentation on `.filter()` can be found at [mozilla.org](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/filter). The reason for using the filter method instead of our previous if statement is because under the conditions of the contract we mustn't log the file names from the module. This must be done in our program. So we store the information in an array so that we can call upon it later in our program. Remember the lines: 137 | 138 | ```js 139 | list.forEach(function (file) { 140 | console.log(file) 141 | }) 142 | ``` 143 | 144 | Effectively, logging each element in our `list` variable. We still need to put in our error callback as specified in the contract. 145 | 146 | ```js 147 | module.exports = function (dir, filterStr, callback) { 148 | 149 | fs.readdir(dir, function (err, list) { 150 | if (err) 151 | return callback(err) 152 | 153 | list = list.filter(function (file) { 154 | return path.extname(file) === '.' + filterStr 155 | }) 156 | 157 | callback(null, list) 158 | }) 159 | } 160 | ``` 161 | 162 | So if there is an error, return the callback `err`, which is our early callback as specified in the hints. If there is no error, later `callback` the value `null` for our `err` argument as per the hints. 163 | 164 | We should now have 2 files. One is our module and the other is our program. Our program can call upon our module and use it as a function to perform the task as per the last exercise. We have also written a response to errors. You can run your program using: 165 | 166 | ```cs 167 | $ node program.js directory ext 168 | ``` 169 | 170 | Or verify with: 171 | 172 | ```cs 173 | $ learnyounode verify program.js 174 | ``` 175 | 176 | # Recap 177 | 178 | This has been quite a long and difficult exercise, and grasping some of the concepts can be quit difficult. I suggest further reading of modules in node documentation and further practice can be carried out by writing modulated versions of the previous exercises. 179 | 180 | 1. Using contracts for modules is beneficial for collaborative development. 181 | 2. When writing your own modules you'll need to prefix the name with './'. 182 | 3. Your `require` your modules the same way as nodes own modules. 183 | 4. `module.export`: allows your to export a function from your module. 184 | 5. `.filter()`: creates an array of all the elements that pass a test provided by your function 185 | 6. use `if (err)` early on in your script for early error callback for efficiency 186 | 7. make sure to provide a `null` value when there is no error. 187 | 188 | That's all for this walthrough. 189 | -------------------------------------------------------------------------------- /7. HTTP Client/solution/solution.js: -------------------------------------------------------------------------------- 1 | var http = require('http') 2 | 3 | http.get(process.argv[2], function (response) { 4 | response.setEncoding('utf8') 5 | response.on('data', console.log) 6 | response.on('error', console.error) 7 | }) -------------------------------------------------------------------------------- /7. HTTP Client/walkthrough.md: -------------------------------------------------------------------------------- 1 | # Exercise 7 - HTTP Client 2 | 3 | We are now moving into the area of HTTP which stands for HyperText Transfer Protocol. The is the scheme that most websites use for deliver of data between a server and a client. Other schemes include https which is the secure version and mailto which is for sending emails. This exercise will be looking at the client. 4 | 5 | # Problem 6 | 7 | > Write a program that performs an HTTP GET request to a URL provided to you as the first command-line argument. Write the String contents of **each** "data" event from the response to a new line on the console (stdout). 8 | 9 | So, some new material here. We need to be able to send requests using the http scheme. Lets look at the hints. 10 | 11 | # Hints 12 | 13 | > For this exercise you will need to use the `http` core module. 14 | 15 | > Documentation on the `http` module can be found by pointing your browser here: 16 | {rootdir:/node_apidoc/http.html} 17 | 18 | > The `http.get()` method is a shortcut for simple GET requests, use it to simplify your solution. The first argument to `http.get()` can be the URL you want to GET, provide a callback as the second argument. 19 | 20 | > Unlike other callback functions, this one has the signature: 21 | 22 | ```js 23 | function callback (response) { /* ... */ } 24 | ``` 25 | 26 | So, we require a different module. Nothing new there. We also can use a new method `get()` which takes 2 arguments, our url and a callback. And our callback function is slightly different. So we can hit the ground running with: 27 | 28 | ```js 29 | var http = require('http') 30 | 31 | http.get(process.argv[2], function (response) { 32 | doSomething; 33 | }) 34 | ``` 35 | 36 | Now we need to assign our event handlers to the response argument. 37 | 38 | ```js 39 | var http = require('http') 40 | 41 | http.get(process.argv[2], function (response) { 42 | response.on('data', console.log) 43 | response.on('error', console.error) 44 | }) 45 | ``` 46 | 47 | So you can see the `.on()` method in this case takes 2 arguments, the data/error and the function to perform. To make this a complete http get request we can add the correct encoding. 48 | 49 | ```js 50 | var http = require('http') 51 | 52 | http.get(process.argv[2], function (response) { 53 | response.setEncoding('utf8') 54 | response.on('data', console.log) 55 | response.on('error', console.error) 56 | }) 57 | ``` 58 | 59 | So by running your program using: 60 | 61 | ```cs 62 | $ node program.js url 63 | ``` 64 | 65 | You can see the response to your request printed out in the command line. If the url points to an html document, you will get each line of the document printed out in the console. 66 | 67 | # Recap 68 | 69 | 1. `http`: is the required module for http requests. 70 | 2. `.get`: is one http method, which is used for making requests to a server. 71 | 3. The `.get` callback function doesn't follow the same node format that we have previously seen. 72 | 4. `.on()`: assigns our event handlers. 73 | 5. Its good practice to include the used encoding. You can't always get away with ommitting it like many people do in html documents. 74 | 75 | That's all for this walkthrough. -------------------------------------------------------------------------------- /8. HTTP Collect/solution/solution.js: -------------------------------------------------------------------------------- 1 | var http = require('http') 2 | var bl = require('bl') 3 | 4 | http.get(process.argv[2], function (response) { 5 | response.pipe(bl(function (err, data) { 6 | if (err) 7 | return console.error(err) 8 | data = data.toString() 9 | console.log(data.length) 10 | console.log(data) 11 | })) 12 | }) -------------------------------------------------------------------------------- /8. HTTP Collect/walkthrough.md: -------------------------------------------------------------------------------- 1 | # Exercise 8 - HTTP Collect 2 | 3 | More on HyperText Transfer Protocol GET requests. 4 | 5 | # Problem 6 | 7 | > Write a program that performs an HTTP GET request to a URL provided to you as the first command-line argument. Collect **all** data from the server (not just the first "data" event) and then write two lines to the console (stdout). 8 | 9 | > The first line you write should just be an integer representing the number of characters received from the server and the second line should contain the complete String of characters sent by the server. 10 | 11 | So for this problem we need to collect data from the server, manipulate what we recieve and print it out to the console. This shouldn't be too difficult. Lets look at the hints. 12 | 13 | # Hints 14 | 15 | > There are two approaches you can take to this problem: 16 | 17 | > **1)** Collect data across multiple "data" events and append the results together prior to printing the output. Use the "end" event to determine when the stream is finished and you can write the output. 18 | 19 | > **2)** Use a third-party package to abstract the difficulties involved in collecting an entire stream of data. Two different packages provide a useful API for solving this problem (there are likely more!): `bl` (Buffer List) and `concat-stream`; take your pick! 20 | 21 | 22 | 23 | 24 | > To install a Node package, use the Node Package Manager `npm`. Simply type: 25 | 26 | ```sh 27 | $ npm install bl 28 | ``` 29 | 30 | > And it will download and install the latest version of the package into a subdirectory named `node_modules`. Any package in this subdirectory under your main program file can be loaded with the `require` syntax without being prefixed by './': 31 | 32 | ```js 33 | var bl = require('bl') 34 | ``` 35 | 36 | > Node will first look in the core modules and then in the `node_modules` directory where the package is located. 37 | 38 | > If you don't have an Internet connection, simply make a `node_modules` directory and copy the entire directory for the package you want to use from inside the {appname} installation directory: 39 | 40 | {rootdir:/node_modules/bl} 41 | {rootdir:/node_modules/concat-stream} 42 | 43 | > Both `bl` and `concat-stream` can have a stream *piped* in to them and they will collect the data for you. Once the stream has ended, a callback will be fired with the data: 44 | 45 | ```js 46 | response.pipe(bl(function (err, data) { /* ... */ })) 47 | // or 48 | response.pipe(concatStream(function (data) { /* ... */ })) 49 | ``` 50 | 51 | > Note that you will probably need to `data.toString()` to convert from a Buffer. 52 | 53 | > Documentation for both of these modules has been installed along with {appname} on your system and you can read them by pointing your browser here: 54 | 55 | {rootdir:/docs/bl.html} 56 | {rootdir:/docs/concat-stream.html} 57 | 58 | For this walkthrough we will be using the 2nd method. So lets start with the `bl` module or *buffer list*. To explain this module I will quote the documentation at it's very best: 59 | 60 | > *"A Node.js Buffer list collector, reader and streamer thingy."* 61 | 62 | So requiring our thingy: 63 | 64 | ```js 65 | var http = require('http') 66 | var bl = require('bl') 67 | ``` 68 | 69 | Simple. Next we start our `get` request as we did previously: 70 | 71 | ```js 72 | http.get(process.argv[2], function (response) { 73 | doSomething; 74 | }) 75 | ``` 76 | 77 | We now need our stream to be piped to us, so following the notation in the hints: 78 | 79 | ```js 80 | http.get(process.argv[2], function (response) { 81 | response.pipe(bl(function (err, data) { 82 | doSomething; 83 | })) 84 | }) 85 | ``` 86 | 87 | We want an early error callback: 88 | 89 | ```js 90 | http.get(process.argv[2], function (response) { 91 | response.pipe(bl(function (err, data) { 92 | if (err) 93 | return console.error(err) 94 | doSomething 95 | })) 96 | }) 97 | ``` 98 | 99 | Now instead of just console logging the data, we want to manipulate it. The data is currently presented as a buffer/array. We want to string it, log its length and log it as a string: 100 | 101 | ```js 102 | http.get(process.argv[2], function (response) { 103 | response.pipe(bl(function (err, data) { 104 | if (err) 105 | return console.error(err) 106 | data = data.toString() 107 | console.log(data.length) 108 | console.log(data) 109 | })) 110 | }) 111 | ``` 112 | 113 | So we are now piping our stream into our `bl` module which is now retrieving data from a url and presenting it to us in an array. We then stringify this array, log its length and then log the data as a string. 114 | 115 | This walkthrough has skirted over a very important topic in your learning. Streaming. For true understanding of streaming in node I suggest further research. There is a very large section in ["Professional Node.js" by Pedro Teixeira](http://www.amazon.co.uk/dp/1118185463). But for this tutorial I will simply quote the node documentation: 116 | 117 | ## Stream 118 | 119 | > `Stability: 2 - Unstable` 120 | 121 | A stream is an abstract interface implemented by various objects in Node. For example a request to an HTTP server is a stream, as is stdout. Streams are readable, writable, or both. All streams are instances of EventEmitter 122 | 123 | You can load the Stream base classes by doing require('stream'). There are base classes provided for Readable streams, Writable streams, Duplex streams, and Transform streams. 124 | 125 | # Recap 126 | 127 | 1. You can use both the `bl` and `concat-stream` modules for streaming in node. 128 | 2. You pipe your stream using the `.pipe()` method on your module. When the stream ends, a callback is fired. 129 | 3. Data collected by the module will be returned as an array. 130 | 131 | Whilst not much new stuff has been introduced it is important to reiterate just how large the topic of streaming is. I may, by popular demand only, provide a tutorial for streaming in the future. That's all for this tutorial. 132 | -------------------------------------------------------------------------------- /9. Juggling Async/solution/solution.js: -------------------------------------------------------------------------------- 1 | var http = require('http') 2 | var bl = require('bl') 3 | var results = [] 4 | var count = 0 5 | 6 | function printResults () { 7 | for (var i = 0; i < 3; i++) 8 | console.log(results[i]) 9 | } 10 | 11 | function httpGet (index) { 12 | http.get(process.argv[2 + index], function (response) { 13 | response.pipe(bl(function (err, data) { 14 | if (err) 15 | return console.error(err) 16 | 17 | results[index] = data.toString() 18 | count++ 19 | 20 | if (count == 3) // yay! we are the last one! 21 | printResults() 22 | })) 23 | }) 24 | } 25 | 26 | for (var i = 0; i < 3; i++) 27 | httpGet(i) 28 | -------------------------------------------------------------------------------- /9. Juggling Async/walkthrough.md: -------------------------------------------------------------------------------- 1 | # Exercise 9 - Juggling Async 2 | 3 | This exercise continues using http, but also brings in some of the previous knowledge you should have gained from the first few exercises. If you have understood everything so far this task should be relatively straightforward. 4 | 5 | # Problem 6 | 7 | > This problem is the same as the previous problem (HTTP COLLECT) in that you need to use `http.get()`. However, this time you will be provided with **three** URLs as the first three command-line arguments. 8 | 9 | > You must collect the complete content provided to you by each of the URLs and print it to the console (stdout). You don't need to print out the length, just the data as a String; one line per URL. The catch is that you **must** print them out in the same order as the URLs are provided to you as command-line arguments. 10 | 11 | So for this exercise we need to collect data from several urls, and as we recieve the data we need to store it without logging it. Then we need to be able to log the data only after everything has been collected. Lets look at the hints. 12 | 13 | # Hints 14 | 15 | > Don't expect these three servers to play nicely! They are not going to give you complete responses in the order you hope, so you can't naively just print the output as you get it because they will be out of order. 16 | 17 | > You will need to queue the results and keep track of how many of the URLs have returned their entire contents. Only once you have them all, you can print the data to the console. 18 | 19 | > Counting callbacks is one of the fundamental ways of managing async in Node. Rather than doing it yourself, you may find it more convenient to rely on a third-party library such as [async](http://npm.im/async) or [after](http://npm.im/after). But for this exercise, try and do it without any external helper library. 20 | 21 | So they aren't really giving us anything new. Just some friendly advise is all. Just as in the previous task we need our `bl` and `http` modules: 22 | 23 | ```js 24 | var bl = require('bl') 25 | var http = require('http') 26 | ``` 27 | 28 | We are going to need 2 main functions. One is to stream the data from the 3 urls and the other is to print out the data once it has been recieved. Lets call the stream function "httpGet" and the print function "printResults". Seeing as the `httpGet` will be calling on `printResults` we should write `printResults` out first. 29 | 30 | We can write our function to print out the elements of an array one line at a time. So our array can be a variable, lets call it `results`. The results variable will contain the data from each url. Then we can use a `for` loop to print out the results. So first create a variable and assign it an empty array: 31 | 32 | ```js 33 | var results = [] 34 | ``` 35 | Then our function containing a `for` loop: 36 | 37 | ```js 38 | function printResults () { 39 | for (var i = 0; i < 3; i++) 40 | console.log(results[i]) 41 | } 42 | ``` 43 | 44 | We now need to write out `httpGet` function. We need to open a stream for each url, collect the data and then store it in the results array. How can we do this to all 3? We can create a for loop to go through each url, collect the data, and then add it to the corresponding position in the array. So the for loop can be simple, like this: 45 | 46 | ```js 47 | for (var i = 0; i < 3; i++) 48 | httpGet(i) 49 | ``` 50 | So, if `i` is less that 3, console log the element in position `i`. Simple. No we can start writing our `httpGet` function around the `i` or `index` parameter. 51 | 52 | ```js 53 | function httpGet (index) { 54 | doSomething 55 | } 56 | ``` 57 | 58 | Our function, as part of the for loop will scroll through index 0, 1 and 2. We can use this as we have done before to access each part of a command line argument that we want. Skipping out the first 2 arguments: 59 | 60 | ```js 61 | function httpGet (index) { 62 | http.get(process.argv[2 + index], function (response) { 63 | doSomething 64 | }) 65 | } 66 | ``` 67 | 68 | We now perform a `http.get` on each url in the command line. Now pipe the stream to collect the data, incuding the early error callback: 69 | 70 | ```js 71 | function httpGet (index) { 72 | http.get(process.argv[2 + index], function (response) { 73 | response.pipe(bl(function (err, data) { 74 | if (err) 75 | return console.error(err) 76 | })) 77 | }) 78 | } 79 | ``` 80 | 81 | Now we need to string the data, and add it to the results array in the corresponding position. We can do this using our index: 82 | 83 | ```js 84 | function httpGet (index) { 85 | http.get(process.argv[2 + index], function (response) { 86 | response.pipe(bl(function (err, data) { 87 | if (err) 88 | return console.error(err) 89 | 90 | results[index] = data.toString() 91 | })) 92 | }) 93 | } 94 | ``` 95 | 96 | So far our program is storing the data from the 3 urls in our command line. We need to now somehow use our `printResults` function to print out the data. But it has to all be done in the correct order, all at once. So we need to wait for all the data to be collected and stored. We can use a count mechanism to do this. 97 | 98 | First declare the count variable: 99 | 100 | ```js 101 | var count = 0 102 | ``` 103 | 104 | Then we add instructions to increase the count by 1 when the data has been stringed and added to the results array: 105 | 106 | ```js 107 | function httpGet (index) { 108 | http.get(process.argv[2 + index], function (response) { 109 | response.pipe(bl(function (err, data) { 110 | if (err) 111 | return console.error(err) 112 | 113 | results[index] = data.toString() 114 | count++ 115 | })) 116 | }) 117 | } 118 | ``` 119 | 120 | So after the data is stored we increase the count variable by 1. 121 | 122 | Now we need to carryout our `printResuts` function when all the data is stored. So at 3 count: 123 | 124 | ```js 125 | function httpGet (index) { 126 | http.get(process.argv[2 + index], function (response) { 127 | response.pipe(bl(function (err, data) { 128 | if (err) 129 | return console.error(err) 130 | 131 | results[index] = data.toString() 132 | count++ 133 | 134 | if (count == 3) 135 | printResults() 136 | })) 137 | }) 138 | } 139 | ``` 140 | 141 | And you've done it. Our `printResults` function now loops through our results array, printing off each element which is a string of data from the corresponding urls in the same order they were passed to the console. 142 | 143 | # Recap 144 | 145 | 1. Embed functions for easy reuse. 146 | 2. Asynchronous processes happen at the same time, so faster processes finish sooner. 147 | 3. You need to manage your asynchronous processes, so that your functions act in the correct order. 148 | 149 | That's all for this walkthrough. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Learnyounode Walkthrough 2 | 3 | This repository contains a written walkthrough for each of the learnyounode exercises. Whilst learnyounode is a learning tool in itself, the exercises can be quite challenging for those who have only recently started coding for the web. These walkthroughs will aim to explain each terms functionallity and help guide the thought process through each exercise. 4 | 5 | For effective learning I suggest that you attempt to tackle all the learnyounode exercises by yourself at first. Then when stuck you can use these walkthroughs, in whole or in part to help get your through and better your understanding of the code. 6 | 7 | # Directory structure 8 | 9 | For each exercise there will be a sub-folder with the name of the exercise. Within the folder will be a walthrough and the official solution. Don't bother going straight to the official solution, you won't learn anything that way. 10 | 11 | # Videos 12 | 13 | Some time ago I started making videos going through these exercises however I only made it as far as exercise 2. These videos take time to put together and the written walkthroughs are far more comprehensive. I do intend to pick-up on the videos again in the future, but these will still be best paired with the written guides. --------------------------------------------------------------------------------