Generator functions allow you to define an iterative algorithm by writing a single function whose execution is not continuous. Applications: - We might want to pause the current iteration to make sure we have enough resources to deal with the next iteration. - The typewriter effect - Ability to iterate over a collection where its length is not known ahead of time. (prevent memory and CPU abuse) Generators Function w/ ability to pause itself > run other code > resume later yield = PAUSE command It will pause on every yield and will resume until there are not more yields. Useful for: 2-way communication while a generator is running; long-lived while loops which do not freeze your program; Generator function with yields can be use to: - control the state of an application - Allow us to have a full SYNC control over the flow of a process, running in the middle multiple async and sync operations, just before we return the final outputs
1console.log('generator running');
2
3
4function *calculator(input) {
5 const doubleThis = 2 * (yield(input/2)); // pause: wait for input/2 = 5
6 const another = yield(doubleThis); // pause: wait for 10
7 return (input*doubleThis*another) // resume: 10*10*10
8}
9
10const calc = calculator(10);
11// console.log('ready', calc.value);
12
13// Start iterator
14const it1 = calc.next();
15// console.log('iterator1:', JSON.stringify(it1));
16
17const it2 = calc.next(7); // use value 7 instead the previous yield value
18// console.log('iterator2:', JSON.stringify(it2)); // 2*7=14
19
20const it3 = calc.next(100); // use value1 100 instead the previous yield value
21// console.log('iterator3:', JSON.stringify(it3)); // 14*100=14K
22
23// ============================================
24function *plainSimpleGenerator (input) {
25 const passedInput = yield input;
26 const doSomeSyncComputation = yield syncComputation(passedInput);
27 const doSomeAsyncComputation = yield syncComputation(doSomeSyncComputation);
28 return doSomeSyncComputation+doSomeAsyncComputation;
29}
30
31const asyncComputation = () => {
32 return Promise.resolve(1);
33 return 1;
34}
35
36const syncComputation = (passedInput) => {
37 return 56 + passedInput;
38}
39
40const simpleGen = plainSimpleGenerator(36);
41const it1s = simpleGen.next();
42console.log('it1s:', JSON.stringify(it1s)); // 36
43
44const it2s = simpleGen.next(it1s.value);
45console.log('it2s:', JSON.stringify(it2s)); // 36+56 = 92
46
47// other code
48const x = syncComputation(34);
49const y = syncComputation(34);
50const z = syncComputation(34);
51
52const it3s = simpleGen.next(x+y+z);
53console.log('it3s:', JSON.stringify(it3s)); // 1
54
55const it4s = simpleGen.next(it3s.value);
56console.log('it4s:', JSON.stringify(it4s)); // 36+1
57
58// ============================================
59console.log('generate a sequence')
60function* generateSequence(start, end) {
61 for (let i = start; i <= end; i++) {
62 yield i;
63 }
64}
65
66for(let value of generateSequence(1, 5)) {
67 console.log('generateSequence', value); // 1, then 2, then 3, then 4, then 5
68}
69
70const itsx1 = generateSequence(1, 5).next();
71console.log('itsx1', itsx1)
72const itsx2 = generateSequence(itsx1.value+1, 5).next();
73console.log('itsx2', itsx2)
74const itsx3 = generateSequence(itsx2.value+1, 5).next();
75console.log('itsx3', itsx3)
76const itsx4 = generateSequence(itsx3.value+1, 5).next();
77console.log('itsx4', itsx3)
78
79
80// ============================================
81let range = {
82 from: 1,
83 to: 5,
84 *[Symbol.iterator]() { // a shorthand for [Symbol.iterator]: function*()
85 for(let value = this.from; value <= this.to; value++) {
86 yield value;
87 }
88 }
89}
90for(let value of range) {
91 console.log('value', value); // 1, then 2, then 3, then 4, then 5
92}
93
94// ============================================
95// asynchronous generators
96async function* generateSequenceAsync(start, end) {
97 for (let i = start; i <= end; i++) {
98 const x = await new Promise(resolve => setTimeout(resolve(i), 2000));
99 yield x;
100 }
101}
102
103(async () => {
104 let generator = generateSequence(1, 5);
105 for await (let value of generator) {
106 console.log('async generator val', value); // 1, then 2, then 3, then 4, then 5 (with delay between)
107 }
108})();
109
110(async () => {
111 const asyncIt1 = await generateSequence(1, 5).next();
112 console.log('asyncIt1', asyncIt1)
113 const asyncIt2 = await generateSequence(asyncIt1.value+1, 5).next();
114 console.log('asyncIt2', asyncIt2)
115})();
116
117// ============================================
118// Pagination Function
119const fetch = async (url) => {
120 return {
121 body: [1,2,3,4,5],
122 nextPage: null
123 }
124}
125async function* fetchCommits(repo) {
126 let url = `https://api.github.com/repos/${repo}/commits`;
127 while (url) {
128 const response = await fetch(url);
129 nextPage = response.nextPage
130 url = nextPage;
131 for(let commit of response.body) {
132 yield commit;
133 }
134 }
135}
136
137(async () => {
138 for await (let commit of fetchCommits("angular/angular")) {
139 console.log('processing commit', commit)
140 }
141})();
142
143(async () => {
144 let count = 0;
145 for await (const commit of fetchCommits('javascript-tutorial/en.javascript.info')) {
146 console.log('commit', commit);
147 if (++count == 100) { // let's stop at 100 commits
148 break;
149 }
150 }
151})();
152
153# Pagination Example
154 // Controlled Pagination Generator
155 async function* getAllPages() {
156 let pageNumber = 1;
157 while (pageNumber) {
158 const docs = await getPage(pageNumber);
159 if (paginatedDocs.length === 0) {
160 paginatedDocs = [...paginatedDocs, ...docs];
161 } else {
162 docs.forEach((doc: any, i: number) => {
163 const { count } = doc;
164 if (groupBy === 'status') {
165 const paginatedDocsItemIndex = paginatedDocs.findIndex((paginatedDocsItem: any) => {
166 return paginatedDocsItem[groupBy] === doc[groupBy];
167 });
168 if (paginatedDocsItemIndex > -1) {
169 paginatedDocs[paginatedDocsItemIndex].count += count;
170 } else {
171 paginatedDocs.push(doc);
172 }
173 }
174 }
175 pageNumber = docs.length >= 1 ? pageNumber + 1 : null;
176 yield docs;
177 }
178 }
179 for await (const page of getAllPages()) {
180 if (!page) {
181 break;
182 }
183 }
184Created on 11/29/2021