Skip to content

Commit 3c21ed1

Browse files
author
JoelCodes
committed
Moved questions to README.md
1 parent 22e28e1 commit 3c21ed1

File tree

21 files changed

+510
-438
lines changed

21 files changed

+510
-438
lines changed

01-making-promises/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Making Promises
2+
3+
Soooooo.....
4+
5+
This is the first set of exercises on promises. A Promise is a mechanism frequently used in JavaScript to handle asynchronous processes; that is, promises are how we reason about the business of waiting.
6+
7+
From a somewhat mechanical perspective, promises are simple state machines with three possible states:
8+
9+
* Pending
10+
* Resolved
11+
* Rejected
12+
13+
Resolved and rejected are *final* states, which means that once a promise gets to one of these states, it absolutely cannot go to any other state. Pending can go either way. A promise that is either resolved or rejected may also have a *payload*, a value that it carries. It's a little like an array that can only contain one value. It's entirely possible that that value will be `undefined`, but that's still a value.
14+
15+
How we access these values is the subject of a future chapter of exercises. For now, we're going to focus on the mechanisms for making promises.
16+
17+
## Exercise 1
18+
19+
Use [Promise.resolve(value)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve) to create a promise that will resolve with the value 3.
20+
21+
## Exercise 2
22+
23+
Use [Promise.reject(error)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/reject) to create a promise that will reject with the string "Boo!"
24+
25+
26+
## Exercise 3
27+
28+
You have the outline of a function `makePromiseWithConstructor(itShouldResolve)`
29+
30+
Use the [Promise constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) to create a promise that will:
31+
32+
* resolve if itShouldResolve is truthy
33+
* reject if itShouldResolve is falsy
34+
35+
## Exercise 4
36+
37+
This is a common use of the Promise constructor. If you want to simulate waiting for a value, a common technique is to create a function like the following. It simply accepts a value, and a delayInMs, then returns a promise that will resolve with that value after that delay.

answers/making-promises.js renamed to 01-making-promises/answers.js

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@
33
*
44
* EXERCISE 1
55
*
6-
* Use Promise.resolve(val) to create a promise that will resolve with the value 3
7-
*
8-
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve
9-
*
106
* @returns {Promise<3>}
117
*/
128
function makePromiseResolveWith3(){
@@ -17,9 +13,6 @@ function makePromiseResolveWith3(){
1713
*
1814
* EXERCISE 2
1915
*
20-
* Use Promise.reject to create a promise that will reject with the string "Boo!"
21-
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/reject
22-
*
2316
* @returns {Promise<,"Boo!">}
2417
*/
2518
function makePromiseRejectWithBoo(){
@@ -30,11 +23,6 @@ function makePromiseRejectWithBoo(){
3023
*
3124
* EXERCISE 3
3225
*
33-
* Use a Promise constructor to create a promise that will:
34-
* - resolve if itShouldResolve is truthy
35-
* - reject if itShouldResolve is falsy
36-
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
37-
*
3826
* @param {boolean} itShouldResolve - Whether the promise should resolve or reject
3927
* @returns {Promise<undefined, undefined>}
4028
*/
@@ -49,9 +37,7 @@ function makePromiseWithConstructor(itShouldResolve){
4937
/**
5038
*
5139
* EXERCISE 4
52-
*
53-
* This is a common use of the Promise constructor. If you want to simulate waiting for a value, a common technique is to create a function like the following. It simply accepts a value, and a delayInMs, then returns a promise that will resolve with that value after that delay.
54-
*
40+
*
5541
* @param {any} value
5642
* @param {number} delayInMs
5743
* @return {Promise<any>} - A promise that will resolve with the value after delayInMs milliseconds

test/making-promises.test.js renamed to 01-making-promises/test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const {
55
makePromiseRejectWithBoo,
66
makePromiseWithConstructor,
77
makeDelayPromise,
8-
} = require('../answers/making-promises');
8+
} = require('./answers');
99
describe('Promise.resolve: (val:T) => Promise<T>', () => {
1010
describe('#makePromiseResolveWith3:() => Promise<number>', () => {
1111
it('creates a resolving promise', () => {

02-consuming-promises/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Consuming Promises
2+
3+
The following exercises will focus on using the .then and .catch methods of promises. These are very important methods, not only for consuming values or handling errors, but also for transforming and combining promises.
4+
5+
## Exercise 1
6+
7+
This is a simple exercise in just waiting for a promise to be done before performing an action. The promise will resolve after a certain number of milliseconds.
8+
9+
## Exercise 2
10+
11+
In this exercise, we will use then and catch for their most basic use: to consume the result of a successful async process, or to handle the error of a failed async process. We will be given a promise, a consumer, and a handler. Set it up so that, if the promise resolves, the consumer will be called with the result, and if the promise rejects, the handler will be called with the error.

02-consuming-promises/answers.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
*
3+
* EXERCISE 1
4+
*
5+
* @param {Promise} promise
6+
* @param {thunk} action
7+
*
8+
*/
9+
function waitForPromise(promise, action){
10+
/* IMPLEMENT ME */
11+
}
12+
/**
13+
*
14+
* EXERCISE 2
15+
*
16+
* @param {Promise} promise
17+
* @param {consumer} consumer
18+
* @param {handler} handler
19+
*/
20+
function consumePromise(promise, consumer, handler){
21+
/* IMPLEMENT ME! */
22+
}
23+
24+
/**
25+
* @callback thunk
26+
* @returns {void}
27+
*/
28+
module.exports = {
29+
waitForPromise,
30+
consumePromise,
31+
};

test/consuming-promises.test.js renamed to 02-consuming-promises/test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const assert = require('assert');
33
const {
44
waitForPromise,
55
consumePromise,
6-
} = require('../answers/consuming-promises');
6+
} = require('./answers');
77

88
describe('Consuming Promises with .then(cb) and .catch(cb)', () => {
99
describe('#waitForPromise(promise, action) => void', () => {

03-transforming-promises/README.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Transforming Promises
2+
3+
## Exercise 1
4+
5+
In this exercise, we'll create a function that can take in a promise and a transformer function and return a new transformed promise.
6+
7+
In the previous exercise, we used `.then(cb)` in an imperative way, responding to its resolution by triggering a side effect. But what if we want to create a new promise based on that resolution, but one that changed the value somehow?
8+
9+
It's similar to arrays in this way: sometimes we use `.then(cb)` the way we would use `.forEach(cb)`, which helps us perform some action, some side effect based on the value. For instance:
10+
11+
```js
12+
const result = [2, 5, 3].forEach((num) => {
13+
console.log(num);
14+
});
15+
```
16+
17+
will print "2", "5", and "3" to the terminal, but result will be undefined. We did some stuff, and nothing was returned. Classic imperative code.
18+
19+
But what if we wanted a new array with all these number squared? We could write the following, of course:
20+
21+
```js
22+
const squares = [];
23+
[2, 5, 3].forEach((num) => {
24+
squares.push(num * num);
25+
});
26+
```
27+
28+
But there's already a common array function that does just this: `.map(cb)`, which lets us write that as a one-liner:
29+
30+
```js
31+
const squares = [2, 5, 3].map(num => num * num)
32+
```
33+
34+
The squares array now contains `[4, 25, 9]`. But why discuss arrays? Because promises are similar. Sometimes, you want to perform a side effect when that resolves, but sometimes, you'd like that result to be formatted in some different way.
35+
36+
So, we're going to create a function that does just that to promises. It's called `mapPromise`. it takes in a promise and a transformer callback, and returns a new promise that follows these rules:
37+
38+
* If the first promise rejects with an error, the new promise rejects with that error.
39+
* If the first promise resolves with a result, it calls the transformer with the value as an argument.
40+
* If the transformer returns with a value, the new promise resolves with that value.
41+
* If the transformer throws an error, the new promise rejects with that error.
42+
43+
## Exercise 2
44+
45+
I have good news about the function above: it already exists! In fact, you've been using it this whole time! It's `.then(cb)`!
46+
47+
In the previous exercise, we used `.then(cb)` in an *imperative* way, performing some action on the resolved value, but delayed. But we can also use `.then(cb)` *declaratively*, returning a new promise that will resolve with a transformed value.
48+
49+
In this exercise, we'll be passing a promise that resolves with a number or a string. We want to return with a promise such that:
50+
51+
* If the input promise resolves with a number, the output promise resolves with the square of that number.
52+
* If the input promise resolves with a string that we can turn into a number (like "1234"), the output promise resolves with the square of that number (1522756 in this example)
53+
* If the input promise resolves with a string that we cannot turn into a number (like "asdf"), then we reject with a message like "Cannot convert 'asdf' to a number!"
54+
* If the input promise rejects with an error, the output promise rejects with the same error
55+
56+
## Exercise 3
57+
58+
In the same way that we can use `.then(cb)` to transform a promise when it resolves, we can also use `.catch(cb)` to transform a promise when it rejects. If we return a value from a `.catch(cb)`, we create a new promise that will resolve. Likewise, if we throw an error in a `.catch`, the new promise will reject with that new error.
59+
60+
We're going to refactor the above promise so that it either resolves with the square of the number if it can, or it resolves with zero.
61+
62+
## Exercise 4
63+
64+
One interesting feature of the `.then(cb)` function is that it can actually take in two callbacks, one for success and one for failure. In most cases, running
65+
66+
```js
67+
somePromise
68+
.then(successCb)
69+
.catch(failureCb)
70+
```
71+
72+
is equivalent to
73+
74+
```js
75+
somePromise
76+
.then(successCb, failureCb)
77+
```
78+
79+
But there's one noteworthy exception. In the first code, the `.then(cb)` returns a new promise to which we attach the `.catch`. That means that it's possible for the first promise to resolve, and the second to reject, which means that the catch could be catching failures from the first promise OR the second promise. When we pass in the failureCb *with* the successCb, we guarantee that our failureCb is ONLY catching the rejection of the first promise.
80+
81+
We can examine this by making a function called "switcheroo". It'll take in a promise and return a promise such that:
82+
83+
* If the input promise rejects with an error, the output promise will resolve with that error.
84+
* If the input promise resolves with a value, the output promise will reject with that value.

03-transforming-promises/answers.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
*
3+
* EXERCISE 1
4+
*
5+
* @param {*} promise
6+
* @param {*} transformer
7+
* @returns {Promise}
8+
*/
9+
function mapPromise(promise, transformer){
10+
return new Promise((resolve, reject) => {
11+
/* IMPLEMENT ME!! */
12+
});
13+
}
14+
15+
/**
16+
*
17+
* EXERCISE 2
18+
*
19+
* @param {Promise<number | string>} numberPromise
20+
* @returns {Promise<number>}
21+
*/
22+
function squarePromise(numberPromise){
23+
return numberPromise
24+
.then(/* IMPLEMENT ME! */);
25+
}
26+
27+
/**
28+
* EXERCISE 3
29+
*
30+
* @param {Promise<number | string>} numberPromise
31+
* @returns {Promise<number>}
32+
*/
33+
function squarePromiseOrZero(promise){
34+
return squarePromise(promise)
35+
.catch(/* IMPLEMENT ME! */);
36+
}
37+
38+
/**
39+
* EXERCISE 4
40+
*
41+
* @param {Promise} promise
42+
* @returns {Promise}
43+
*/
44+
function switcheroo(promise){
45+
return promise.then(/* IMPLEMENT ME */);
46+
}
47+
48+
/**
49+
* @callback consumer
50+
* @param {*} value
51+
*/
52+
53+
/**
54+
* @callback handler
55+
* @param {*} error
56+
*/
57+
58+
module.exports = {
59+
mapPromise,
60+
squarePromise,
61+
squarePromiseOrZero,
62+
switcheroo,
63+
};

test/transforming-promises.test.js renamed to 03-transforming-promises/test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const {
55
squarePromise,
66
squarePromiseOrZero,
77
switcheroo,
8-
} = require('../answers/transforming-promises');
8+
} = require('./answers');
99

1010
describe('Transforming Promises with .then(cb) and .catch(cb)', () => {
1111
describe('#mapPromise(promise, transformer) => Promise', () => {

04-chaining-promises/README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Chaining Promises
2+
3+
## Exercise 1
4+
5+
Just as we did with mapPromise, we want to envision a new way of transforming promises: chaining them. We want to be able to have one promise resolve, then when it's done, use its value to start a second async process that returns a promise.
6+
7+
WE DON'T WANT TO START THAT SECOND PROCESS UNTIL AFTER THAT FIRST PROCESS RESOLVES! In this way, we can imagine that we may need some artifact from the first process before we can start the second. For instance, we may want to create a User object in a database, get an id back, and then use that UserId to create a series of, say, services that this user can access.
8+
9+
This chaining is a common problem in async, so let's create a function to do this.
10+
11+
## Exercise 2
12+
13+
I'm sure you're already a few steps ahead of me here, but just like with transforming promises with synchronous promises, we have a method to do this: `.then(cb)`!
14+
15+
If you return a promise from your callback to .then, you won't have a promise of a promise of some value. It'll just kinda smoosh them together, so a promise of a promise of a promise of an array just becomes a promise of an array.
16+
17+
Let's practice that! You're going to create an example like the one above, which will use `.then(cb)` to transform the value of a promise using another async process. It's a little hairy.
18+
19+
## Exercise 3
20+
21+
The most common point of this scenario is to chain two async processes, especially when the some result of the first one is needed before we can start the second. This is a very common technique, and we're going to use it to implement a somewhat more "real-world" scenario.
22+
23+
Our situation is that we have two api calls, one that gets a user object by id, and one that gets an organization object by id. The user object might look like this:
24+
25+
```js
26+
{
27+
id: 'u001',
28+
name: 'Mike',
29+
email: 'mike@mike.mike',
30+
organizationId: 'o001'
31+
}
32+
```
33+
34+
The organization object might look like this:
35+
36+
```js
37+
{
38+
id: 'o001',
39+
name: 'Research and Development'
40+
}
41+
```
42+
43+
We want to make a function that combines these two calls into one, making an object that looks like the following:
44+
45+
```js
46+
{
47+
id: 'u001',
48+
name: 'Mike',
49+
email: 'mike@mike.mike',
50+
organizationId: 'o001'
51+
organization: {
52+
id: 'o001',
53+
name: 'Research and Development'
54+
}
55+
}
56+
```
57+
58+
We can't get the organization object until we have the user object and the organization id. That means that we have to request the user, wait until we have it, then request the organization, wait for it to return, and return a combined object.
59+
60+
It's also worth noting that the getUserById function will resolve with undefined if no user is found with that id. Then we don't have to request the organization at all!
61+
62+
For this example, the test creates the getUserById and getOrganizationById functions, then passes them to a function that itself returns the function we want to build: one which takes in a userId and returns a combined object.

0 commit comments

Comments
 (0)