├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── package.json └── test ├── index.html └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Simon Y. Blackwell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # generx v0.0.9 2 | 3 | JavaScript generators extended with forEach, map, reduce ... most standard Array methods, plus the abiliyt to reset and re-use the generator. 4 | 5 | For situations where less than the entire generator yield collection is required, generx can (but won't always) produce results faster than first converting the generator results into an array while 6 | also allowing the developer to use the expressive nature of functional oriented array methods in place of `.next()` or `for(let item of )` code. 7 | 8 | See the Medium article: [Next Generation JavaScript Generators](https://medium.com/me/stats/post/df08312fa62d). 9 | 10 | # Installation 11 | 12 | `npm install generx` 13 | 14 | The current BETA code is not currently transpiled and is only fully tested and working in the most recent release of Chrome and Firefox. 15 | 16 | 17 | # Usage 18 | 19 | Just re-assign your generator functions to the Proxy returned by `generx()` and then use their instantiated values like arrays, e.g. 20 | 21 | ``` 22 | function* example1() { 23 | for(const item of [1,2,3,4,5]) { 24 | yield item; 25 | } 26 | } 27 | example1 = generx(example1); 28 | const result = example1().reduce((accum,item) => accum += item); // result = 15 29 | 30 | async function* example2() { 31 | for(const item of [1,2,3,4,5]) { 32 | yield item; 33 | } 34 | } 35 | example2 = generx(example2); 36 | const await result = example2().reduce((accum,item) => accum += item); // result = 15 37 | ``` 38 | 39 | You can even pass in an asynchronous method to `reduce`, `forEach` and other functions accepting functions as arguments: 40 | 41 | ``` 42 | async function* example3() { 43 | for(const item of [1,2,3,4,5]) { 44 | yield item; 45 | } 46 | } 47 | example3 = generx(example3); 48 | const await result = example3().reduce(async (accum,item) => accum += item); // result = 15 49 | 50 | ``` 51 | 52 | # API 53 | 54 | ## Standard Array Methods 55 | 56 | The below methods are currently supported and behave the same way as their array counterparts. Those marked with a + resolve 57 | only those elements required to satisfy their semantics and will keep memory, network, and CPU utilization down: 58 | 59 | ``` 60 | +every, 61 | +fill, 62 | +find, 63 | +findIndex, 64 | forEach, 65 | +includes, 66 | +indexOf, 67 | join, 68 | lastIndexOf, 69 | map, 70 | pop, 71 | push, 72 | reduce, 73 | reverse, 74 | +shift, 75 | +slice, 76 | sort, 77 | +some, 78 | +unshift 79 | ``` 80 | 81 | The standard array looping approach `for(let i=0;i < generator.length;i++) { ... }` will also work and you can break early. 82 | 83 | If you are using an asynchronous generator, then use `for(let i=0; i < generator.length;i++) { const value = await generator[i]; ... }` 84 | 85 | ## Additional Properties and Methods 86 | 87 | There is an additional read-only property `realized` which contains an array copy of the realized values. Accessing it causes a slice operation. If you just need the 88 | length use `count()` below. 89 | 90 | There is an additional method `count()` which will return the current number of realized values. 91 | 92 | There is an additional method `realize()` which will force resolution of the entire generator yield collection. 93 | 94 | ## .length vs .count() 95 | 96 | `.count()` return the minimum of the number of values yielded and the `.length`. The count may be less than the number of values yielded if `.pop()` or `.shift()` 97 | have been called. 98 | 99 | The property `length` works almost just like that with an Array. Setting it will limit the number of values yielded to the length provided. 100 | However, it starts out with the value `Infinity` since it is theoretically possible for a generator to yield forever. It remains at `Infinity` until it 101 | becomes set to the current `count()` when the generator has no more values to yield, i.e. `.next()` returns a value of `{done:true,value:}`. 102 | 103 | ## reset() 104 | 105 | Calling reset() will allow a generator to be re-used. 106 | 107 | ## Array Accessor Notation 108 | 109 | `generx'd` generators can also be accessed using array notation, e.g. 110 | 111 | ``` 112 | function* example4() { 113 | for(const item of [1,2,3,4,5]) { 114 | yield item; 115 | } 116 | } 117 | example4 = generx(example4); 118 | const result = example4()[2]; // result = 3 119 | ``` 120 | 121 | For `async` generators, the array values should be awaited to force Promise resolution. Until the Promise resolves, the value at an index will be a Promise. 122 | 123 | ``` 124 | async function* example5() { 125 | for(const item of [1,2,3,4,5]) { 126 | yield item; 127 | } 128 | } 129 | const example5 = generx(example5), 130 | values = example5(), 131 | promise = values[0], // promise instanceof Promise 132 | result = await values[1]; // result = 2 133 | ``` 134 | 135 | ### Setting Values 136 | 137 | It is possible to set values at any index: 138 | 139 | ``` 140 | function* example6() { 141 | for(const item of [1,2,3,4,5]) { 142 | yield item; 143 | } 144 | } 145 | const example6 = generx(example6), 146 | values = example6(); 147 | values[1] = 0; 148 | const result = values[1]; // result = 0 149 | ``` 150 | 151 | If the array index in greater than the current `.count()` and the generator is asynchronous, the intermediate values will be forced to start resolution. 152 | 153 | If the array index is beyond the total yield count, the length will be extended and the values at intermediate indexes will be undefined. 154 | 155 | ### Deleting Values 156 | 157 | Deleting values at indexes works just like an array. Deleting a value at an index beyond the current `.count()` has no effect. Deleting a value at an index below the current 158 | `.count()` will set the value at the index to `undefined`. 159 | 160 | # Release History (reverse chronological order) 161 | 162 | 2021-06-22 v0.0.9 163 | 164 | - @outis Iterating over a generx generator and accessing elements as an array don't play well together. 165 | Before that can be looked into, however, I've reorganized some of the code in the resettable generator 166 | proxy. The main changes are about encapsulation (the state of the resettable generator is moved from 167 | local variables to a state object) and abstraction to reduce repetition (2 duplicate code blocks from 168 | the proxy's array access handler have been refactored into a function). 169 | - @anywhichway Reformatted release history. 170 | 171 | 2021-06-18 v0.0.8 172 | 173 | - @outis Simplified proxied next() of resettable generator (and added value parameter, as per the spec). 174 | - @outis Fix for issue reset() doesn't restore array access after iterating a generator & accessing it as an array. #1. 175 | - @outis Made next() of resettable generator non-enumerable. 176 | 177 | 2018-12-31 v0.0.7 178 | 179 | - Added `reset()`. 180 | 181 | 2018-11-10 v0.0.6 182 | 183 | - Enhanced documentation. 184 | 185 | 2018-11-10 v0.0.5 186 | 187 | - Renamed `.finalize()` to `.realize()`. Changed return value to the array of all values. 188 | - Added `.fill(value,start,end)`, `.join(separator)`, `.pop()`, `.push(value)`, `.realized`. 189 | 190 | 2018-11-09 v0.0.4 191 | 192 | - Added unit tests for `count()`. Added support for delete and set on array indexes. 193 | - Enhanced documentation. 194 | 195 | 2018-11-08 v0.0.3 196 | 197 | - Added `count()`. 198 | - Enhanced documentation. 199 | - Improved async Promise resolution 200 | 201 | 2018-11-07 v0.0.2 202 | 203 | - Fixed reverse. It was throwing an error due to undefined function. 204 | - Modified `map` and `slice` to return a `generx` enhanced generator rather than array. This will 205 | - produce results faster for mapped functions and for slices less than the full generator yield results. 206 | 207 | 2018-11-06 v0.0.1 Initial public release 208 | 209 | # License 210 | 211 | MIT License 212 | 213 | Copyright (c) 2018 Simon Y. Blackwell, AnyWhichWay, LLC 214 | 215 | Permission is hereby granted, free of charge, to any person obtaining a copy 216 | of this software and associated documentation files (the Software), to deal 217 | in the Software without restriction, including without limitation the rights 218 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 219 | copies of the Software, and to permit persons to whom the Software is 220 | furnished to do so, subject to the following conditions: 221 | 222 | The above copyright notice and this permission notice shall be included in all 223 | copies or substantial portions of the Software. 224 | 225 | THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 226 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 227 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 228 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 229 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 230 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 231 | SOFTWARE. 232 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | export {generx as default}; 2 | 3 | const ASYNCPROTO = Object.getPrototypeOf(async function(){}), 4 | ASYNCGENERATORPROTO = Object.getPrototypeOf(async function*() {}()); 5 | 6 | export function generx(f,recursed) { 7 | const generator = typeof(f)==="function" ? f("") : f, 8 | proto = Object.getPrototypeOf(generator), 9 | isasync = Object.getPrototypeOf(generator).constructor===ASYNCGENERATORPROTO.constructor; 10 | if(isasync) { 11 | proto.every = async function(f) { 12 | for(let i=0;await this.proxy[i] && i0) { 121 | yield result.pop(); 122 | } 123 | })(); 124 | } 125 | proto.shift = async function() { 126 | const result = await this.proxy[0], 127 | count = await this.count(); 128 | for(let i=0;i0) { 286 | yield result.pop(); 287 | } 288 | })(); 289 | } 290 | proto.slice = function*(start=0,end=Infinity) { 291 | for(let i=start,j=0;i state.next(value), 368 | }); 369 | // define reset to use the original arguments to create 370 | // a new generator and assign it to the generator variable 371 | Object.defineProperty(state.generator,"reset",{ 372 | enumerable:false, 373 | value: () => { 374 | state = new ResettableState(...arguments); 375 | return state.generator; 376 | }, 377 | }); 378 | const proxy = new Proxy(state.generator,{ 379 | deleteProperty(target,property) { 380 | delete target[property]; 381 | delete state.realized[property]; 382 | return true; 383 | }, 384 | get(target,property) { 385 | if(typeof(property)==="symbol") { 386 | return target[property]; 387 | } 388 | const i = parseInt(property); 389 | if(i>=0) { 390 | if(i=state.count) { 392 | state.count = i+1; 393 | } 394 | return state.realized[i]; 395 | } 396 | let next = state.next(); 397 | // Note: The apparent duplicate code below ensures generator looks ahead to see if it is done 398 | while(state.length===Infinity && !next.done) { 399 | let value = isasync ? next : next.value; 400 | if(isasync) { 401 | value = realizeLater(target, state, next); 402 | } 403 | // save the value to result array, might be a promise 404 | target[state.realized.length] = state.realized[state.realized.length] = value; 405 | if(i=state.count) { 407 | state.count = i+1; 408 | } 409 | return state.realized[i]; 410 | } 411 | // peek ahead so length gets set properly 412 | next = state.next(); 413 | value = isasync ? next : next.value; 414 | if(isasync) { 415 | value = realizeLater(target, state, next); 416 | } else { 417 | if(next.done) { 418 | if(value!==undefined) { 419 | target[state.realized.length] = state.realized[state.realized.length] = value; 420 | } 421 | state.length = state.realized.length; 422 | } else { 423 | // do not try to optimize by moving this up, results can legitimately contain undefined 424 | target[state.realized.length] = state.realized[state.realized.length] = value; 425 | next = state.next(); 426 | } 427 | } 428 | } 429 | state.length = state.realized.length; // change length from Infinity to actual length 430 | if(i>=state.count) { 431 | state.count = i+1; 432 | } 433 | return state.realized[i]; 434 | } 435 | return target[property]; 436 | }, 437 | ownKeys(target) { 438 | target.realize(); 439 | return Object.keys(target).concat("count","length","next","proxy","realized","reset"); 440 | }, 441 | set(target,property,value) { 442 | const i = parseInt(property); 443 | if(i>=0) { 444 | // force resolution of values before the set point 445 | for(let j=state.realized.length;j=state.count) { 450 | state.count = i + 1; 451 | } 452 | if(i>=state.length) { 453 | state.length = i + 1; 454 | } 455 | } 456 | target[property] = value; 457 | return true; 458 | }, 459 | }); 460 | Object.defineProperty(state.generator,"count",{value:() => state.count}); 461 | Object.defineProperty(state.generator,"length",{ 462 | get() { 463 | return state.length; 464 | }, 465 | set(value) { 466 | if(value 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 19 | 20 | 21 | 22 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | import generx from "../index.js"; 2 | 3 | const testarray = [1,2,3,4,5,1]; 4 | 5 | function* synchronous() { 6 | for(const item of testarray) { 7 | yield item; 8 | } 9 | } 10 | synchronous = generx(synchronous); 11 | 12 | async function* asynchronous() { 13 | for(const item of testarray) { 14 | yield item; 15 | } 16 | } 17 | asynchronous = generx(asynchronous); 18 | 19 | describe("sync",function() { 20 | it("length",function(done) { 21 | chai.expect(synchronous().length).equal(Infinity); 22 | done(); 23 | }); 24 | it("final length",function(done) { 25 | chai.expect(synchronous().realize().length).equal(testarray.length); 26 | done(); 27 | }); 28 | it("array access",function(done) { 29 | chai.expect(synchronous()[0]).equal(testarray[0]); 30 | done(); 31 | }); 32 | it("count",function(done) { 33 | const generator = synchronous(); 34 | generator[0]; 35 | chai.expect(generator.count()).equal(1); 36 | done(); 37 | }); 38 | it("set",function(done) { 39 | const generator = synchronous(); 40 | generator[2] = 3; 41 | chai.expect(generator.count()).equal(3); 42 | chai.expect(generator.length).equal(Infinity); 43 | chai.expect(generator[2]).equal(3); 44 | done(); 45 | }); 46 | it("set past generator limit",function(done) { 47 | const generator = synchronous(); 48 | generator[10] = 3; 49 | chai.expect(generator.count()).equal(11); 50 | chai.expect(generator.length).equal(11); 51 | chai.expect(generator[10]).equal(3); 52 | done(); 53 | }); 54 | it("delete",function(done) { 55 | const generator = synchronous(); 56 | delete generator[0]; 57 | chai.expect(generator.count()).equal(0); 58 | done(); 59 | }); 60 | it("delete existing",function(done) { 61 | const generator = synchronous(); 62 | generator[0]; 63 | delete generator[0]; 64 | chai.expect(generator.count()).equal(1); 65 | done(); 66 | }); 67 | it("every",function(done) { 68 | chai.expect(synchronous().every((item,i) => item===testarray[i])).equal(true); 69 | done(); 70 | }); 71 | it("fill",function(done) { 72 | const generator = synchronous().fill(0,0,7); 73 | chai.expect(generator.length).equal(7); 74 | for(let i=0;i<7;i++) { 75 | chai.expect(generator[i]).equal(0); 76 | } 77 | done(); 78 | }); 79 | it("fill no end",function(done) { 80 | const generator = synchronous().fill(0,0); 81 | for(let i=0;i item===1)).equal(testarray.find(item => item===1)); 88 | done(); 89 | }); 90 | it("findIndex",function(done) { 91 | chai.expect(synchronous().findIndex((item,i) => item===1 ? true : false)).equal(testarray.findIndex((item,i) => item===1 ? true : false)); 92 | done(); 93 | }); 94 | it("forEach",function(done) { 95 | let sum1 = 0, sum2 = 0; 96 | testarray.forEach(i => sum1+=i); 97 | synchronous().forEach(i => sum2+=i); 98 | chai.expect(sum1).equal(sum2); 99 | done(); 100 | }); 101 | it("includes",function(done) { 102 | chai.expect(synchronous().includes(1)).equal(testarray.includes(1)); 103 | done(); 104 | }); 105 | it("indexOf",function(done) { 106 | chai.expect(synchronous().indexOf(1)).equal(0); 107 | done(); 108 | }); 109 | it("join",function(done) { 110 | chai.expect(synchronous().join(";")).equal(testarray.join(";")); 111 | done(); 112 | }); 113 | it("keys",function(done) { 114 | chai.expect(Object.keys(synchronous())).eql(Object.keys(testarray)); 115 | done(); 116 | }); 117 | it("lastIndexOf",function(done) { 118 | chai.expect(synchronous().lastIndexOf(1)).equal(testarray.lastIndexOf(1)); 119 | done(); 120 | }); 121 | it("map",function(done) { 122 | const a1 = testarray.map(i => i), 123 | a2 = synchronous().map(i => i); 124 | chai.expect(a1.every((item,i) => item===a2[i])).equal(true); 125 | done(); 126 | }); 127 | it("pop",function(done) { 128 | const generator = synchronous(); 129 | chai.expect(generator.pop()).equal(testarray[testarray.length-1]); 130 | chai.expect(generator.length).equal(testarray.length-1); 131 | chai.expect(generator[testarray.length-1]).equal(undefined); 132 | done(); 133 | }); 134 | it("push",function(done) { 135 | const generator = synchronous(); 136 | chai.expect(generator.push(6)).equal(testarray.length+1); 137 | chai.expect(generator[testarray.length]).equal(6); 138 | done(); 139 | }); 140 | it("reduce",function(done) { 141 | const r1 = testarray.reduce((accum,i) => i,0), 142 | r2 = synchronous().reduce((accum,i) => i,0); 143 | chai.expect(r1).equal(r2); 144 | done(); 145 | }); 146 | it("reset",function(done) { 147 | const a1 = testarray, 148 | g2 = synchronous(); 149 | let a2 = [...g2]; 150 | g2.reset(); 151 | a2 = [...g2]; 152 | chai.expect(a1).eql(a2); 153 | done(); 154 | }); 155 | it("reset should restore array access after spread & array access",function(done) { 156 | const a1 = testarray, 157 | g1 = synchronous(); 158 | // realize the sequence, which consumes the values & sets length 159 | [...g1]; 160 | g1[0]; 161 | g1.reset(); 162 | chai.expect(a1[0]).equal(g1[0]); 163 | done(); 164 | }); 165 | it("reverse",function(done) { 166 | const a1 = testarray.reverse(), 167 | a2 = synchronous().reverse(); 168 | chai.expect(a1.every((item,i) => item===a2[i])).equal(true); 169 | done(); 170 | }); 171 | it("shift",function(done) { 172 | const a1 = testarray.slice(), 173 | a2 = synchronous(); 174 | chai.expect(a2.shift()).equal(a1.shift()); 175 | chai.expect(a2.count()).equal(0); 176 | done(); 177 | }); 178 | it("slice part",function(done) { 179 | const a1 = testarray.slice(1,2), 180 | a2 = synchronous().slice(1,2); 181 | chai.expect(a1.every((item,i) => item===a2[i])).equal(true); 182 | done(); 183 | }); 184 | it("slice full",function(done) { 185 | const a1 = testarray.slice(), 186 | a2 = synchronous().slice(); 187 | chai.expect(a2.every((item,i) => item===a1[i])).equal(true); 188 | done(); 189 | }); 190 | it("slice twice",function(done) { 191 | const a1 = testarray.slice(1,3), 192 | gen = synchronous(), 193 | a2 = gen.slice(0,1), 194 | a3 = gen.slice(1,3); 195 | chai.expect(a1.every((item,i) => item===a3[i])).equal(true); 196 | done(); 197 | }); 198 | it("unshift",function(done) { 199 | const generator = synchronous(); 200 | chai.expect(generator.unshift(6)).equal(1); 201 | chai.expect(generator[0]).equal(6); 202 | done(); 203 | }); 204 | }); 205 | 206 | describe("async",function() { 207 | it("length",async function() { 208 | chai.expect(await asynchronous().length).equal(Infinity); 209 | }); 210 | it("final length",async function() { 211 | const array = await asynchronous().realize(); 212 | chai.expect(array.length).equal(testarray.length); 213 | }); 214 | it("array access",async function() { 215 | const generator = asynchronous(); 216 | await generator[0]; 217 | chai.expect(generator[0]).equal(testarray[0]); 218 | }); 219 | it("count",async function() { 220 | const generator = asynchronous(); 221 | await generator[0]; 222 | chai.expect(generator.count()).equal(1); 223 | }); 224 | it("every",async function() { 225 | chai.expect(await asynchronous().every((item,i) => item===testarray[i])).equal(true); 226 | }); 227 | it("fill",async function() { 228 | const generator = await asynchronous().fill(0,0,7); 229 | chai.expect(generator.length).equal(7); 230 | for(let i=0;i<7;i++) { 231 | chai.expect(generator[i]).equal(0); 232 | } 233 | }); 234 | it("fill no end",async function() { 235 | const generator = await asynchronous().fill(0,0); 236 | for(let i=0;i item===1)).equal(testarray.find(item => item===1)); 242 | }); 243 | it("findIndex",async function() { 244 | chai.expect(await asynchronous().findIndex((item,i) => item===1 ? true : false)).equal(testarray.findIndex((item,i) => item===1 ? true : false)); 245 | }); 246 | it("forEach",async function() { 247 | let sum1 = 0, sum2 = 0; 248 | testarray.forEach(i => sum1+=i); 249 | await asynchronous().forEach(i => sum2+=i); 250 | chai.expect(sum1).equal(sum2); 251 | }); 252 | it("includes",async function() { 253 | chai.expect(await asynchronous().includes(1)).equal(testarray.includes(1)); 254 | }); 255 | it("indexOf",async function() { 256 | chai.expect(await asynchronous().indexOf(1)).equal(0); 257 | }); 258 | it("join",async function() { 259 | chai.expect(await asynchronous().join(";")).equal(testarray.join(";")); 260 | }); 261 | it("lastIndexOf",async function() { 262 | chai.expect(await asynchronous().lastIndexOf(1)).equal(testarray.lastIndexOf(1)); 263 | }); 264 | it("map",async function() { 265 | const a1 = testarray.map(i => i), 266 | a2 = await asynchronous().map(i => i); 267 | chai.expect(a1.every(async (item,i) => item===await a2[i])).equal(true); 268 | }); 269 | it("pop",async function() { 270 | const generator = asynchronous(); 271 | chai.expect(await generator.pop()).equal(testarray[testarray.length-1]); 272 | chai.expect(generator.length).equal(testarray.length-1); 273 | chai.expect(generator[testarray.length-1]).equal(undefined); 274 | }); 275 | it("push",async function() { 276 | const generator = asynchronous(); 277 | chai.expect(await generator.push(6)).equal(testarray.length+1); 278 | chai.expect(generator[testarray.length]).equal(6); 279 | }); 280 | it("reduce",async function() { 281 | const r1 = testarray.reduce((accum,i) => accum += i,0), 282 | r2 = await asynchronous().reduce((accum,i) => accum += i,0); 283 | chai.expect(r1).equal(r2); 284 | }); 285 | it("reset",async function() { 286 | const a1 = testarray, 287 | g2 = asynchronous(); 288 | let a2 = []; 289 | for await (let a of g2) {} 290 | g2.reset(); 291 | for await (let a of g2) { 292 | a2.push(a); 293 | } 294 | chai.expect(a1).eql(a2); 295 | }); 296 | it("reset should restore array access after iteration & array access",async function() { 297 | const a1 = testarray, 298 | g1 = asynchronous(); 299 | // go through the sequence 300 | for await (let a of g1) {} 301 | await g1[0]; 302 | await g1.reset(); 303 | chai.expect(a1[0]).equal(await g1[0]); 304 | }); 305 | it("reverse",async function() { 306 | const a1 = testarray.reverse(), 307 | a2 = await asynchronous().reverse(); 308 | chai.expect(a1.every(async (item,i) => item===await a2[i])).equal(true); 309 | }); 310 | it("shift",async function() { 311 | const a1 = testarray.slice(), 312 | a2 = synchronous(); 313 | chai.expect(await a2.shift()).equal(a1.shift()); 314 | chai.expect(a2.count()).equal(0); 315 | }); 316 | it("slice part",async function() { 317 | const a1 = testarray.slice(1,2), 318 | a2 = await asynchronous().slice(1,2); 319 | chai.expect(a1.every(async (item,i) => item===await a2[i])).equal(true); 320 | }); 321 | it("slice full",async function() { 322 | const a1 = testarray.slice(), 323 | a2 = await asynchronous().slice(); 324 | chai.expect(await a2.every(async (item,i) => item===a1[i])).equal(true); 325 | }); 326 | it("slice twice",async function() { 327 | const a1 = testarray.slice(1,3), 328 | gen = asynchronous(), 329 | a2 = await gen.slice(0,1), 330 | a3 = await gen.slice(1,3); 331 | chai.expect(a1.every(async (item,i) => item===await a3[i])).equal(true); 332 | }); 333 | it("unshift",async function() { 334 | const generator = asynchronous(); 335 | chai.expect(await generator.unshift(6)).equal(1); 336 | chai.expect(generator[0]).equal(6); 337 | }); 338 | }); 339 | 340 | describe("examples",function() { 341 | it("example1",function(done) { 342 | function* example1() { 343 | for(const item of [1,2,3,4,5]) { 344 | yield item; 345 | } 346 | } 347 | example1 = generx(example1); 348 | const result = example1().reduce((accum,item) => accum += item); // result = 15 349 | chai.expect(result).equal(15); 350 | done(); 351 | }); 352 | it("example2",async function() { 353 | async function* example2() { 354 | for(const item of [1,2,3,4,5]) { 355 | yield item; 356 | } 357 | } 358 | example2 = generx(example2); 359 | const result = await example2().reduce((accum,item) => accum += item); // result = 15 360 | chai.expect(result).equal(15); 361 | }); 362 | it("example3",async function() { 363 | async function* example3() { 364 | for(const item of [1,2,3,4,5]) { 365 | yield item; 366 | } 367 | } 368 | example3 = generx(example3); 369 | const result = await example3().reduce(async (accum,item) => accum += item); // result = 15 370 | chai.expect(result).equal(15); 371 | }); 372 | it("example4",function(done) { 373 | function* example4() { 374 | for(const item of [1,2,3,4,5]) { 375 | yield item; 376 | } 377 | } 378 | example4 = generx(example4); 379 | const result = example4()[2]; // result = 3 380 | chai.expect(result).equal(3); 381 | done(); 382 | }); 383 | it("example5",async function() { 384 | async function* example5() { 385 | for(const item of [1,2,3,4,5]) { 386 | yield item; 387 | } 388 | } 389 | example5 = generx(example5); 390 | const values = example5(), 391 | promise = values[0], 392 | result = await values[1]; // result = 2 393 | chai.expect(promise).to.be.instanceof(Promise); 394 | chai.expect(result).equal(2); 395 | }); 396 | it("example6",async function() { 397 | async function* example6() { 398 | for(const item of [1,2,3,4,5]) { 399 | yield item; 400 | } 401 | } 402 | example6 = generx(example6); 403 | const values = example6(); 404 | for(let i=0;await values[i] && i