Asynchronous Code, Callbacks & Promises
Call Stack
The mechanism the JS interpreter uses to keep track of its place in a script that calls multiple functions.
How JS “knows” what function is currently being run and what functions are called from within that function etc.
Example: If you read a book the “Call Stack” represents your finger which is keeping track of the line you are currently reading.
How it works

- 
    When a script calls a function, the interpreter adds it to the call stack and then starts carrying out the function 
- 
    Any functions that are called by that function are added to the call stack further up, and run where their calls are reached. 
- 
    When the current function is finished, the interpreter takes it off the stack and resumes execution where it left off in the last code listing. 

JavaScript is a Single Threaded Language
At any given point in time, that single JavaScript thread is running at most one line of JavaScript code.
How Asynchronous Callbacks work
- 
    Browser come with Web APIs that are able to handle certain tasks in the background /like making requests or setTimeout) 
- 
    The Js call stack recognizes these Web API functions and passes them off to the browser to take care of 
- 
    Once the browser finishes those tasks, the yretunr and are pushed onto the stack as a callback. 

 
 
 

Example for Callbacks where we see that it can sometimes be a real pain:
A moving button whit a success and failure callback
button {
	font-size: 50px;
}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel="stylesheet" href="app.css">
  <title>Callback Hell</title>
</head>
<body>
  <h4>Try shrinking your window and see what happens!</h4>
  <button>Move Me!</button>
  <script src="app.js"></script>
</body>
</html>
const btn = document.querySelector('button');
// setTimeout(()=>{
//     btn.style.transform=`translateX(100px)`;
//     setTimeout(()=>{
//         btn.style.transform=`translateX(200px)`;
//         setTimeout(()=>{
//             btn.style.transform=`translateX(100px)`;
//         },1000);
//     },1000);
// },1000);
//-> in short:
const moveX=(element,amount,delay,onSuccess,onFailure)=>{
    setTimeout(()=>{
        const bodyBoundary= document.body.clientWidth;
        const elementRight=element.getBoundingClientRect().right;
     
     const currLeft=element.getBoundingClientRect().left;
     if(elementRight+amount>bodyBoundary){
         onFailure();
     }
     else{
        element.style.transform=`translateX(${currLeft+amount}px)`;
        onSuccess();
     }
    },delay)
};
// moveX(btn, 100, 2000, () => {
//     moveX(btn, 100, 1000, () => {
//         moveX(btn, 100, 1000, () => {
//             moveX(btn, 100, 1000, () => {
//                 moveX(btn, 100, 1000)
//             })
//         })
//     })
// })
moveX(btn,100,1000,()=>{
    //success
    moveX(btn,400,1000,()=>{
         //success
    moveX(btn,800,1000,()=>{
        
    },
    //fail
    ()=>{
    alert("CAnnot move further3")
    })
    },
    //fail
    ()=>{alert("CAnnot move further1")})
}, 
//fail
()=>{
    alert("CAnnot move further2")
})
Introducing to Promises
A Promise is an object representing the eventual completion or failure of an asynchronous operation.
When working with promises there are two things we need to understand:
- How we create promises or how we create a function that returns a promise.
- How to consume ore interact with promises.
A promise is a returned object to which you attach callbacks, instead of passing callbacks into a function.
const willGetYouADog = new Promise((resolve,reject)=>{
    //resolves the promise:
    //resolve()
    //rejects the promise:
    //reject()
    const rand = Math.random();
    if (rand<0.5) resolve()
    else reject()
})
//The `Then` paramater is used to define the action which should happen, when the promise is resolved!
willGetYouADog.then(()=>{
    console.log('We got a dog')
}).catch() //.catch runs when it gets rejected
//you could also write
willGetYouADog.catch(()=>console.log('No Dog'))
Returning Promises from Functions
const makeDogPromise = () => {
	return new Promise((resolve, reject) => {
		setTimeout(() => {
			const rand = Math.random();
			if (rand < 0.5) {
				resolve();
			}
			else {
				reject();
			}
		}, 5000);
	});
};
makeDogPromise()
	.then(() => {
		console.log('YAY WE GOT A DOG!!!!');
	})
	.catch(() => {
		console.log(':( NO DOG');
	});
Resolving/Rejecting with Values
//This is a FAKE Http Request Function
//It takes 1 second to resolve or reject the promise, depending on the url that is passed in
const fakeRequest = (url) => {
	return new Promise((resolve, reject) => {
		setTimeout(() => {
			const pages = {
				'/users' : [
					{ id: 1, username: 'Bilbo' },
					{ id: 5, username: 'Esmerelda' }
				],
				'/about' : 'This is the about page!'
			};
			const data = pages[url];
			if (data) {
				resolve({ status: 200, data }); //resolve with a value!
			}
			else {
				reject({ status: 404 }); //reject with a value!
			}
		}, 1000);
	});
};
//this should resolve
fakeRequest('/users')
	.then((res) => {
		console.log('Status Code', res.status);
		console.log('Data', res.data);
		console.log('REQUEST WORKED!');
	})
	.catch((res) => {
		console.log(res.status);
		console.log('REQUEST FAILED');
	});
//THIS should reject
fakeRequest('/dogs')
	.then((res) => {
		console.log('Status Code', res.status);
		console.log('Data', res.data);
		console.log('REQUEST WORKED!');
	})
	.catch((res) => {
		console.log(res.status);
		console.log('REQUEST FAILED');
	});
Promise Chaining
//This is a FAKE Http Request Function
//It takes 1 second to resolve or reject the promise, depending on the url that is passed in
const fakeRequest = (url) => {
	return new Promise((resolve, reject) => {
		setTimeout(() => {
			const pages = {
				'/users'        : [
					{ id: 1, username: 'Bilbo' },
					{ id: 5, username: 'Esmerelda' }
				],
				'/users/1'      : {
					id        : 1,
					username  : 'Bilbo',
					upvotes   : 360,
					city      : 'Lisbon',
					topPostId : 454321
				},
				'/users/5'      : {
					id       : 5,
					username : 'Esmerelda',
					upvotes  : 571,
					city     : 'Honolulu'
				},
				'/posts/454321' : {
					id    : 454321,
					title :
						'Ladies & Gentlemen, may I introduce my pet pig, Hamlet'
				},
				'/about'        : 'This is the about page!'
			};
			const data = pages[url];
			if (data) {
				resolve({ status: 200, data }); //resolve with a value!
			}
			else {
				reject({ status: 404 }); //reject with a value!
			}
		}, 1000);
	});
};
fakeRequest('/users')
	.then((res) => {
		console.log(res);
		const id = res.data[0].id;
        //take output from request and make new request
		return fakeRequest(`/users/${id}`);
	})
	.then((res) => {
		console.log(res);
		const postId = res.data.topPostId;
		return fakeRequest(`/posts/${postId}`);
	})
	.then((res) => {
		console.log(res);
	})
	.catch((err) => {
		console.log('OH NO!', err);
	});
// ************************************************
// ATTEMPT 2 (deliberate error to illustrate CATCH)
// ************************************************
// fakeRequest('/users')
// 	.then((res) => {
// 		console.log(res);
// 		const id = res.data[0].id;
// 		return fakeRequest(`/useALSKDJrs/${id}`); //INVALID URL, CATCH WILL RUN!
// 	})
// 	.then((res) => {
// 		console.log(res);
// 		const postId = res.data.topPostId;
// 		return fakeRequest(`/posts/${postId}`);
// 	})
// 	.then((res) => {
// 		console.log(res);
// 	})
// 	.catch((err) => {
// 		console.log('OH NO!', err);
// 	});
Refactoring Callback example with Promises
button {
	font-size: 50px;
}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel="stylesheet" href="app.css">
  <title>Refactoring With Promises</title>
</head>
<body>
  <h4>Try shrinking your window and see what happens!</h4>
  <button>Move Me!</button>
  <script src="app.js"></script>
</body>
</html>
const moveX = (element, amount, delay) => {
	return new Promise((resolve, reject) => {
		setTimeout(() => {
			const bodyBoundary = document.body.clientWidth;
			const elRight = element.getBoundingClientRect().right;
			const currLeft = element.getBoundingClientRect().left;
			if (elRight + amount > bodyBoundary) {
				reject({ bodyBoundary, elRight, amount });
			}
			else {
				element.style.transform = `translateX(${currLeft + amount}px)`;
				resolve();
			}
		}, delay);
	});
};
const btn = document.querySelector('button');
moveX(btn, 100, 1000)
	.then(() => moveX(btn, 100, 1000))
	.then(() => moveX(btn, 100, 1000))
	.then(() => moveX(btn, 100, 1000))
	.then(() => moveX(btn, 100, 1000))
	.then(() => moveX(btn, 100, 1000))
	.then(() => moveX(btn, 100, 1000))
	.then(() => moveX(btn, 100, 1000))
	.then(() => moveX(btn, 100, 1000))
	.then(() => moveX(btn, 100, 1000))
	.then(() => moveX(btn, 100, 1000))
	.then(() => moveX(btn, 100, 1000))
	.then(() => moveX(btn, 100, 1000))
	.then(() => moveX(btn, 100, 1000))
	.then(() => moveX(btn, 100, 1000))
	.then(() => moveX(btn, 100, 1000))
	.catch(({ bodyBoundary, amount, elRight }) => {
		console.log(`Cannot Move! Body is ${bodyBoundary}px wide`);
		console.log(`Element is at ${elRight}px, ${amount}px is too large!`);
	});
//This function moves an element "amount" number of pixels after a delay.
//If the element will stay on screen, we move the element and call the onSuccess callback function
//If the element will move off screen, we do not move the element and instead call the onFailure callback
// const moveX = (element, amount, delay, onSuccess, onFailure) => {
// 	setTimeout(() => {
// 		const bodyBoundary = document.body.clientWidth;
// 		const elRight = element.getBoundingClientRect().right;
// 		const currLeft = element.getBoundingClientRect().left;
// 		if (elRight + amount > bodyBoundary) {
// 			onFailure();
// 		}
// 		else {
// 			element.style.transform = `translateX(${currLeft + amount}px)`;
// 			onSuccess();
// 		}
// 	}, delay);
// };
// LOOK AT THIS UGLY MESS!
// moveX(
// 	btn,
// 	300,
// 	1000,
// 	() => {
// 		//success callback
// 		moveX(
// 			btn,
// 			300,
// 			1000,
// 			() => {
// 				//success callback
// 				moveX(
// 					btn,
// 					300,
// 					1000,
// 					() => {
// 						//success callback
// 						moveX(
// 							btn,
// 							300,
// 							1000,
// 							() => {
// 								//success callback
// 								moveX(
// 									btn,
// 									300,
// 									1000,
// 									() => {
// 										//success callback
// 										alert('YOU HAVE A WIDE SCREEN!');
// 									},
// 									() => {
// 										//failure callback
// 										alert('CANNOT MOVE FURTHER!');
// 									}
// 								);
// 							},
// 							() => {
// 								//failure callback
// 								alert('CANNOT MOVE FURTHER!');
// 							}
// 						);
// 					},
// 					() => {
// 						//failure callback
// 						alert('CANNOT MOVE FURTHER!');
// 					}
// 				);
// 			},
// 			() => {
// 				//failure callback
// 				alert('CANNOT MOVE FURTHER!');
// 			}
// 		);
// 	},
// 	() => {
// 		//failure callback
// 		alert('CANNOT MOVE FURTHER!');
// 	}
// );