-
-
Save nobitagit/70b25693b2311315d8236338d1bfdade to your computer and use it in GitHub Desktop.
Introduction to JavaScript Generators
This file contains hidden or 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
// Intro to generators in JavaScript | |
// You can use: https://repl.it/languages/babel | |
// to follow along the code | |
// A generic counter | |
function counter() { | |
console.log('counter #1'); | |
console.log('1'); | |
console.log('2'); | |
} | |
counter() | |
// logs: | |
// 1 | |
// 2 | |
// A counter as a generator | |
function* counter2() { | |
console.log('counter #2'); | |
console.log('1'); | |
console.log('2'); | |
} | |
counter2(); | |
// logs nothing | |
// Reason: a generator function returns an INSTANCE of itself when it is invoked | |
// Hence we should ideally call this function createCounter, | |
// as when invoked it just instantiates itself without actually executing anything. | |
function* createCounter() { | |
console.log('counter #3'); | |
console.log('1'); | |
console.log('2'); | |
} | |
const counter3 = createCounter(); | |
counter3.next(); | |
// logs: | |
// 1 | |
// 2 | |
// If we want to pause a generator we use the keyword `yield` | |
function* createCounterWithYeld() { | |
console.log('counter #4'); | |
console.log('1'); | |
yield; | |
console.log('2'); | |
} | |
const counter4 = createCounterWithYeld(); | |
counter4.next(); | |
// logs: | |
// 1 | |
console.log('invoke next on counter #4'); | |
counter4.next(); | |
// logs: | |
// 2 |
This file contains hidden or 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
// We can return values from generators | |
function* createCounter() { | |
yield 'Instantiated'; | |
} | |
const counter = createCounter(); | |
console.log('1nd call to counter.next()'); | |
console.log(counter.next()); | |
// logs: | |
// { value: 'Instantiated', done: false } | |
// Done is false because we haven't gone past the `yield` | |
// Executing `next` again will mark the function as `done` | |
console.log(); | |
console.log('2nd call to counter.next()'); | |
console.log(counter.next()); | |
console.log(); | |
// logs: | |
// { value: 'Instantiated', done: false } | |
// Note that "value": undefined this time | |
// We can supply values to the function and have them assigned to variables | |
console.log('createCounterWithValues'); | |
function* createCounterWithValues() { | |
const start = yield; | |
console.log(start); | |
} | |
const counter2 = createCounterWithValues(); | |
console.log(counter2.next()); | |
console.log(counter2.next('5')); | |
// logs: | |
// { value: undefined, done: false } | |
// 5 | |
// { value: undefined, done: true } | |
This file contains hidden or 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
// While the code in a generator looks synchronous it is indeed | |
// fully async thanks to `yield` and `next`. | |
function* createUser() { | |
const uId = yield; | |
console.log(`User Id: ${uId}`); | |
const companyDetails = yield; | |
console.log(`Company: ${companyDetails.name}`); | |
} | |
const user = createUser(); | |
user.next(); | |
console.log(user.next('abcd-efg-12-34-45')); | |
// Request company details from a server, once it is done | |
// pass it to the generator and resume execution | |
console.log(user.next({name: 'Facebook', nation: 'USA'})); | |
// Lets' suppose, though that we execute the first part of the generator, then we do some | |
// other task and we want to stop the generator a throw an error | |
const user2 = createUser(); | |
user2.next(); | |
console.log(user.next('hilm-efg-34-45-88')); | |
// Request company details returns a 4xx error, so we want to actually throw an error. | |
// We use the `throw` method | |
// This way though will hang the app or crash it badly as we see if we | |
// uncomment the below line to test this function | |
// user2.throw(); | |
console.log('throw call commented here, uncomment to test it'); | |
console.log(); | |
// How to solve this then? | |
// Since generators' code looks synchronous we can treat it like a normal sync call | |
// and use a standard try/catch block | |
function* createUserWithTryCatch() { | |
const uId = yield; | |
console.log(`User Id: ${uId}`); | |
try { | |
const companyDetails = yield; | |
console.log(`Company: ${companyDetails.name}`); | |
} catch(e) { | |
console.log('An error was caught!!'); | |
} | |
} | |
const user3 = createUserWithTryCatch(); | |
user3.next(); | |
user.next('hilm-efg-34-45-88'); | |
user3.next(); | |
user3.throw(); | |
console.log('The app does not hang or break, hence this code runs'); | |
// If we want to pass an error to the function we just pass it as a paramenter | |
// to the throw function, just like: user3.throw('Something has gone wrong'); | |
// and the error will be available inside the catch block | |
This file contains hidden or 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
function* daysOfTheWeek() { | |
yield 'Monday'; | |
yield 'Tuesday'; | |
yield 'Wednesday'; | |
yield 'Thursday'; | |
yield 'Friday'; | |
yield 'Saturday'; | |
yield 'Sunday'; | |
} | |
const day = daysOfTheWeek(); | |
console.log(day.next()); | |
console.log(day.next()); | |
console.log(day.next()); | |
console.log(day.next()); | |
console.log(day.next()); | |
console.log(day.next()); | |
console.log(day.next()); | |
console.log(day.next()); | |
// logs: | |
// { value: 'Monday', done: false } | |
// { value: 'Tuesday', done: false } | |
// { value: 'Wednesday', done: false } | |
// { value: 'Thursday', done: false } | |
// { value: 'Friday', done: false } | |
// { value: 'Saturday', done: false } | |
// { value: 'Sunday', done: false } | |
// { value: undefined, done: true } | |
// A cleaner way to go through all of the yields in one go | |
// is to use a for...of loop like so: | |
const day2 = daysOfTheWeek(); | |
for (let dayName of day2) { | |
console.log(dayName); | |
} | |
// logs: | |
// Monday | |
// Tuesday | |
// Wednesday | |
// Thursday | |
// Friday | |
// Saturday | |
// Sunday | |
// Note how the for...of executes the next function and returns the value | |
// of the function call. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment