Created
October 30, 2020 17:24
-
-
Save suityou01/f9a451ceb544381437aa6a58ce2dd923 to your computer and use it in GitHub Desktop.
Coroutines
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Types of function in javascript | |
1. Regular function - function f () {} | |
2. Anonymous function - () => {} | |
3. Asynchronous function - async function f () {} | |
4. Asynchronous anonymous function async () {} | |
5. Generator function - function* f () {} | |
6. Asyncronous generator function async function* f (){} | |
*/ | |
/* What the f? */ | |
/* | |
function* f(){ | |
yield 1; | |
yield 2; | |
yield 3; | |
} | |
console.log( f() ); | |
let caller = f(); | |
console.log(caller.next()); | |
console.log(caller.next()); | |
console.log(caller.next()); | |
console.log(caller.next()); | |
*/ | |
/*Generator can be used for data generation*/ | |
/* | |
function* serial(){ | |
let n=0; | |
while(true){ | |
yield n++; | |
} | |
} | |
let s=serial(); | |
let t=serial(); | |
console.log(s.next()); | |
console.log(s.next()); | |
console.log(s.next()); | |
console.log(t.next()); //A reference to a generator function is also an instance of that generator function | |
*/ | |
/*Generators can be iterated using regular higher order array functions*/ | |
/* | |
function* finite_serial(){ | |
for(let i=0; i<5; i++){ | |
yield i; | |
} | |
} | |
let serials = [...finite_serial()]; | |
console.log(serials); | |
*/ | |
/*It is a pull based mechanism. Also known as imperative event based.*/ | |
/*This means the caller calls the tune of when the next value is called*/ | |
/*This is useful for streaming for example*/ | |
/* | |
function sleep(milliseconds) { | |
const date = Date.now(); | |
let currentDate = null; | |
do { | |
currentDate = Date.now(); | |
} while (currentDate - date < milliseconds); | |
} | |
let q = [ 'apple', 'banana', 'pear']; | |
function* feeder(){ | |
while(q.length===0){ | |
q.push('french fry'); | |
console.log("Fed a french fry to the hippo"); | |
} | |
sleep(2000); | |
yield* eater(); | |
} | |
function* eater(){ | |
while(q.length>0){ | |
console.log(`Yum I just ate a ${q.pop()} `); | |
} | |
sleep(2000); | |
yield* feeder(); | |
} | |
let hungryhippo = eater(); | |
hungryhippo.next(); | |
*/ | |
/*Generators can be asynchronous. That means the iterator is a promise*/ | |
/* | |
async function* awaitme(){ | |
yield "Hello"; | |
yield "World"; | |
} | |
let f = awaitme(); | |
f.next() | |
.then(data => console.log(data)) //Prints Hello | |
f.next() | |
.then(data => console.log(data)) //Prints World | |
let p = awaitme(); | |
console.log(p.next()); //Prints Promise | |
*/ | |
/*Recreate python's range function!*/ | |
/* | |
x = range(6) | |
for n in x: | |
print(n) | |
*/ | |
/* | |
const range = (end, start = 0, step = 1) => { | |
function* generateRange() { | |
let x = start - step; | |
while(x < end - step) yield x += step; | |
} | |
return { | |
[Symbol.iterator]: generateRange | |
}; | |
} | |
console.log([...range(50)].map((item)=>{ | |
item2 = {}; | |
item2.firstName = "Fred", | |
item2.lastName = "Bloggs" | |
return item2; | |
})); | |
console.log([...range(7)]); | |
console.log("*********"); | |
for (let i of range(6)) console.log(i); | |
console.log("*********"); | |
[...range(7)].forEach(r=>console.log(r)); | |
console.log("*********"); | |
*/ | |
/*A practical use case for an async generator*/ | |
/*A file downloader*/ | |
/* | |
function task(milliseconds) { | |
const date = Date.now(); | |
let currentDate = null; | |
do { | |
currentDate = Date.now(); | |
} while (currentDate - date < milliseconds); | |
} | |
function longtask(){ | |
return task(5000); | |
} | |
async function* download_documents(total) { | |
for(let i=1; i<=total; i++){ | |
longtask(); | |
yield { processed: i, total }; | |
} | |
} | |
(async () => { | |
for await (const val of download_documents(5)) { | |
// Prints "1 / 5", "2 / 5", "3 / 5", etc. | |
console.log(`${val.processed} / ${val.total}`); | |
} | |
})(); | |
console.log("Other stuff is happening while the docs are 'downloading'"); | |
*/ | |
//***NB The await keyword is abstracting the 'then' callback*** | |
/* | |
Best ever description of abstraction, Richard Feyman | |
In a computer's arithmetic logic unit, it can do simple binary addition | |
So that two binary numbers can be added like so | |
1101 } Summand | |
0011 } Summand | |
------ | |
10000 } Total | |
The function that does this has some basic rules | |
For each digit pair to be summed we have the following rule table | |
A B Carry Total | |
0 0 0 0 | |
1 0 0 1 | |
0 1 0 1 | |
1 1 1 0 | |
Then we get onto the problem of multiplication | |
1101 * 10 | |
Is just multiple additions! | |
So our multiply function could look like : | |
function multiply(x,y){ | |
let total = 0; | |
for (i=0;i<y;i++){ | |
total=add(total,add(x,x)); | |
} | |
return total; | |
} | |
The complexity of the multiple addition operations is encapsulated by the closures of the multiply function. | |
This is now in and of itself an abstraction | |
*/ | |
/*So our generator functions sit and wait until we call the next method on the returned iterator*/ | |
/*This is a pull*/ | |
/*How about a push mechanism?*/ | |
/*<Observables enter stage left>.....*/ | |
/*First we need a source of emitting (read pushing) data*/ | |
/* | |
class Beacon { | |
constructor() { | |
let i = 0; | |
this._id = setInterval(() => this.emit(i++), 200); | |
} | |
emit(n) { | |
const limit = 10; | |
if (this.ondata) { | |
this.ondata("Ping"); | |
} | |
if (n === limit) { | |
if (this.oncomplete) { | |
this.oncomplete(); | |
} | |
this.destroy(); | |
} | |
} | |
destroy() { | |
clearInterval(this._id); | |
} | |
} | |
//Kind of forget about this for now and focus on the next bit | |
function Observable(subscriber) { | |
let beacon = new Beacon(); | |
beacon.ondata = (e) => subscriber.next(e); | |
beacon.onerror = (err) => subscriber.error(err); | |
beacon.oncomplete = () => subscriber.complete(); | |
return () => { | |
beacon.destroy(); | |
}; | |
} | |
//This is the observable. It's a relatively simple contract that you subscribe to and then wait on the next, error and complete hooks | |
const unsubscribe = Observable({ | |
next(ping) { console.log(ping); }, | |
error(err) { console.error(err); }, | |
complete() { console.log('done')} | |
}); | |
*/ | |
/* | |
Is it like stream? | |
Is it like a Promise? | |
Is it multicast or unicast? | |
Tune in next week? ;-) | |
*/ | |
/*A quick encore before I go */ | |
/* | |
A Promise and an Observable are both async primitives | |
A Promise is always multicast | |
A Promise always resolves or rejects asynchronously | |
A Promise cannot be cancelled | |
The subscribe method of an Observable is similar to the "then" of a Promise | |
An Observable is 'usually' asynchronous | |
An Observable is 'usually' unicast | |
*/ | |
/*Back to first principles for a second*/ | |
/*The inversion of control pattern (don't even think about mentioning Dependency Injection!!!)*/ | |
/* | |
class puppet{ | |
move_left_leg(){ | |
console.log("Moving left leg"); | |
} | |
move_right_leg(){ | |
console.log("Moving right leg"); | |
} | |
} | |
class puppeteer{ | |
constructor(puppet){ | |
this.puppet = puppet; | |
} | |
dance(){ | |
this.puppet.move_left_leg(); | |
this.puppet.move_right_leg(); | |
this.puppet.move_right_leg(); | |
this.puppet.move_left_leg(); | |
} | |
} | |
let ppr = new puppeteer(new puppet); | |
ppr.dance(); | |
*/ | |
//The important point here is that the puppeteer controls the flow, not the puppet. The control is "handed over" to the puppeteer. | |
//We say the control flow has been inverted. Hence "Inversion of Control" or IoC. | |
//Now you can subsitute a different type of puppet in your line that instantiates the puppeteer, and that is called Dependency Injection, | |
//which IS depencency injection | |
//Real world example of IoC | |
/* | |
class TextBox{ | |
constructor(visible, mandatory, readonly){ | |
this.visible = visible; | |
this.mandatory = mandatory; | |
this.readonly = readonly; | |
} | |
render(){ | |
console.log(`<input type='text' class='${this.visible ? 'show': 'hide'}' disabled='${this.readonly}' required='${this.mandatory}' />`); | |
} | |
} | |
class CheckBox{ | |
constructor(visible, mandatory, readonly){ | |
this.visible = visible; | |
this.mandatory = mandatory; | |
this.readonly = readonly; | |
} | |
render(){ | |
console.log(`<input type='check' class='${this.visible ? 'show': 'hide'}' disabled='${this.readonly}' required='${this.mandatory}' />`); | |
} | |
} | |
class Form{ | |
static render(field){ | |
field.render(); | |
} | |
} | |
Form.render(new TextBox(true,true,true)); | |
Form.render(new CheckBox(true,true,true)); | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment