From 407e32716414d5d16e6ec1be3fa2b3041b7f715f Mon Sep 17 00:00:00 2001 From: Mark Tyers Date: Wed, 21 Jun 2017 14:57:23 +0100 Subject: [PATCH] completed promises section --- .images/github_commits.png | Bin 0 -> 25300 bytes 02 JavaScript Objects.md | 2 +- 03 Asynchronous JavaScript.md | 85 +++++++++++- 05 Version Control.md | 235 ++++++++++++++++++++++++++++++--- exercises/03_async/promises.js | 27 ++-- 5 files changed, 314 insertions(+), 35 deletions(-) create mode 100644 .images/github_commits.png diff --git a/.images/github_commits.png b/.images/github_commits.png new file mode 100644 index 0000000000000000000000000000000000000000..76418f4d3f8988701126f08f3d97f0c381295ad9 GIT binary patch literal 25300 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 new Promise( (resolve, reject) => { + request(url, (err, res, body) => { + if (err) reject(new Error('invalid API call')) + resolve(body) + }) +}) +``` +This example creates a `Promise` that wraps a standard callback used to handle an API call. Notice that there are two possible cases handled here. + +1. If the API call throws an error we set the promise state to _rejected_. +2. If the API call succeeds we set the promise state to _fulfilled_. + +As you can see it it simple to wrap any async callbacks in promises but how are these called? + +### 6.2 Consuming a Promise + +To use promises we need a mechanism that gets triggered as soon as a promise changes state. A promise includes a `then()` method which gets called if the state changes to _fulfilled_ and a `catch()` method that gets called if the state changes to _rejected_. +```javascript +const aPromise = getData('http://api.fixer.io/latest?base=GBP') + +aPromise.then( data => console.log(data)) + +aPromise.catch( err => console.error(`error: ${err.message}`) ) +``` +In this example we create a _new Promise_ and store it in a variable. It get executed _immediately_. The second line calls its `then()` method which will get executed if the promise state becomes _fulfilled_ (the API call is successful). The parameter will be assigned the value passed when the `resolve()` function is called in the promise, in this case it will contain the JSON data returned by the API call. + +If the state of the promise changes to _rejected_, the `catch()` method is called. The parameter will be set to the value passed to the `reject()` function inside the promise. In this example it will contain an `Error` object. + +This code can be written in a more concise way by _chaining_ the promise methods. +```javascript +getData('http://api.fixer.io/latest?base=GBP') + .then( data => console.log(data)) + .catch( err => console.error(`error: ${err.message}`)) +``` +Because the Promise is executed immediately we don't need to store it in a variable. The `.then()` and `.catch()` methods are simply chained onto the promise. This form is much more compact and allows us to chain multiple promises together to solve more complex tasks. + +### 6.3 Chaining Promises + +The real power of promises comes from their ability to be _chained_. This allows the results from a promise to be passed to another promise. All you need to do is pass another promise to the `next()` method. +```javascript +const getData = url => new Promise( (resolve, reject) => { + request(url, (err, res, body) => { + if (err) reject(new Error('invalid API call')) + resolve(body) + }) +}) + +const printObject = data => new Promise( resolve => { + const indent = 2 + data = JSON.parse(data) + const str = JSON.stringify(data, null, indent) + console.log(str) + resolve() +}) + +getData('http://api.fixer.io/latest?base=GBP') + .then( data => printObject(data)) + .catch(err => console.error(`error: ${err.message}`)) +``` +Notice that we pass the `printObject` promise to the `then()` method. The data passed back from the `getData` promise is passed to the `printObject` promise. + +If a promise only takes a single parameter and this matches the data passed back when the previous promise _fulfills_ there is a more concise way to write this. + +```javascript +getData('http://api.fixer.io/latest?base=GBP') + .then(printObject) + .catch(err => console.error(`error: ${err.message}`)) +``` + +### 6.4 Test Your Knowledge + +Run the `promises.js` script, its functionality should be familiar to the `currency.js` script you worked with in chapter 3. + +Open the `promises.js` script and study the code carefully. Notice that it defines 5 promises and chains them together. You are going to extend the functionality by defining some additional promises and adding them to the promise chain. 1. modify the script to ask for the currency to convert to and display only the one conversion rate. 2. instead of printing the exchange rate, ask for the amount to be converted and them return the equivalent in the chosen currency diff --git a/05 Version Control.md b/05 Version Control.md index d4c967d..440ce81 100644 --- a/05 Version Control.md +++ b/05 Version Control.md @@ -1,19 +1,66 @@ # Version Control -1. Git Remotes * -2. Branching -3. GitFlow +- Where is my code? +- My files are corrupted +- What has changed and when? +- I’ve broken my program and can’t fix it +- I need to work on a different computer +- My team members are all working on the same file! +- Who has modified the files and when? +- Who has been doing the programming? -## 1 Git Remotes +Revision control +- The management of changes to documents +- Each changes are usually identified by a revision number +- Each revision has a timestamp and the person responsible +- Revisions can be compared, restored, and merged + +What is Git? + +- Git is a version control system (VCS) +- It can help you keep track of files that are frequently changed +- It supports distributed development +- Its Open Source (you can download and install it for free) +- Alternatives: Mercurial, subverson (SVN) + +In this chapter we will be covering the use of Git to version control our source code. + +1. Working with Git Locally * +2. Git Remotes * +3. Branching +4. GitFlow + +## 1 Working with Git Locally + +Even if you are not working as part of a team, Git can offer a number of advantages. In this section you will learn how to use Git to manage your code within your local development environment. + +### 1.1 Configuration + +Before carrying out any work, the repository needs to be configured. This involves TODO + +### 1.1 Test Your Knowledge + +Create a new directory on your computer and, after navigating into it initialise an empty repository. +```shell +$ mkdir local_git/ +$ cd local_git/ +$ ls -a + . .. + +$ git init + Initialised empty Git repository in /home/johndoe/Documents/local_git/.git/ + +$ ls -a + . .. .git ``` -git clone https://github.coventry.ac.uk/304CEM-1718SEPJAN/currency.git -``` +Running the `git init` command _initializes_ the repository and creates a new hidden directory called `.git/`. This contains all the information required to track your code changes. +next we need to add our user details and the preferred editor to the local configuration file which is kept in the `.git/` directory. Substitute your own name and email address. Since the **nano** text editor is far easier to use than the default **Vim** editor we will specify this as our default one. Finally we tell Git to cache our username and password for an hour (3600 seconds). ``` $ git config user.name "John Doe" -$ git config user.email johndoe@example.com +$ git config user.email 'johndoe@example.com' $ git config core.editor 'nano' $ git config credential.helper cache $ git config credential.helper 'cache --timeout=3600' @@ -24,25 +71,177 @@ $ git config --list user.email=johndoe@example.com ``` -### 1.1 Commits +Now we will create a new document in the `local_git/` directory. +``` +$ touch index.js +$ ls -a + . .. .git index.js +``` +Git should have tracked the changes in the project directory and noticed that there is an additional file. +``` +$ git status + On branch master + + Initial commit + Untracked files: + (use "git add ..." to include in what will be committed) + + index.js + + nothing added to commit but untracked files present (use "git add" to track) +``` +As you can see, the new `index.js` file is not currently being tracked. Let's enable tracking for this file. This is done in two steps, files are staged and then the staged files are committed to the repository with a commit message that explains the changes. +``` +$ git status + On branch master + + Initial commit + + Changes to be committed: + (use "git rm --cached ..." to unstage) + + new file: index.js + +$ git commit +``` +This will open the **Nano** text editor (the one we specified as our default) so we can add a commit message. +``` + +# Please enter the commit message for your changes. Lines starting +# with '#' will be ignored, and an empty message aborts the commit. +# On branch master +# +# Initial commit +# +# Changes to be committed: +# new file: index.js +# + + + [ Read 10 lines ] +^G Get Help ^O Write Out ^W Where Is ^K Cut Text ^J Justify ^C Cur Pos ^Y Prev Page +^X Exit ^R Read File ^\ Replace ^U Uncut Text ^T To Spell ^_ Go To Line ^V Next Page +``` +Enter the text `added index.js file` on the top line then save using _ctrl+o_ and finally quit using _ctrl+x_ (all available commands are listed along the bottom of the editor window). You will see the following message in the terminal window. +``` +[master (root-commit) d9036c2] added index.js file + 1 file changed, 0 insertions(+), 0 deletions(-) + create mode 100644 index.js +``` +To see the most recent commits you can use the `git log` command. ``` $ git log -commit 51f469eb41eb486c4316a97e7badc0ec9af36cb5 -Author: Mark Tyers -Date: Mon Jun 12 10:24:18 2017 +0100 +commit d9036c2bdf224ea72981eaa095ef76931c92e31d +Author: John Doe +Date: Fri Jun 16 09:04:00 2017 +0100 - renamed files + added index.js file -commit d3ef104463d3bb33b8b8862fe986e8b93038ade3 -Author: Mark Tyers -Date: Mon Jun 12 07:45:22 2017 +0100 +``` +If we check the status of the repository we should see that there are no further files to commit. +``` +$ git status +On branch master +nothing to commit, working tree clean +``` + +Next we will edit the index.js file. You can either open it in a visual code editor or use `nano index.js`. Enter the following then save your changes. +```javascript +// this is a simple script to show how git works +console.log('Hello Git!') +``` +If we check our repository status. +``` +$ git status +On branch master +Changes not staged for commit: + (use "git add ..." to update what will be committed) + (use "git checkout -- ..." to discard changes in working directory) + + modified: index.js + +no changes added to commit (use "git add" and/or "git commit -a") +``` +This time we will use a shortcut that stages the changes and makes a commit all from a single `git commit` command. +``` +$ git commit -am 'added message to index.js' + [master 721df6d] added message to index.js + 1 file changed, 2 insertions(+) +``` +The `-a` flag automatically stages all modified files and the `-m` flag allows us to add the commit message without opening our text editor. +``` +$ git log +commit 721df6dc9368bbe948be86fc19e696b7059dce5f +Author: Mark Tyers +Date: Fri Jun 16 09:17:29 2017 +0100 + + added message to index.js + +commit d9036c2bdf224ea72981eaa095ef76931c92e31d +Author: Mark Tyers +Date: Fri Jun 16 09:04:00 2017 +0100 + + added index.js file +``` +As you can see, there are now two commits in our repository. If there are too many commits to fit on the screen you can navigate using `space` for the next page, `w` for the previous page and `q` to quit. + +## 2 Git Remotes + +In the previous section you learned how to use Git to track local changes. In this section you will learn how to use remote git services. + +You will be shown two ways to configure this depending on whether you already have a local Git repository. The examples will be using [GitHub](www.github.com) but these will work equally with other services such as [GitLab](www.gitlab.com) or [BitBucket](www.bitbucket.com). + +TODO + +``` +git clone https://github.coventry.ac.uk/304CEM-1718SEPJAN/currency.git +``` + +### 2.1 Adding a Remotes + +If you already have a local Git repository (such as the one in your `local_repo/` directory), you can connect this to a remote. + +#### 2.1.1 Test Your Knowledge + +Create an account on GitHub.com and log in. Click on the green **New repository** button and in the _Repository name_ field enter `remote-repo`. Leave the description blank and click on the green **Create repository** button. - added start of gitflow +near the top of the screen you will see a section called _Quick setup_ which will be displaying a url. There are two URLs, one uses HTTP and the other uses SSH/Git. Make sure the **HTTPS** option is selected and copy the url to the clipboard, it should look something like this. ``` -You navigate using `space` for the next page, `w` for the previous page and `q` to quit. +https://github.com/johndoe/remote-repo.git +``` +In the terminal, make sure you are still in the `local_git/` directory and add the GitHub remote to your project. +``` +$ git remote +$ git remote add origin https://github.com/johndoe/remote-repo.git +$ git remote + origin +``` +The first time we view the remotes none are shown. We then add our GitHub remote and assign it the name `origin`, which is the standard name used by Git. If we now list the remotes we see that origin has now been added. + +Finally we push our commits to the remote repository on GitHub. +```$ git push origin --all + Username for 'https://github.com': johndoe + Password for 'https://johndoe@github.com': + Counting objects: 6, done. + Delta compression using up to 8 threads. + Compressing objects: 100% (3/3), done. + Writing objects: 100% (6/6), 506 bytes | 0 bytes/s, done. + Total 6 (delta 0), reused 0 (delta 0) + To https://github.com/marktyers/remote-repo.git + * [new branch] master -> master + ``` + If we refresh the repository web page on GitHub you should see the `index.js` file. The complete repository has been pushed including both our commits which can be seen under the _commits_ tab. -### 1.2 Remotes + If you select the _commits_ tab you should see your profile picture and name next to each of the two commits. + +![GitHub commits](.images/github_commits.png) + + If this has not happened it means that the name and email you have stored in the local git config settings _don't match the details you have stored on GitHub_. You can't retrospectively fix the current commits but, if you update the local settings, all future commits will show your details correctly. + +### 2.2 Cloning a Repository + +TODO ``` $ git remote show diff --git a/exercises/03_async/promises.js b/exercises/03_async/promises.js index 368fa40..39696d4 100644 --- a/exercises/03_async/promises.js +++ b/exercises/03_async/promises.js @@ -15,19 +15,19 @@ const checkValidCurrencyCode = code => new Promise( (resolve, reject) => { request('http://api.fixer.io/latest', (err, res, body) => { if (err) reject(new Error('invalid API call')) const rates = JSON.parse(body).rates - if (!rates.hasOwnProperty(code)) it.throw(new Error(`invalid currency code ${code}`)) - resolve() + if (!rates.hasOwnProperty(code)) reject(new Error(`invalid currency code ${code}`)) + resolve(code) }) }) -const getData = url => new Promise( (resolve, reject) => { - request(url, (err, res, body) => { +const getData = code => new Promise( (resolve, reject) => { + request(`http://api.fixer.io/latest?base=${code}`, (err, res, body) => { if (err) reject(new Error('invalid API call')) resolve(body) }) }) -const printObject = data => new Promise( (resolve) => { +const printObject = data => new Promise( resolve => { const indent = 2 data = JSON.parse(data) const str = JSON.stringify(data, null, indent) @@ -35,14 +35,13 @@ const printObject = data => new Promise( (resolve) => { resolve() }) +const exit = () => new Promise( () => { + process.exit() +}) + getInput('enter base currency') - .then( data => { - this.base = data - }) - .then( () => checkValidCurrencyCode(this.base)) - .then( () => getData(`http://api.fixer.io/latest?base=${this.base}`)) - .then( data => printObject(data)) - .then( () => { - process.exit() - }) + .then(checkValidCurrencyCode) + .then(getData) + .then(printObject) + .then(exit) .catch( err => console.error(`error: ${err.message}`))