1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
<html> <style> #container { width: 400px; height: 400px; position: relative; background: yellow; } #animate { width: 50px; height: 50px; position: absolute; background-color: red; } </style> <body> <p> <button onclick="myMove()">Click Me</button> </p> <div id ="container"> <div id ="animate"></div> </div> <script> function myMove() { var elem = document.getElementById("animate"); var pos = 0; var id = setInterval(frame, 5); function frame() { if (pos == 150) { clearInterval(id); } else { pos++; elem.style.top = pos + 'px'; elem.style.left = pos + 'px'; } } } </script> </body> </html> |
Category Archives: javascript
Js interview
https://career.guru99.com/top-85-javascript-interview-questions/
Problem
1 2 3 4 5 6 7 8 9 10 11 |
console.log('1') // 1) logs 1 // 2) run function afterTwoSeconds() after 2 seconds setTimeout(function afterTwoSeconds() { console.log('2') }, 2000) // 3) logs 3 console.log('3') // 2 seconds later, logs 2. |
Answer
This will actually log “1 3 2”, since the “2” is on a setTimeout which will only execute, by this example, after two seconds. Your application does not hang waiting for the two seconds to finish. Instead it keeps executing the rest of the code and when the timeout is finished it returns to afterTwoSeconds.
Problem
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
function foo1() { return { bar: "hello" }; } function foo2() { return // automatically inserts a semi-colon. Thus returning nothing { bar: "hello" }; } |
Answer
Not only is this surprising, but what makes this particularly gnarly is that foo2() returns undefined without any error being thrown.
The reason for this has to do with the fact that semicolons are technically optional in JavaScript (although omitting them is generally really bad form). As a result, when the line containing the return statement (with nothing else on the line) is encountered in foo2(), a semicolon is automatically inserted immediately after the return statement.
No error is thrown since the remainder of the code is perfectly valid, even though it doesn’t ever get invoked or do anything (it is simply an unused code block that defines a property bar which is equal to the string “hello”).
This behavior also argues for following the convention of placing an opening curly brace at the end of a line in JavaScript, rather than on the beginning of a new line. As shown here, this becomes more than just a stylistic preference in JavaScript.
Problem
1 2 3 4 5 6 |
(function(){ var a = b = 3; })(); console.log("a defined? " + (typeof a !== 'undefined')); console.log("b defined? " + (typeof b !== 'undefined')); |
Solution
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
(function(){ b = 3; // ends up being a global variable (since it is not preceded by the var keyword) console.log(b); var a = b; console.log(a); })(); console.log(b); // defined. b is global console.log(typeof b); // since b does not have var in front, it is designated as a global. //console.log(a); // a is NOT defined at this scope. It is only visible in the function. console.log("a defined? " + (typeof a !== 'undefined')); // a is undefined because it is local only to the function console.log("b defined? " + (typeof b !== 'undefined')); // b is defined because it is a global |
Problem
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var myObject = { foo: "bar", func: function() { var self = this; console.log("outer func: this.foo = " + this.foo); console.log("outer func: self.foo = " + self.foo); (function() { console.log("inner func: this.foo = " + this.foo); console.log("inner func: self.foo = " + self.foo); }()); } }; myObject.func(); |
Solution
In the outer function, both this and self refer to myObject and therefore both can properly reference and access foo.
In the inner function, though, this no longer refers to myObject. As a result, this.foo is undefined in the inner function, whereas the reference to the local variable self remains in scope and is accessible there.
1 2 3 4 |
outer func: this.foo = bar outer func: self.foo = bar inner func: this.foo = undefined inner func: self.foo = bar |
Problem
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
var myApp = {} myApp.id = 0; myApp.next = function() { return this.id++; } myApp.reset = function() { this.id = 0; } myApp.next(); //0 myApp.next(); //1 var getNextId = myApp.next; getNextId(); //NaN whoops! |
Solution
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
var myApp = {} // declare a literal object myApp.id = 0; // add an id property and initialize it to 0 // add a property 'next'. It references a function object myApp.next = function() { return this.id++; } // when next is called, the this references the literal object myApp. console.log(myApp.next()); // 0 console.log(myApp.next()); // 1 var myApp2 = {} // let's make another literal object. This time, no property id is added. // add property next2, and assign it to the next function //so the two literal objects share the same function. myApp2.next2 = myApp.next; // When myApp2 literal object executes the "next" function, we get a NaN because 'id' does not exist // for myApp 2. console.log(myApp2.next2()); // getNextId is simply a reference to the next function // its 'this' references global var getNextId = myApp.next; //console.log(getNextId()); //Nan // however, if we define id, then it will be ok id = 6680; console.log(getNextId()); // 6680 console.log(getNextId()); // 6681 console.log(getNextId()); // 6682 |
What is JavaScript?
JavaScript is a client-side as well as server side scripting language that can be inserted into HTML pages and is understood by web browsers. JavaScript is also an Object Oriented Programming language.
What are JavaScript Data Types?
Following are the JavaScript Data types:
Number
String
Boolean
Function
Object
Undefined
What is the use of isNaN function?
isNan function returns true if the argument is not a number otherwise it is false.
What is negative infinity?
Negative Infinity is a number in JavaScript which can be derived by dividing negative number by zero.
Is it possible to break JavaScript Code into several lines?
Breaking within a string statement can be done by the use of a backslash, ‘\’, at the end of the first line
Example:
1 |
document.write("This is \a program"); |
What are undeclared and undefined variables?
Undeclared variables are those that do not exist in a program and are not declared. If the program tries to read the value of an undeclared variable, then a runtime error is encountered.
1 |
console.log(a) // runtime error, a was never declared anywhere in scope. |
Undefined variables are those that are declared in the program but have not been given any value. If the program tries to read the value of an undefined variable, an undefined value is returned.
Uninitialized variables
1 2 |
let a; console.log(a) // undefined |
> var noValueYet;
> console.log(noValueYet);
undefined
missing parameters
1 2 3 4 5 6 |
function add(a, b) { console.log(a) // 2 console.log(b) // undefined } add(2) // missing 2nd parameter |
> function foo(x) { console.log(x) }
> foo()
undefined
unknown variables of objects
> var obj = {};
> console.log(obj.unknownProperty)
undefined
null: used by programmers to indicate “no value”, e.g. as a parameter to a function.
Examining a variable:
console.log(typeof unknownVariable === “undefined”); // true
var foo;
console.log(typeof foo === “undefined”); // true
console.log(foo === undefined); // true
var bar = null;
console.log(bar === null); // true
Write the code for adding new elements dynamically?
1 2 3 |
var h = document.createElement("H1") // Create a <h1> element var t = document.createTextNode("Hello World"); // Create a text node h.appendChild(t); // Append the text to <h1> |
shellsort (js)
Shellsort is the same as insertion sort. Except that it uses a pass array to decide how many number of steps to jump.
In Insertion Sort, we always use 1 step. When we backtrack, we go back 1 step. But in Shellsort, we have an array, where we decide how many jumps to backtrack. This is called a “gap”. Of course in the very end, we’ll definitely have 1 step at the end.
1 2 3 4 5 6 7 8 |
for (var gapIndex = 0; gapIndex < gaps.length; gapIndex++) { var gap = gaps[gapIndex]; for (var i = gap; i < dataStore.length; ++i) { // do Insertion sort using "gap" number of steps when going backwards } } |
In our example, we decide to use 5, 3, 1. What this means is that we will sort this via Insertion via passes where we jump 5 steps, then 3 steps, then 1 step. So for step 5, we start at index 5 of the array. Do the Insertion sort, but instead of using 1 step when going backwards, we use 5 steps.
first pass, 5 steps
We start off at index 5. Will process the insertion sort, and when we’re done, we go down the array 1 by 1.
The insertion sort itself will use 5 steps when sorting via
1 |
j -= gap |
Everything else is processed the same way as Insertion Sort. We get the current valueToInsert. It needs to satisfy a few points:
1) is the index larger or equal to the gap index?
2) is the previous gapped element larger than the valueToInsert?
If so, we assign the previous gapped element to the current one.
If not, we just put the valueToInsert back to the current element.
Given the Insertion Code with the gap element like so:
1 2 3 4 5 6 7 8 9 10 11 |
for (var i = gap; i < dataStore.length; ++i) { var valueToInsert = dataStore[i]; for (var j = i; j >= gap && (dataStore[j-gap] > valueToInsert) ; j -= gap) { dataStore[j] = dataStore[j - gap]; } dataStore[j] = valueToInsert; // insert valueToInsert at the appropriate place console.log("index j: "+j + ", dataStore[j] = valueToInsert"); } |
The gap we use is 5. So we need to do an Insertion pass where the gap is 5.
We start index 5. The value at that index is 11. 11 is the value we need to check and find a proper place to insert.
The Insertion sort happens when we have j point to i.
1) j>= gap, 5 >= 5 ? yes
2) previous gapped element > valueToInsert, 6 > 11 ? NO
dataStore[5] = 11, we move down the array.
The next index is 6. dataStore[6] is 8. The previous gapped element is dataStore[6-5] is 0
1) j >= gap, 6 >= 5? yes
2) previous gapped element > valueToInsert, 0 > 8, NO
dataStore[6] = 8, we move down the array
The next index is 7. dataStore[7] is 0. Its previous gapped element dataStore[7-5] is 2.
1) j >= gap, 7 >= 5? yes
2) previous gapped element > valueToInsert, 2 > 0 yes
When both requirements is true, we exchange the element values. Basically storing the previous value into the current value. This is the because the previous value is larger than the current value.
We continue on the insertion sort by looking back at -gap elements. Namely, j-= gap. Hence our j is now at index 2, dataStore[2] = 2.
We check the requirements again:
1) j >= gap, 2 >= 5 NO
So we jump out of the loop and assign the valueToInsert at dataStore[2]. This perfectly switches the numbers at dataStore[2], and dataStore[7].
We move onto the next index at 8. dataStore[8] is 5.
Previous gapped element is dataStore[8-5] = 9
1) j >= gap, 8 >= 5, yes
2) previous gapped element > valueToInsert, 9 > 5 yes
Hence we need to copy the previous element to the current one. We then backtrack gap steps into index 3
1) j >= gap, 3 >= 5, NO
therefore dataStore[3] = 5.
We keep going down the array.
We process the last element at index 9, dataStore[9] is 4.
dataStore[9] is 4
dataStore[9-5] is 3
1) j>= gap, 9 >= 5
2) previous gapped element > valueToInsert, 3 > 4, NO.
So we jump out of the loop
dataStore[9] = 4
next gap pass, 3 steps
We start from beginning to end, and at index gap. We do the insertion sort with gap steps, however, we process each element down the array one by one.
index 3
Hence, our next pass involves gap 3. We start off at index 3.
We see that dataStore[3] is 9. valueToInsert = 9.
Previous gap element is dataStore[0] is 6.
is index 3 >= 3 yes
6 > 9 (valeToinsert) NO, move on to next index
index 4
We now on index 4.
dataStore[4] = 3, valueToInsert = 3
dataStore[4-3] = 0
4 >= 3 yes
0 > 3 (valeToInsert) NO, move on to next index
index 5
We now on index 5.
dataStore[5] is 11, valueToInsert = 11
dataStore[5-3] is 2
5 >= 3 yes
2 > 11 (valeToInsert), NO, move on to next index
index 6
We now on index 6.
dataStore[6] is 8 valueToInsert = 8
dataStore[6-3] is 9.
6 >= 3 yes
9 > 8(valeToInsert), we then copy over the value from the previous element to the current
Thus, dataStore[6] = 9.
We go back 1 gap step so now j is at index 3.
dataStore[3] is 9
dataStore[3-3] is 6
3 >= 3 yes
6 > 8 (valeToInsert), NO, we skip out. have current index j, dataStore[3] = 8 (valueToInsert)
move forward down the array
index 7
We are now at index 7.
dataStore[7] is 0. valueToInsert is 0.
dataStore[7-3] is 3
7>=3 yes
3 > 0 yes
Let’s copy previous element to current so now
dataStore[7] is 3.
j moves a “gap” step back to index 4
dataStore[4] is 3
dataStore[1] is 0
4 >= 3, YES
0 > 0 NO
dataStore[4] is 0.
Move forward
index 8
dataStore[8] is 5 (valueToInsert)
dataStore[8-3] is 11
8 >= 3 yes
11 > 5 yes
So we need to copy the previous value over to current.
dataStore[8] = 11
Go back 1 “gap” step to index 5
dataStore[5] is 11
dataStore[5-3] is 2
5 >= 3 yes
2 >= 5 (valueToInsert) NO
dataStore[5] = 5
move on…
Index 9
Finally, we are index 9.
dataStore[9] is 4 (valueToInsert)
dataStore[9-3] is 8
9 >= 3 yes
8 > 4 yes
so we copy the previous gap element to current
dataStore[9] = 8
we go previous gap element to index 6
dataStore[6] is 8
dataStore[6-3] is 9
6 >= 3, YES
9 >= 4 (valueToInsert), YES
copy the previous gap element to current
dataStore[6] = 9
go back “gap” elements
dataStore[3] is 9
dataStore[0] is 6
3 >= 3, YES
6 > 4 (valueToInsert) YES
copy the previous gap element to current
dataStore[3] = 6
go back “gap” elements
index is now 0
0 >= 3 NO
so dataStore[0] = 4 (valueToInsert)
Done for gap 3 pass
Full Source
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
"use strict"; var dataStore = [6,0,2,9,3,11,8,0,5,4]; // gaps element is "starting index" var gaps = [5,3,1]; function shellsort() { for (var gapIndex = 0; gapIndex < gaps.length; gapIndex++) { var gap = gaps[gapIndex]; console.log("***** gap:" + gap + " *******"); for (var i = gap; i < dataStore.length; ++i) { var valueToInsert = dataStore[i]; console.log("i " + i + ", valueToInsert is: " + valueToInsert); console.log("------------ APPLY INSERTION SORT HERE ------------"); console.log("dataStore[i-gap]: "+dataStore[i-gap]); for (var j = i; j >= gap && dataStore[j-gap] > valueToInsert; j -= gap) { console.log("-- j is: " + j + "--"); console.log("dataStore is: "+dataStore[j-gap]); dataStore[j] = dataStore[j - gap]; } dataStore[j] = valueToInsert; console.log("index j: "+j + ", dataStore[j] = valueToInsert"); } } return dataStore; } var sorted = shellsort(); console.log(sorted); |
Circular List in JS (prototype pattern, constructor pattern)
Prototype Pattern
– Using prototypes to create classes, all instances of that class will the same functions.
– Can’t have private utility functions. All properties and functionalities can be seen by caller.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
"use strict"; var StepEnum = Object.freeze({NEXT: "step next", PREV: "step previous"}); function ListNode(newData, newPrev, newNext) { // this = {} if (new.target === undefined) { console.log('You didnt use new. Giving you a new Node Object'); return new Node(); } // public properties this.data = newData; this.next = newNext; this.prev = newPrev; // public functions this.clean = function() { this.data = null; this.next = null; this.prev = null; } this.display = function() { console.log("[" + this.data + "]") } // return this } // List Node function List(newName) { this.head = null; this.tail = null; this.now = null; this.listName = newName; // private // O(1) this.removeLastNode = function(data, ref) { if (this.dataMatches(data.toUpperCase(), ref.data.toUpperCase())) { if (this.head === this.tail) { this.head = this.head.next = this.head.prev = null; this.tail = this.tail.next = this.tail.prev = null; ref.clean(); return true; } } return false; } // private this.removeNodeAtHead = function(ref) { if (ref === this.head) { var temp = ref; ref.next.prev = this.tail; ref.prev.next = ref.next; this.head = ref.next; temp.clean(); return true; } return false; } // private // O(1) this.removeNodeAtTail = function(ref) { if (ref === this.tail) { var temp = ref; ref.next.prev = ref.prev; ref.prev.next = ref.next; this.tail = ref.prev; temp.clean(); return true; } return false; } // private // O(1) this.removeNodeInBetween = function(ref) { if ((ref !== this.tail) && (ref !== this.head)) { ref.next.prev = ref.prev; ref.prev.next = ref.next; ref.clean(); return true; } return false; } } List.prototype.about = function() { console.log(`Circular list (${this.listName}) with nodes that reference next and previous. Ricky Tsao 2018`); } List.prototype.current = function() { return this.now; } List.prototype.setCurrentToHead = function() { this.now = this.head; return this.now; } List.prototype.setCurrentToTail = function() { this.now = this.tail; return this.now }; List.prototype.step = function(steps, StepEnum) { if(!isNaN(steps) && steps > 0) { while (steps > 0) { this.now = (StepEnum.NEXT) ? this.now.next : this.now.prev; steps--; } } } List.prototype.isEmpty = function() { return (!this.head && !this.tail); } List.prototype.insert = function(data) { console.log("PROTOTYPE - Inserting data: " + data); try { if (!this.isString(data)) throw "Data Must be a String"; if (this.isEmpty()) { this.head = new ListNode(data, null, null); this.tail = this.head; } else { this.tail = new ListNode(data, this.tail, this.head); this.tail.prev.next = this.tail; this.head.prev = this.tail; } this.now = this.tail; } catch(err) { console.log("\n------ ERROR ------"); console.log("description: data not entered"); console.log("reason: " + err); console.log("===================\n\n") } } // Notice that in our prototype, we can use private functions from List // public // O(n) List.prototype.remove = function(data) { if (this.isEmpty()) { console.log("Nothing to remove because list is empty"); return; } try { var traversal = this.head; if (this.removeLastNode(data, traversal)) { return;} do { if (this.dataMatches(data.toUpperCase(), traversal.data.toUpperCase())) { if (this.removeNodeAtHead(traversal)) return; if (this.removeNodeAtTail(traversal)) return; if (this.removeNodeInBetween(traversal)) return; } traversal = traversal.next; } while (traversal != this.head); } catch(error) { console.log("Warning: "+ error); } }; List.prototype.dataMatches = function(data1, data2) { return (data1.toUpperCase() === data2.toUpperCase()); } List.prototype.isString = function(data) { return ((typeof data) === 'string'); } List.prototype.display = function() { console.log("PROTOTYPE - display CircularList: " + this.listName) if (this.isEmpty()) { console.log("-- LIST (" + this.listName + ") IS EMPTY --"); return; } var traversal = this.head; console.log("-----" + this.listName + "-----"); do { traversal.display(); traversal = traversal.next; } while (traversal !== this.head && traversal != null); console.log("===============") } var metro = new List("metro"); metro.about(); metro.insert("Hou Hai"); metro.insert("Keyuan"); metro.insert("Window of the World"); metro.display(); metro.remove("Window of the World"); metro.display(); metro.remove("Hou Hai"); metro.display(); metro.remove("Window of the World"); metro.display(); metro.remove("Shui wan"); metro.display(); metro.remove("Keyuan"); metro.display(); |
Constructor Pattern with scoped private variables
By using scoped variables, we can have privacy, and can create get/set functionalities for public access.
However, not very practical as I will need to create get/set functions for all the private variables.
Then, using them in my functions can be awkward as assigning references must be dealt with using set functions.
And being assigned, we’ll have to call the get functions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
class CircularList { constructor(listName) { console.log("Constructing CircularList " + listName); // https://stackoverflow.com/questions/22156326/private-properties-in-javascript-es6-classes // create private variable // assign it to incoming parameter var _name = listName; // setting new name this.setName = function(listName) { _name = listName;} // getting the new name this.getName = function() { return _name; } // private member variables var _head = null; this.setHead = function(newHead) {_head = newHead;} this.getHead = function() {return _head;} var _tail = null; this.setTail = function(newTail) {_tail = newTail;} this.getTail = function() {return _tail;} var _now = null; this.setNow = function(newNow) {_now=newNow;} this.getNow = function() {return _now;} console.log("Finished constructing CircularList") } // constructor isString(data) { return ((typeof data) === 'string'); } isEmpty() { return (this.getHead() == null && this.getTail() == null); } insert(data) { console.log("CircularList - Inserting data: " + data); try { if (!this.isString(data)) throw "Data Must be a String"; if (this.isEmpty()) { this.setHead(new ListNode(data, null, null)); this.setTail(this.getHead()); } else { this.setTail(new ListNode(data, this.getTail(), this.getHead())); this.getTail().prev.next = this.getTail(); this.getHead().prev = this.getTail(); } this.setNow(this.getTail()); } catch(err) { console.log("\n------ ERROR ------"); console.log("description: data not entered"); console.log("reason: " + err); console.log("===================\n\n") } } print() { if (this.getHead() === null) { console.log("-- LIST IS EMPTY --"); return; } var traversal = this.getHead(); do { traversal.display(); traversal = traversal.next; } while (traversal !== this.getHead() && traversal != null); } } let a = new CircularList("rickylist") a.insert("ricky"); a.insert("bob"); a.insert("claire"); a.print(); |
Immediately-Invoked Function Expression (iife)
https://www.kirupa.com/html5/immediately_invoked_function_expressions_iife.htm
function variation 1 – declare named function, then execute it
1 2 3 4 5 6 7 8 9 10 11 12 |
function areYouLucky() { var foo = Math.round(Math.random() * 100); if (foo > 50) { alert("You are lucky!"); } else { alert("You are not lucky!"); } } // calling this function! areYouLucky(); |
function variation 2 – anonymous function expression, execute it via reference
1 2 3 4 5 6 7 8 9 10 11 12 |
// anonymous function #1 var isLucky = function() { var foo = Math.round(Math.random() * 100); if (foo > 50) { return "You are lucky!"; } else { return "You are not lucky!"; } }; var me = isLucky(); |
function variation 3 – Immediately Invoked Function Expression
say you have a function you want to execute like so:
1 2 3 4 |
function() { var shout = "I AM ALIVE!!!"; alert(shout); } |
…usually, you’ll have a variable name reference the function, then execute it. Or you give the function a name, and use that name for the execution as shown above.
You can also invoke a function like so:
1 2 3 4 5 |
var greeting = function() { return "hello"; }(); console.log(greeting); // hello |
What we just did is create a reference and point it to the return value of a function expression. Remember, a function without a name is a function expression. A function with a name is a function statement.
If you want to write function() as an expression itself, without a variable reference pointing to it, the compiler will give you an error.
1 2 3 4 |
// syntax error function() { } |
It’s expecting it to be a function statement. It’s looking for a function name. In order to remedy this, you can use parentheses. Also, when you use parenthesis, JS assumes that the inside is an expression. We can create a function expression and immediately invoke it like so:
1 2 3 4 |
(function() { var shout = "I AM ALIVE!!!"; console.log(shout); })(); |
The general structure for creating an IIFE that takes arguments is as follows:
1 2 3 4 5 6 7 8 |
// arg1 and arg2 somewhere in outer scope (function(a, b) { // a = ... // b = ... })(arg1, arg2); |
You can invoke it outside or inside the parentheses, doesn’t matter.
example:
1 2 3 4 5 6 7 8 9 |
(function(first, last) { console.log("My name is " + last + ", " + first + " " + last + "."); }("James", "Bond")); // same (function(first, last) { console.log("My name is " + last + ", " + first + " " + last + "."); })("James", "Bond"); |
What happens in memory
When the IIFE is immediately invoked, we have an execution context pushed onto the execution stack.
In its Creation Phase, it will put variables into memory.
1 2 3 |
(function(name) { var greeting = 'Hello'; }('John')); |
In our example, greeting variable is put into memory and assigned undefined. By definition, any variable created within a certain execution context belongs there. In other words, the variable is local to the function which is was defined. Furthermore, a let variable is local to the block which it was defined.
Sa you want to manipulate the global variable. Simple, just pass it in.
1 2 3 |
(function(global, name) { global.greeting = 'Hello'; }(window, 'John')); |
This pattern makes it intentional for you to affect the global object. And not by accidental.
Review Scope real quick…
…you learned that JavaScript doesn’t have the concept of block scoping. It only has lexical scope.
This means that variables declared inside a block such as an if statement or loop will actually be accessible to the entire enclosing function:
1 2 3 4 5 6 7 8 |
function scopeDemo() { if (true) { var foo = "I know what you did last summer!"; } alert(foo); // totally exists! } scopeDemo(); |
As you can see in this example, the foo variable, despite being stuck inside the if statement, is accessible outside of it because your if is not a scopable block. This ability to have your “supposedly inner” variables promoted and accessible to the entire enclosing function is known as variable hoisting!
When do use an IIFE?
what is so different about an IIFE compared to a function you declare and call immediately like you have always done?
The main difference is that an IIFE leaves behind no evidence of its existence after it has run.
This is largely because IIFEs are anonymous functions that are nameless. This means you can’t track them by examining variables. Because the code you put inside an IIFE is actually inside a function, any variables you declare are local to that function.
IIFE provides you with a very simple way of running code fully inside its own bubble and then disappearing without a trace.
By taking advantage of the local scope IIFE’s create, you can selectively choose what to expose or what not to expose.
Here, we declare an object. However, property secretCode is exposed for public usage.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
var weakSauce = { secretCode: "Zorb!", checkCode: function (code) { if (this.secretCode == code) { alert("You are awesome!"); } else { alert("Try again!"); } } }; var bar = Object.create(weakSauce); console.log(bar.secretCode) // exposed! |
By taking advantage of the local scope IIFE’s create, you can selectively choose what to expose or what not to expose
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var awesomeSauce = (function () { var secretCode = "Zorb!"; function privateCheckCode(code) { if (secretCode == code) { alert("You are awesome!"); } else { alert("Try again!"); } } // the public method we want to return return { checkCode: privateCheckCode }; })(); var foo = Object.create(awesomeSauce); alert(foo.secretCode); // undefined |
This time around, you won’t be able to access secretCode. The reason is that the contents of secretCode are buried inside the IIFE and not accessible publicly.
Usage: To Avoid Polluting the Global Scope
Let’s say you’re implementing your own objects. You have different function interfaces like so…
rtScript.js
1 2 3 4 5 |
var gCounter = 0; function toString() { ...} function add() { ... } |
The problem is, when you import your file into projects, what if other implementations have the same names as well? It would lead to name collisions.
So in order to solve this problem, we want to hide these function interfaces and just expose a global variable name like so:
1 2 3 4 5 6 7 |
var rtScript = (function() { var gCounter = 0; return { toString: function {...} add: function{...} } })(); |
What this does is that it hides all the function interfaces and global variables used in your implementation.
All that’s exposed is the rtScript variable.
Hence others can use your implementation like so:
otherCode.js
1 2 3 4 5 6 |
(function(rt) { rt.toString(); rt.add(); }(rtScript)); |
Usage: capture surrounding state via Closure, and parameter passing
Given we have a function quotatious
It takes an array, and pushes a inner function onto it. Thus, it manipulates the references in quotes and points them to their own respective inner function. Even though the scope have not been created just yet, each inner function references its surround environment. Thus, they all reference the i from the loop and the array names.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function quotatious(names) { var quotes = []; for (var i = 0; i < names.length; i++) { var theFunction = function() { return "My name is " + names[i] + "!"; } quotes.push(theFunction); } return quotes; } |
In the outer scope, when we declare people to be an array with let’s say 4 elements, and pass it into quotatious, we will see that quotes array inside quotatious will have its references pointing to inner functions.
Each inner function will reference i as well as name.
Then quotes array will be returned by quotations function.
We then execute its element, which is a reference to the inner function.
1 2 3 4 5 6 7 8 |
// our list of names var people = ["Tony Stark", "John Rambo", "James Bond", "Rick James"]; // getting an array of functions var peopleFunctions = quotatious(people); // get the first function peopleFunctions[0](); |
When we execute the 0th element of peopleFunction, it will execute the inner function. The inner function will create scope, and reference i. But at this point, i is 5 because it shared among all the inner functions in quotatious.
Thus, names[5] returns undefined.
Solution
The trick is to create a shell that holds the correct i for each inner function.
Hence we create an IIFE first. the IIFE will take in the i as a parameter, and copy the value into the parameter index.
names is captured by closure, where as i is captured by parameter passing.
1 2 3 4 5 |
for (var i = 0; i < names.length; i++) { (function(index) { // IIFE })(i); } |
This is the key here. As different i gets passed into the IIFE, the i is kept as that value via the parameter index. Hence any inner function in the IIFE will always be able to reference index as that value.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
for (var i = 0; i < names.length; i++) { (function(index) { // IIFE var temp = function() { // inner function return names[index]; // green dotted line } quotes.push(temp); // black line })(i); } return quotes; |
So when we use quotes to execute the inner function, the inner function will be able to get its own referenced i.
1 |
quotes[0](); // purple arrow |
Insertion Sort
http://www.geeksforgeeks.org/insertion-sort/
Insertion sort is a simple sorting algorithm that works the way we sort playing cards in our hands
Say we’re given a data set, 8 5 3 12 2
Option 1 – previous is larger
We start off the algorithm at index 1.
So we see 5 and store it in a temp variable.
There are 2 conditions we must satisfy:
1) Is the current index > 0 ? (1 > 0 ? yes )
2) Is the previous element is larger than our temp? 8 > 5, yes.
Both satisfies so we copy the previous element (8) to the current location where 5 is.
We move 1 step back and hit index 0.
We evaluate the 2 situations again:
1) 0 > 0, no. We stop.
If we reach index 0, that means we’ve reached the end, and we store 5 there.
We then move on to index 2 and continue processing in the same manner.
Option 2 – previous is smaller
If say we have 3 5 7.
We start off at index 1.
We see 5 and store it in a temp variable.
There are 2 conditions we must satisfy:
1) Is the current index > 0 ? (1 > 0 ? yes )
2) Is the previous element is larger than our temp? 3 > 5, no.
In this case, we don’t do anything move onto the next index 2.
This is the part where if the whole array is sorted, we only execute this comparison and then move on to the next index. Since the previous will always be smaller than the current, we simply stop and move on. Hence, the best case is O(n) because we do execute a comparison at every index once. We do this for every element and then stop.
Insertion sort takes the current number and go backwards to see where to insert it. We do this by comparing the previous element and seeing if its bigger than our valueToInsert. If it’s larger, push it down and we keep going. If it’s smaller, we stop and we insert our number. If we reach the end at index 0, we insert our number.
Now we have 5 8 3 12 2.
We work our next pass which is index 2 with value 3.
We store 3 and start searching for the insertion point. We evaluate the 2 points:
1) is index > 0? 2 > 0, yes.
2) We look at the previous element 8. is 8 > 3? yes, so we move 8 to where 3 is.
Move backwards 1 step.
1) is index > 0? 1 > 0, yes.
2) Then we look at previous element 5, is 5 > 3? yes. so we move 5 to where 8 is.
1) is index > 0? 0 > 0, no. We’ve reached the end. Hence we put 3 at index 0.
This leaves us with 3 5 8 12 2.
Then we work on value 12.
1) is index > 0? index 3 > 0, yes.
2) Previous element 8 > 12?
no. We stop, and move on.
Moving on…finally, our last value is 2. Apply the same as we did above and we’ll see that 2 will be inserted at the way in the beginning because it is smaller than the other numbers.
Time Complexity
The best case input is an array that is already sorted. In this case insertion sort has a linear running time (i.e., O(n)). During each iteration, the first remaining element of the input is only compared with the right-most element of the sorted subsection of the array.
The simplest worst case input is an array sorted in reverse order. The set of all worst case inputs consists of all arrays where each element is the smallest or second-smallest of the elements before it. In these cases every iteration of the inner loop will scan and shift the entire sorted subsection of the array before inserting the next element. This gives insertion sort a quadratic running time O(n^2).
The average case is also quadratic, which makes insertion sort impractical for sorting large arrays.
However, insertion sort is one of the fastest algorithms for sorting very small arrays, even faster than quicksort; indeed, good quicksort implementations use insertion sort for arrays smaller than a certain threshold, also when arising as subproblems; the exact threshold must be determined experimentally and depends on the machine, but is commonly around ten.
Selection sort
https://en.wikipedia.org/wiki/Selection_sort
Worst-case performance О(n2) comparisons, О(n) swaps
Best-case performance О(n2) comparisons, О(n) swaps
The idea is that we latch onto the first element and deem it being the minimum.
We then go through the rest of the array, and look for anything that is smaller. If so, we swap. This is 1 pass.
At this point, after the 1st pass, we deem first element as minimum. Then we move on
to the next element and call it the minimum. We do another pass and look in the leftover array
to see if there’s anything smaller than our minimum. If so, swap.
We keep doing this, pulling minimum numbers from the array and pushing it towards the front, until
all the elements are sorted from smallest to largest.
So there’s 2 loops: outer and inner.
Outer simply goes through the array.
The inner goes through the remaining elements AFTER the outer via a loop and compares it to the outer.
if the inner element is smaller than the outer element, then we swap. Essentially what is happening is that we are bringing in the smallest element to the front.
Example
1 |
var dataArray = [8,3,1, 6, 2]; |
Given an array, we make passes on it where:
– we select the starting value to be the minimum.
– we then make (length of array – 1) passes over it. (through all of our passes, the last element will be automatically sorted, so no need to do anything on it)
– For every pass, we need to do the very important action of pulling the minimum from the array and swapping it with the first value.
Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
Outer starts at index 0, which is value 6 . [6] 8 0 7 6 4 3 1 5 10 Inner starts at the next element at value 8. 8 < 6? no 0 < 6? yes. swap [0] 8 6 7 6 4 3 1 5 10 7 < 0? no 6 < 0? no 4 < 0? no 3 < 0? no 1 < 0? no 5 < 0? no 10 < 0? no. outer 0 is finished. 0 [8] 6 7 6 4 3 1 5 10 outer 2. minimum starts at index 1, which is value 8. Inner starts at next element, which is value 6. 6 < 8? yes. swap 0 [6] 8 7 6 4 3 1 5 10 7 < 6? no 6 < 6? no 4 < 6? yes, swap. 0 [4] 8 7 6 6 3 1 5 10 3 < 4? yes, swap 0 [3] 8 7 6 6 4 1 5 10 1 < 3? yes, swap 0 [1] 8 7 6 6 4 3 5 10 5 < 1? no 10 < 1? no. Outer index 1 finished. Outer index 2 starts, which is value 8. 0 1 [8] 7 6 6 4 3 5 10 |
…continuing on in the same fashion…
Eventually all the smallest number will be swapped to the front, resulting in the list being sorted.
full source
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
"use strict"; // GIVEN // E A D H B // first pass looks for the minimal value, and swaps it with the value at the front of the list // A E D H B // next pass finds the minimal value after the first element (which is now in place) and swaps it. // so from e, d, h, b..b is smallest and swap with the next value, which is e. // A B D H E // move on, and do another pass , D, H, E. // D is smallest so everything stays // A B D H E // move on, and do another pass. H, E. // E is smaller is swap H and E. // A B D E H var dataArray = [8,3,1,6,2]; function swap(array, index1, index2) { var temp = array[index1]; array[index1] = array[index2]; array[index2] = temp; } // given a certain pass // we get the minimum value and swap it function swapSmallerValuesToFront(data, outerIndex, lastIndex) { for (var innerIndex = outerIndex + 1; innerIndex <= lastIndex; innerIndex++) { console.log(""); console.log("inner index: " + innerIndex + ", data[inner]: " + data[innerIndex]); console.log("data[outerIndex]: " + data[outerIndex]); if (data[innerIndex] < data[outerIndex]) { console.log(" We need to swap because current element is smaller"); swap(data, innerIndex, outerIndex); console.log("---> data display: " + data); } } } function selectionSort(intArray) { var data = intArray.slice(0); var indexOfMinValue, temp; var lastIndex = data.length-1; // we always do length - 1 passes for (var pass = 0; pass <= lastIndex - 1; pass++) { console.log("---------------> Outer Index: " + pass + " <-----------"); swapSmallerValuesToFront(data, pass, lastIndex); } return data; } let sortedArray = selectionSort(dataArray); console.log(sortedArray); |
Bubble Sort
http://www.geeksforgeeks.org/bubble-sort/
“Bubble” up the largest number in the array. Once that’s done, we “Bubble” again to the array-1 for the next largest number. We keep doing it until we have “Bubbled” every number in the array.
Think of it as looping through the array and looking at each tuple. If a tuple is where element1 > element2, then we swap. This way, the largest element of this run will “Bubble” up to the end. (very right side).
Due to us checking every tuple, we need to makes sure we check from 1..length-1 and NOT 1..length:
(5 1) 4 2 8 –> [1 5] 4 2 8, Here, algorithm compares the first two elements, and swaps since 5 > 1.
1 (5 4) 2 8 –> 1 [4 5] 2 8, Swap since 5 > 4
1 4 (5 2) 8 –> 1 4 [2 5] 8, Swap since 5 > 2
1 4 2 (5 8) –> 1 4 2 [5 8], Now, since these elements are already in order (8 > 5), algorithm does not swap them.
As tuples, the last index should be
element[last-1], element[last]
Therefore, we only need to check from (index 0) to (< lastIndex-1) because it does not make sense at all to swap the very last element of the array to its next element because the next element does not exist. The last swap we do should be data[lastIndex-1], data[lastIndex]. That is why we give outer - 1 into bubbleUpLargest.
Example
Example:
First Pass:
( 5 1 4 2 8 ) –> ( 1 5 4 2 8 ), Here, algorithm compares the first two elements, and swaps since 5 > 1.
( 1 5 4 2 8 ) –> ( 1 4 5 2 8 ), Swap since 5 > 4
( 1 4 5 2 8 ) –> ( 1 4 2 5 8 ), Swap since 5 > 2
( 1 4 2 5 8 ) –> ( 1 4 2 5 8 ), Now, since these elements are already in order (8 > 5), algorithm does not swap them.
Second Pass:
( 1 4 2 5 8 ) –> ( 1 4 2 5 8 )
( 1 4 2 5 8 ) –> ( 1 2 4 5 8 ), Swap since 4 > 2
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )
Now, the array is already sorted, but our algorithm does not know if it is completed. The algorithm needs one whole pass without any swap to know it is sorted.
Third Pass:
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )
Numerical example
Given: 6 8 0 7 6 4 3 1 5 10
We compare tuples, so we loop a total of length – 1 times.
[6 8] 0 7 6 4 3 1 5 10
is 6 > 8? no
6 [8 0] 7 6 4 3 1 5 10
is 8 > 0? yes, swap
6 0 [8 7] 6 4 3 1 5 10
is 8 > 7? yes, swap
6 0 7 [8 6] 4 3 1 5 10
is 8 > 6? yes, swap
6 0 7 6 [8 4] 3 1 5 10
is 8 > 4? yes, swap
6 0 7 6 4 [8, 3] 1 5 10
is 8 > 3? yes, swap
6 0 7 6 4 3 [8, 1] 5 10
is 8 > 1? yes, swap
6 0 7 6 4 3 1 [8, 5] 10
is 8 > 5? yes, swap
6 0 7 6 4 3 1 5 [8 10]
is 8 > 10? no
pass 1 finished, so as you can see 8 “bubbles up”, until it meets 10.
Since we’re done, we are confident that the last number is largest. 10 is done, so we move our lastIndex down and process the leftover array for the 2nd pass.
[6 0] 7 6 4 3 1 5 8 (10)
is 6 > 0? yes, swap
0 [6 7] 6 4 3 1 5 8
is 6 > 7? no, move on
0 6 [7 6] 4 3 1 5 8
is 7 > 6? yes, swap
0 6 6 [7 4] 3 1 5 8
is 7 > 4? yes, swap
0 6 6 4 [7 3] 1 5 8
is 7 > 3? yes, swap
0 6 6 4 3 [7 1] 5 8
is 7 > 1? yes, swap
0 6 6 4 3 1 [7 5] 8
is 7 > 5? yes, swap
0 6 6 4 3 1 5 [7 8]
is 7 > 8? no
pass 2 finished, so as you can see, 7 “bubbles up”.
0 6 6 4 3 1 5 7 (8) (10) so now, 8, 10 done. We need to process 0 6 6 4 3 1 5 7.
We move our lastIndex down and process the leftover array for the 3rd pass.
..this keeps going until all numbers are sorted from smallest to largest.
the bubble sort
1 2 3 4 5 6 7 8 9 10 11 |
function bubbleSort(intArray) { var data = intArray.slice(0); // own copy var numElements = data.length; for (var lastIndex = numElements-1; lastIndex >=1; lastIndex--) { console.log("---"+lastIndex+"---"); bubbleUpLargestViaTuples(data, lastIndex); } return data; } |
bubbleUpLargest
Then in here, we simply go through all the elements and do its swapping. Think of it as
looping through tuples and swapping them.
1 2 3 4 5 6 7 8 9 10 11 12 |
function bubbleUpLargestViaTuples(data, lastIndex) { for (var inner = 0; inner < lastIndex; inner++) { console.log("inner: " + inner); console.log("data[inner]: " + data[inner]); console.log("data[inner+1]: " + data[inner+1]); if(data[inner] > data[inner+1]) { swap(data, inner, inner+1); } } } |
The swap function is just a standard swap
1 2 3 4 5 |
function swap(arr, index1, index2) { var temp = arr[index1]; arr[index1] = arr[index2]; arr[index2] = temp; } |
Worst-case performance O(n^2)
Best-case performance O(n)
Average performance O(n^2)
The only significant advantage that bubble sort has over most other implementations, even quicksort, but not insertion sort, is that the ability to detect that the list is sorted efficiently is built into the algorithm. When the list is already sorted (best-case), the complexity of bubble sort is only O(n). By contrast, most other algorithms, even those with better average-case complexity, perform their entire sorting process on the set and thus are more complex.
However, not only does insertion sort have this mechanism too, but it also performs better on a list that is substantially sorted (having a small number of inversions).
Bubble sort should be avoided in the case of large collections. It will not be efficient in the case of a reverse-ordered collection.
source code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
"user strict"; var dataArray = [6,8,0,7,6,4,3,1,5,10]; function swap(array, index1, index2) { var temp = array[index1]; array[index1] = array[index2]; array[index2] = temp; } function bubbleUpLargestViaTuples(data, lastIndex) { for (var inner = 0; inner < lastIndex; inner++) { console.log("inner: " + inner); console.log("data[inner]: " + data[inner]); console.log("data[inner+1]: " + data[inner+1]); if(data[inner] > data[inner+1]) { swap(data, inner, inner+1); } } } function bubbleSort(intArray) { var data = intArray.slice(0); // own copy var numElements = data.length; for (var lastIndex = numElements-1; lastIndex >=1; lastIndex--) { console.log("---"+lastIndex+"---"); bubbleUpLargestViaTuples(data, lastIndex); } return data; } let sorted = bubbleSort(dataArray); console.log(sorted); |
Promise (js)
http://javascript.info/promise-basics
https://scotch.io/tutorials/javascript-promises-for-dummies
https://www.toptal.com/javascript/asynchronous-javascript-async-await-tutorial
https://stackoverflow.com/questions/39988890/do-javascript-promises-block-the-stack
Why do we need promises?
Say you have a url, when given two numbers, will return you the sum.
You give the url two numbers in its parameter, and issue a remote call to get the result.
Naturally, you need to wait for the result.
Sometimes, its slow in response, etc. You don’t want your entire process to be blocked while waiting for the result.
Calling APIs, downloading files, reading files are among some of the usual async operations that you’ll perform.
The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value.
So this means once your Promise starts running (whether executing a timer or downloading from the internet) execution will continue on down the code. Once your Promise finishes (resolve or reject), then execution returns to the callback that you give to your Promise.
In this tutorial, we use setTimeout to simulate this server request response time.
1 2 3 4 5 6 7 8 9 |
function addAsync(num1, num2, callback) { setTimeout(function() { callback(num1 + num2); // result the result via callback }, 2000); // takes 2 seconds to simulate web } addAsync(1, 3, function(result) { console.log(result); // 4 }); |
The problem happens when we want to do callbacks in succession
say we want to add 1, 2, then get the result, and add 3. The get that
result and add 4.
If the calculation functionality is on a server, and we do this through this via callbacks, it would look like this:
1 2 3 4 5 6 7 8 9 |
addAsync(2, 4, function(result1) { console.log("result1: " + result1); addAsync(result1, 3, function(result2) { console.log("result2: " + result2); addAsync(result2, 8, function(result3) { console.log("result3: " + result3); }); }); }); |
result
info: querying url http://www.add.com
result1: 6
info: querying url http://www.add.com
result2: 9
info: querying url http://www.add.com
result3: 17
The syntax is less user friendly. In a nicer term, It looks like a pyramid, but people usually refer this as “callback hell”, because the callback nested into another callback. Imagine you have 10 callbacks, your code will nested 10 times!
Solution – Promise
Promises were the next logical step in escaping callback hell. This method did not remove the use of callbacks, but it made the chaining of functions straightforward and simplified the code, making it much easier to read.
Using promises in our previous example would make it simple.
First, we declare a function addAsync with two numbers to add.
We create a Promise object. Within our new created Promise object, we have a callback function parameter. We provide code that we want to execute within this callback. The callback function itself, has two parameters: resolve, and reject. Once the code has been executed and result is received, it will call resolve/reject in our code. This is so that a ‘then’ can be chained later on.
1 2 3 4 5 6 7 8 9 10 11 |
function addAsync(num1, num2) { let p = new Promise(function (resolve, reject) { ... resolve(num1+num2); }); return p; } let tmp = addAsync(1,2); |
How is it a then() can be chained later on? The Promise object is returned from our addAsync function, for others to use. In other words, when they execute this addAsync, they will be returned the Promise object.
The reason why we want to return the promise object is so that others can call then on it. Calling then on a Promise object means that you’ll get notified when a resolve or reject is called within the Promise. The first parameter callback of our then() will be triggered if its a resolve. 2nd parameter callback of our then() will be triggered if it’s a reject.
1 2 3 4 5 6 7 |
let tmp = addAsync(1,2); tmp.then(function(val) { // resolve from Promise obj in addAsync called }, function() { // rejected from Promise obj in addAsync called }); |
The then function simply waits for the resolve or reject to happen.
If its a resolve, the first callback parameter of the then will be executed. If reject, the 2nd callback parameter will be executed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function addAsync(num1, num2) { let p = new Promise((resolve, reject) => { setTimeout(function() { resolve(num1 + num2); }, 2000); }); return p; } addAsync(10, 10).then(result=> { // AFTER 2 seconds, num1+num2 is passed to parameter result console.log(result); }); |
Within setup of a Promise instance, we do a setTimeout to simulate a remote request that takes 2 seconds just like our previous example. Notice
that we don’t deal with custom callbacks. We now use resolve and then for the results.
Promise object’s resolve will trigger then()
1 2 3 |
addAsync(1, 2).then(success => { console.log("success! ...result is: " + success); }); |
Further Chaining…
ref – https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
Once a Promise is fulfilled or rejected, the respective handler function (onFulfilled or onRejected parameters of then() ) will be called asynchronously (scheduled in the current thread loop). The behavior of the handler function follows a specific set of rules.
If a handler function:
returns a value, the promise returned by then gets resolved with the returned value as its value
1 2 3 4 |
let aPromise = addAsync(1,2); aPromise.then(function(value) { // resolve handler return value; }); |
// much later…
output of aPromise is:
Promise {
__proto__: Promise
[[PromiseStatus]]: “resolved”
[[PromiseValue]]: 3
doesn’t return anything
the promise returned by then gets resolved with an undefined value
1 2 3 |
let aPromise = addAsync(1,2); aPromise.then(function(value) { // resolve handler }); |
output of aPromise is:
Promise {
__proto__: Promise
[[PromiseStatus]]: “resolved”
[[PromiseValue]]: undefined
throws an error
So if we only call the reject, it’ll only hit the reject handler like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
function executePromise(i) { return new Promise(function(resolve, reject) { if (i > 8) { resolve(i-1); } else { reject("Booo...less than 8."); } }); } executePromise(7).then(function(result) { // resolve console.log('RESOLVED: ' + result); }, function(err) { // reject console.log('REJECTED : ' + err); }).catch(function(err){ console.log('CAUGHT: ' + err); }); |
output: REJECTED : Booo…less than 8.
Note that if you DO NOT HAVE a reject handler, the reject will automatically be taken care of in the catch handler.
Normally, the catch handler catches any thing throw in the Promise object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
function executePromise(i) { return new Promise(function(resolve, reject) { if (i > 8) { resolve(i-1); } else if ( i < 8 && i > 0 ) { throw 'Ooopssiiie'; } else { reject("Booo...less than 8."); } }); } executePromise(7).then(function(result) { console.log('RESOLVED: ' + result); }, function(err) { // reject console.log('REJECTED : ' + err); }).catch(function(err){ console.log('CAUGHT: ' + err); // comes here }); |
output:
REJECTED : Ooopssiiie
There is a situation where where you have an async operation inside of your Promise object. As a result, you throw 1 second later. It won’t catch because the Promise object has already been executed.
The only thing that works is reject, which naturally gets caught, given there is no reject handler.
1 2 3 4 5 6 7 8 9 10 11 |
new Promise(function(resolve, reject) { setTimeout(function() { //throw 'or nah'; // won't work // return Promise.reject('or nah'); also won't work reject('hehehehe'); }, 1000); }).catch(function(e) { console.log(e); // reject caught here }); |
catch is not like then(), where then() waits for the resolve/reject. Catch DOES NOT WAIT for the throw
The idea here is that you use resolve/reject in your Promise to take care of any async operations so that the then() gets triggered when the async operation is done. Then, you go on with your throw/catches.
Never use async callbacks in your Promise object. Use resolve/reject first.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function timeout(duration) { console.log(' wait ' + duration + ' milli-seconds '); return new Promise(function(resolve) { console.log('...new Promise '); setTimeout(resolve, duration); }); } // then catches the resolve timeout(3000).then(function() { console.log('...then'); throw 'worky!'; // return Promise.reject('worky'); also works }).catch(function(e) { console.log('catch: ' + e); // 'worky!' }); |
This is basically why you should not put async callbacks inside of promises.
Promise object returned at then() over-rides previous Promises.
Chaining the Promise
Let’s chain multiple Promises together.
1) The first promise is that if mom is happy she’ll give you the phone
2) Given 1), you then promise to show your friend the phone.
1)
Create the promise. If mom is happy, call resolve with the phone object.
If not happy, then call the reject with the error object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
var isMomHappy = true; var willIGetNewPhone = new Promise( function (resolve, reject) { console.log("-- willIGetNewPhone --"); if (isMomHappy) { console.log("Yay! Mom is happy"); var phone = { brand: 'Apple', color: 'Red' }; resolve(phone); // fulfilled } else { console.log("Uh oh, Mom NOT happy"); var reason = new Error('mom is not happy'); reject(reason); // reject } } // Promise function ); //Promise |
2) Create the 2nd Promise
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// create the functionality var showOff = function (phone) { // create object console.log("-- showOff --"); return new Promise( function (resolve, reject) { var message = 'Hey friend, I have a new ' + phone.color + ' ' + phone.brand + ' phone'; resolve(message); } // function ); // Promise }; // object |
Chain em together
Let’s Chain the promises together. First, WillIGetNewPhone is the first promise. It runs, and when it resolves via .then, it will pass the phone object to the 2nd promise in obj showOff.
Once showOff resolves, it will then go to its resolve function definition and do the display.
finally, don’t forget to call askMom().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// call our promise var askMom = function () { willIGetNewPhone .then(showOff) // chain it here .then(function (fulfilled) { console.log(fulfilled); // output: 'Hey friend, I have a new black Samsung phone.' }) .catch(function (error) { // oops, mom don't buy it console.log(error.message); // output: 'mom is not happy' }); }; askMom(); |
Mom, Dad example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
var isMomHappy = true; var isDadHome = true; // CREATE the Promise var askMomForPhone = new Promise(function (resolve, reject) { console.log("askMomForPhone - new Promise created"); console.log("askMomForPhone - lets implement the code and let it know when to call resolve/reject"); if (isMomHappy) { var phone = { brand: 'Apple', color: 'Red' }; console.log("askMomForPhone - Success condition. Let's call RESOLVE"); resolve(phone); // fulfilled } else { var reason = new Error('ERROR - mom is not happy'); console.log("askMomForPhone - Unsuccessful condition (MOM NOT HAPPY). Let's call REJECT"); reject(reason); // reject } } // Promise function ); //Promise // CONSUME THE Promise var askMom = function () { console.log("askMomForPhone - Let's call object willIGetNewPhone, it then instantiate a new Promise object..."); askMomForPhone .then(function (fulfilled) { console.log("askMomForPhone - .then"); console.log(fulfilled); }) .catch(function (error) { console.log("askMomForPhone - .catch"); console.log(error.message); console.log("If mom is not happy, then let's ask DAD!!! :X "); askDad(); }); }; var askDadForPhone = new Promise(function(resolve, reject) { console.log("askDadForPhone - new Promise created"); console.log("askDadForPhone - lets implement the code and let it know when to call resolve/reject"); var phone = { brand: 'ZTE', color: 'blue' }; if(isDadHome == true && isMomHappy == false) { console.log("askDadForPhone - Success condition (MOM IS NOT HAPPY, BUT DAD IS HOME!) Let's resolve it by having Dad buy it."); resolve(phone); } else if (isMomHappy == true) { console.log("askDadForPhone - Success condition (MOM IS HAPPY, DAD MAY OR MAY NOT BE HOME.) DOESN'T MATTER, DO NOTHING."); } else { console.log("askDadForPhone - Unsuccess condition (DAD IS NOT HOME) - Let's call REJECT"); reject("ERROR - dad is not home!"); } }); var askDad = function() { askDadForPhone .then(function(phone){ console.log("-- RESOLVE --"); console.log("askDadForPhone - .then"); console.log(phone); }) .catch(function(error){ console.log("askDadForPhone - .catch"); console.log(error); }); }; console.log("\n------- Let's run this script! ---------"); askMom(); // run the promise consumption |
Similarly
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
function promise1(prev) { return new Promise((resolve, reject) => { output.push('Promise 1'); console.log("pushed promise 1 to array"); setTimeout(() => { console.log("promise 1 resolved"); resolve(output) }, 2500); }); } function promise2(prev) { return new Promise((resolve, reject) => { output.push('Promise 2'); console.log("pushed promise 2 to array"); setTimeout(() => { console.log("promise 2 resolved"); resolve(output) }, 3500); }); } function promise3(prev) { return new Promise((resolve, reject) => { output.push('Promise 3'); console.log("pushed promise 3 to array"); setTimeout(() => { console.log("promise 3 resolved"); resolve(output) }, 500); }); } let output = []; promise1(output) .then((output) => { // resolve from promise1 will call this 'then' console.log("promise 1 done, let's go promise 2"); return promise2(output); }) .then((output) => { // resolve from promise 2 will call this 'then' console.log("promise 2 done, let's go promise 3"); return promise3(output); }) .then((output) => { console.log("promise 3 done. log output"); console.log(output); // ['Promise 1', 'Promise 2', 'Promise 3'] }) .catch((error) => { // Something went wrong }); |
Waiting for all tasks to be done
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
// Instead do this function promise1(prev) { return new Promise((resolve, reject) => { output.push('Promise 1'); console.log("pushed promise 1 to array"); setTimeout(() => { console.log("promise 1 resolved"); resolve(output) }, 2500); }); } function promise2(prev) { return new Promise((resolve, reject) => { output.push('Promise 2'); console.log("pushed promise 2 to array"); setTimeout(() => { console.log("promise 2 resolved"); resolve(output) }, 3500); }); } function promise3(prev) { return new Promise((resolve, reject) => { output.push('Promise 3'); console.log("pushed promise 3 to array"); setTimeout(() => { console.log("promise 3 resolved"); resolve(output) }, 500); }); } let output = []; Promise.all([ promise1(output), promise2(output), promise3(output) ]) .then(function(output) { console.log("!! -- all done -- !!"); console.log(output); // ['Promise 1', 'Promise 2', 'Promise 3'] }); |
Promise All
Promise All runs all its promises parallel. When all of its promises are done, it will then run its resolve.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
var max = 12; var max2 = 5; var max3 = 8; var promise1 = new Promise(function(resolve, reject){ let interval = setInterval(function(){ console.log("promise1: "+max--); if (max === 1) { resolve(1); clearInterval(interval); } }, 1000); }); var promise2 = new Promise(function(resolve, reject){ let interval2 = setInterval(function(){ console.log("promise2: "+max2--); if (max2 === 2) { resolve(2); clearInterval(interval2); } }, 1200); }); var promise3 = new Promise(function(resolve, reject) { let interval3 = setInterval(function(){ console.log("promise3: "+max3--); if (max3 === 3) { resolve(3); clearInterval(interval3); } }, 1300); }); Promise.all([promise1, promise2, promise3]).then(function(values) { console.log(values); }); // expected output: Array [1, 2, 3] |
Usages
https://medium.com/@ivantsov/promises-in-js-one-more-interview-question-6d59634a3463
Given:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
function foo() { console.log('foo start'); return new Promise(resolve => { console.log('new Promise in foo'); setTimeout(() => { console.log('foo resolved...!'); resolve('foo resolved'); }, 3000); console.log('foo Promise created and returned'); }); } // foo function bar() { console.log('bar start'); return new Promise(resolve => { console.log('new Promise in bar'); setTimeout(() => { console.log('bar resolved...!'); resolve('bar resolved'); }, 4000); console.log('bar Promise created and returned'); }); } |
Example 1
1 2 3 4 5 6 7 |
foo().then(function() { return bar().then(function(result){ console.log('result: ' + result); }); }).then(function(finalResult) { console.log('finalResult: ' + finalResult); }); |
output:
foo start
new Promise in foo
foo Promise created and returned
foo resolved…!
bar start
new Promise in bar
bar Promise created and returned
bar resolved…!
result: bar resolved
finalResult: undefined
The reason why finalResult is undefined is due to:
1 2 3 4 5 6 7 8 |
foo().then(function() { return bar().then(function(result){ console.log('result: ' + result); // no return object here }); }).then(function(finalResult) { console.log('finalResult: ' + finalResult); }); |
Thus, bar() returns an object that resolves. The then() captures it and then at this point, there is nothing. Hence, we need to return another Promise object like so:
1 2 3 4 5 6 7 8 9 10 11 |
foo().then(function() { return bar().then(function(result){ console.log('result: ' + result); // returns Promise object that // resolves with value result return result; }); }).then(function(finalResult) { console.log('finalResult: ' + finalResult); }); |
in order to propagate data further onto the next then().
Errors
Handling errors is basically the 2nd parameter. Just implement a callback for it and handle the parameter of your callback.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
function executePromise(i) { return new Promise(function(resolve, reject) { // do a thing, possibly async, then... if (i > 8) { resolve(i-1); } else { reject("Booo...less than 8."); } }); } executePromise(7).then(function(result){ console.log(result); }, function(err) { console.log("hoooo dayam!!!! error yo: " + err); }); |
await vs then
ref – https://dev.to/masteringjs/using-then-vs-async-await-in-javascript-2pma
await is simply syntactic sugar that replaces a .then() handler with a bit simpler syntax. But, under the covers the operation is similar.
The code that comes after the await (that is within the async function) is basically put inside an invisible .then() handler.
JavaScript will pause the async function’s execution until the promise settles. All code that is after the await will wait (block) until the await settles.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
const waitFiveSeconds = () => { return new Promise((resolve, reject) => { console.log('Promise done') setTimeout(() => { resolve(88); }, 5000) }) } async function test() { console.log('Ready'); const result= await waitFiveSeconds(); // code here will wait until waitFiveSeconds settles console.log('I will print second: ' + result); console.log('end of function test') } test(); // async operation console.log('I will print first'); |
With then(), the rest of the function will continue to execute just like how async/await lets all code below the async function run. When the Promise settles, the then() will then executes. So in other words, all code below await, is very similar to having a then.
Therefore, await is just an internal version of .then() (doing basically the same thing). The reason to choose one over the other doesn’t really have to do with performance, but has to do with desired coding style or coding convenience. Certainly, the interpreter has a few more opportunities to optimize things internally with await, but it’s unlikely that should be how you decide which to use. If all else was equal, I would choose await for the reason cited above. But, I’d first choose which made the code simpler to write and understand and maintain and test.
Used properly, await can often save you a bunch of lines of code making your code simpler to read, test and maintain. That’s why it was invented.
There’s no meaningful difference between the two versions of your code. Both achieve the same result when the axios call is successful or has an error.
Where await could make more of a convenience difference is if you had multiple successive asynchronous calls that needed to be serialized. Then, rather than bracketing them each inside a .then() handler to chain them properly, you could just use await and have simpler looking code.
apply, bind, call
https://hangar.runway7.net/javascript/difference-call-apply
https://javascript.info/bind
call
When invoking functions with call:
1) the 1st object is used as the ‘this’
2) additional arguments used with call will act as the function’s parameters.
Note that the first argument to call () sets the ‘this’ value. The other arguments after the first argument are passed as parameters to the avg () function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
//global variable var avgScore = "global aveScore"; //global function function ave (arrayOfScores) { console.log("--> ave"); var sumOfScores = 0; for (score in arrayOfScores) { console.log("adding score: " + arrayOfScores[score]); sumOfScores += score; } // whenever we declare a property on this object in a function, // it is bound to the global object this.aveScore = sumOfScores / arrayOfScores.length; console.log("ave: " + sumOfScores / arrayOfScores.length); console.log("<-- ave"); } var gameController = { scores : [20, 34, 55, 46, 77], avgScore: null }; //ave(gameController.scores); //console.log("global.aveScore: " + global.aveScore); // 236.8 //console.log("gameController.aveScore: " + gameController.aveScore); //undefined // using CALL // first parameter is used as the reference to this // second parameter acts as the 1st argument passed in ave.call(gameController, gameController.scores); console.log("global.aveScore: " + global.aveScore); // undefined console.log("gameController.aveScore: " + gameController.aveScore); // 246.8 |
another example for call
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
"use strict" var person1 = {name: 'Marvin', age: 42, size: '2xM'}; var person2 = {name: 'Zaphod', age: 42000000000, size: '1xS'}; var sayHello = function(){ console.log('Hello, ' + this.name); }; var sayGoodbye = function(){ console.log('Goodbye, ' + this.name); }; sayHello.call(person1); sayGoodbye.call(person2); sayHello.apply(person1); sayGoodbye.apply(person2); |
The functions sayHello, sayGoodybye are run with the ‘this’ context set to objects person1 and person2. The ‘this’ in the function will reference those objects.
1 2 3 4 5 |
sayHello.call(person1); sayGoodbye.call(person2); sayHello.apply(person1); sayGoodbye.apply(person2); |
The apply and call methods are almost identical when setting the this value except that you pass the function parameters to apply () as an array, while you have to list the parameters individually to pass them to the call () method.
In other words, both call and apply perform very similar functions: they execute a function in the context, or scope, of the first argument that you pass to them. Also, they’re both functions that can only be called on other functions.
The difference is when you want to seed this call with a set of arguments. Say you want to make a say() method that’s a little more dynamic:
call would be:
1 2 3 4 5 6 |
var say = function(greeting){ alert(greeting + ', ' + this.name); }; say.call(person1, 'Hello'); say.call(person2, 'Goodbye'); |
It runs the function in the context of the first argument, and subsequent arguments are passed in to the function to work with. So how does it work with more than one argument?
1 2 3 4 5 6 7 |
var update = function(name, age, size) { this.name = name; this.age = age; this.size = size; }; update.call(person1, 'Slarty', 200, '1xM'); |
difference between call and apply
Both can be called on functions, which they run in the context of the first argument. In call the subsequent arguments are passed in to the function as they are, while apply expects the second argument to be an array that it unpacks as arguments for the called function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
"use strict" var person1 = {name: 'Marvin', age: 42, size: '2xM'}; var person2 = {name: 'Zaphod', age: 42000000000, size: '1xS'}; var sayHello = function(){ console.log('Hello, ' + this.name); }; var sayGoodbye = function(){ console.log('Goodbye, ' + this.name); }; sayHello.call(person1); sayGoodbye.call(person2); sayHello.apply(person1); sayGoodbye.apply(person2); var update = function(name, age, size) { this.name = name; this.age = age; this.size = size; console.log(name + ", " + age + ", " + size); }; update.call(person1, 'Slarty', 200, '1xM'); // parameters update.apply(person1, ["Ricky", 37, "1xL"]); // array |
Bind
Problem: Losing “this”
We already know that in JavaScript it’s easy to lose this. Once a method is passed somewhere separately from the object – ‘this’ is lost.
Here’s how it may happen with setTimeout:
1 2 3 4 5 6 7 8 9 10 11 |
"use strict" let user = { firstName: "John", sayHi() { console.log("Hello, " + this.firstName); } }; let f = user.sayHi; f(); // "this" is undefined |
As we can see, the output shows not “John” as this.firstName, but undefined!
That’s because f got the function user.sayHi, separately from the object. The last line can be rewritten as:
solution:
REMEMBER that functions bind objects. Hence, we get the function, bind an object to it, and run that function with ().
1 2 3 4 5 6 7 8 9 10 |
let user = { firstName: "John" }; function func() { alert(this.firstName); } let funcUser = func.bind(user); funcUser(); // John |
Here func.bind(user) as a “bound variant” of func, with fixed this=user.
parameters are passed as usual
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
"use strict" let user = { firstName: "John" }; function func(phrase) { console.log(phrase + ', ' + this.firstName); } // bind this to user let funcUser = func.bind(user); funcUser("Hello"); |
Now let’s try with an object method. The object is user, its method is sayHi:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
"use strict" let user = { firstName: "John", sayHi() { console.log("Hello, " + this.firstName); } }; let user2 = { firstName: "Ricky" } let sayHi = user.sayHi.bind(user2); // (*) sayHi(); setTimeout(sayHi, 1000); |
In the line (*) we take the method user.sayHi and bind it to user. The sayHi is a “bound” function, that can be called alone or passed to setTimeout – doesn’t matter, the context will be right.
Here we have the object ‘user’, its function ‘say’. ‘say’ has 1 argument ‘phrase’ which can be passed in normally after the bind.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
"use strict" let user = { firstName: "John", say(phrase) { console.log(phrase + ", " + this.firstName); } }; let say = user.say.bind(user); // first bind the object say("Hello"); // Hello, John ("Hello" argument is passed to say) say("Bye"); // Bye, John ("Bye" is passed to say) |
Binding all functions in an object
If an object “user” has many methods and we plan to actively pass it around, then we could bind them all in a loop.
We use a for loop to go through all the keys in the object user. When the typekey is a function, it means we have stepped up to a function object. That means we have the reference to the function object. In turn, we then use the bind method to bind it to the object we want.
1 2 3 4 5 |
for (let key in user) { if (typeof user[key] == 'function') { user[key] = user[key].bind(user); } } |
A function cannot be re-bound
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
"use strict" function Ricky() { this.name = "Ricky"; this.sayHi = function () { console.log( this.name ); } } var ricky = new Ricky(); ricky.sayHi(); // bind ricky's say Hi to another object with property name john var bound = ricky.sayHi.bind({ name: "John" }); bound(); var bound2 = bound.bind({ name: "Alice" }); bound2(); // still John |
The exotic bound function object returned by f.bind(…) remembers the context (and arguments if provided) only at creation time.
Note that the bind from a function to an object will always hold. It’s the returned object that CANNOT be bind again.
In the below example, we see that we try to bind var bound again, but the output is still John. However, calling bind on a function will always give you a successful bind.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// bind ricky's say Hi to another object with property name john var bound = ricky.sayHi.bind({ name: "John" }); var bound2 = bound.bind({ name: "Alice" }); bound2(); // John bound2 = ricky.sayHi.bind({ name: "Alice" }); bound2(); // alice |