Sync/Async Generator Functions

JS
S
JavaScript

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    }
184

Created on 11/29/2021