from_async.js (7620B)
1 // |reftest| skip-if(!xulRuntime.shell) -- needs drainJobQueue 2 3 // Basic Smoke Test 4 async function* asyncGen(n) { 5 for (let i = 0; i < n; i++) { 6 yield i * 2; 7 } 8 } 9 10 let done = false; 11 Array.fromAsync(asyncGen(4)).then((x) => { 12 assertEq(Array.isArray(x), true); 13 assertEq(x.length, 4); 14 assertEq(x[0], 0); 15 assertEq(x[1], 2); 16 assertEq(x[2], 4); 17 assertEq(x[3], 6); 18 done = true; 19 } 20 ); 21 22 drainJobQueue(); 23 assertEq(done, true); 24 25 (async function () { 26 class InterruptableAsyncIterator { 27 count = 0 28 closed = false 29 throwAfter = NaN 30 constructor(n, throwAfter = NaN) { 31 this.count = n; 32 this.throwAfter = throwAfter; 33 } 34 [Symbol.asyncIterator] = function () { 35 return { 36 iter: this, 37 i: 0, 38 async next() { 39 if (this.i > this.iter.throwAfter) { 40 throw "Exception" 41 } 42 if (this.i++ < this.iter.count) { 43 return Promise.resolve({ done: false, value: this.i - 1 }); 44 } 45 return Promise.resolve({ done: true, value: undefined }); 46 }, 47 async return(x) { 48 this.iter.closed = true; 49 return { value: x, done: true }; 50 } 51 } 52 } 53 } 54 55 var one = await Array.fromAsync(new InterruptableAsyncIterator(2)); 56 assertEq(one.length, 2) 57 assertEq(one[0], 0); 58 assertEq(one[1], 1); 59 60 var two = new InterruptableAsyncIterator(10, 2); 61 var threw = false; 62 try { 63 var res = await Array.fromAsync(two); 64 } catch (e) { 65 threw = true; 66 assertEq(e, "Exception"); 67 } 68 assertEq(threw, true); 69 // The iterator is not closed unless we have an abrupt completion while mapping. 70 assertEq(two.closed, false); 71 72 // Test throwing while mapping: Iterator should be closed. 73 var three = new InterruptableAsyncIterator(10, 9); 74 threw = false; 75 try { 76 var res = await Array.fromAsync(three, (x) => { 77 if (x > 3) { 78 throw "Range" 79 } 80 return x; 81 }); 82 } catch (e) { 83 assertEq(e, "Range"); 84 threw = true; 85 } 86 assertEq(threw, true); 87 assertEq(three.closed, true); 88 89 var sync = await Array.fromAsync([1, 2, 3]); 90 assertEq(sync.length, 3); 91 assertEq(sync[0], 1) 92 assertEq(sync[1], 2) 93 assertEq(sync[2], 3) 94 95 let closed_frozen = false; 96 class Frozen { 97 constructor(x) { 98 this.count = x; 99 Object.freeze(this); 100 } 101 [Symbol.asyncIterator] = function () { 102 return { 103 iter: this, 104 i: 0, 105 async next() { 106 if (this.i++ < this.iter.count) { 107 return Promise.resolve({ done: false, value: this.i - 1 }); 108 } 109 return Promise.resolve({ done: true, value: undefined }); 110 }, 111 async return(x) { 112 // Can't use Frozen instance, becuse frozen is frozen. 113 closed_frozen = true; 114 return { value: x, done: true }; 115 } 116 } 117 } 118 } 119 120 // We should close the iterator when define property throws. 121 // Test by defining into a frozen object. 122 var frozen = new Frozen(10); 123 threw = false; 124 try { 125 var result = await Array.fromAsync.call(Frozen, frozen); 126 } catch (e) { 127 threw = true; 128 } 129 130 assertEq(threw, true); 131 assertEq(closed_frozen, true); 132 133 })(); 134 135 drainJobQueue(); 136 137 (async function () { 138 var badSyncIterator = { 139 [Symbol.iterator]() { 140 return null; 141 } 142 }; 143 144 var badAsyncIterator = { 145 [Symbol.asyncIterator]() { 146 return null; 147 } 148 }; 149 150 async function errorMessage(fn) { 151 try { 152 await fn(); 153 } catch (e) { 154 return e.message; 155 } 156 throw new Error("missing error"); 157 } 158 159 // Ensure Array.from and Array.fromAsync use consistent error reporting. 160 var expected = await errorMessage(() => Array.from(badSyncIterator)); 161 var actual = await errorMessage(() => Array.fromAsync(badSyncIterator)); 162 assertEq(actual, expected); 163 164 // Ensure for-of iteration and Array.fromAsync use consistent error reporting. 165 var expected = await errorMessage(() => { for (var _ of badSyncIterator); }); 166 var actual = await errorMessage(() => Array.fromAsync(badSyncIterator)); 167 assertEq(actual, expected); 168 169 // Ensure await for-of iteration and Array.fromAsync use consistent error reporting. 170 var expected = await errorMessage(async () => { for await (var _ of badAsyncIterator); }); 171 var actual = await errorMessage(() => Array.fromAsync(badAsyncIterator)); 172 assertEq(actual, expected); 173 })(); 174 175 drainJobQueue(); 176 177 (async function () { 178 function* gen() { 179 for (let i = 0; i < 4; ++i) { 180 yield Promise.resolve(i); 181 } 182 }; 183 184 var array = await Array.fromAsync(gen()); 185 186 // Promise values are unwrapped via AsyncFromSyncIterator. 187 assertEqArray(array, [0, 1, 2, 3]); 188 })(); 189 190 drainJobQueue(); 191 192 (async function () { 193 var badSyncIterator = { 194 [Symbol.iterator]: 123, 195 }; 196 197 var badAsyncIterator = { 198 [Symbol.asyncIterator]: 123, 199 }; 200 201 async function errorMessage(fn) { 202 try { 203 await fn(); 204 } catch (e) { 205 return e.message; 206 } 207 throw new Error("missing error"); 208 } 209 210 // Ensure Array.from and Array.fromAsync use consistent error reporting. 211 var expected = await errorMessage(() => Array.from(badSyncIterator)); 212 var actual = await errorMessage(() => Array.fromAsync(badSyncIterator)); 213 assertEq(actual.endsWith("is not iterable"), expected.endsWith("is not iterable")); 214 215 // Ensure for-of iteration and Array.fromAsync use consistent error reporting. 216 var expected = await errorMessage(() => { for (var _ of badSyncIterator); }); 217 var actual = await errorMessage(() => Array.fromAsync(badSyncIterator)); 218 assertEq(actual.endsWith("is not iterable"), expected.endsWith("is not iterable")); 219 220 // Ensure await for-of iteration and Array.fromAsync use consistent error reporting. 221 var expected = await errorMessage(async () => { for await (var _ of badAsyncIterator); }); 222 var actual = await errorMessage(() => Array.fromAsync(badAsyncIterator)); 223 assertEq(actual.endsWith("is not iterable"), expected.endsWith("is not iterable")); 224 })(); 225 226 drainJobQueue(); 227 228 229 var g = newGlobal(); 230 g.asyncGen = asyncGen; 231 var p = g.evaluate(` 232 Array.fromAsync(asyncGen(4)) 233 `) 234 235 p.then((x) => { 236 assertEq(x instanceof Array, false); // Should use the other global's Array. 237 assertEq(x instanceof g.Array, true); 238 }) 239 240 drainJobQueue(); 241 242 243 var g2 = newGlobal({ newCompartment: true }); 244 g2.asyncGen = asyncGen; 245 var p = g2.evaluate(` 246 Array.fromAsync(asyncGen(4)) 247 `) 248 249 p.then((x) => { 250 assertEq(x instanceof Array, false); // Should use the other global's Array. 251 assertEq(x instanceof g2.Array, true); 252 nukeCCW(x); // this will throw if x happens to not be a CCW (it should be!) 253 }) 254 drainJobQueue(); 255 256 // Test having a CCW 'this' value. 257 g2.obj = {}; 258 var p2 = g2.evaluate(` 259 Array.fromAsync.call(obj, asyncGen(4)) 260 `) 261 262 p2.then((x) => { 263 assertEq(x instanceof Array, false); // Should use the other global's Array. 264 assertEq(x instanceof g2.Array, true); 265 nukeCCW(x); 266 }) 267 268 drainJobQueue(); 269 270 // Verify user promise resolution behaviour. 271 var myThenCalled = false; 272 var obj = { then: () => { myThenCalled = true; } } 273 function* genO() { 274 yield obj; 275 return; 276 } 277 278 var res = Array.fromAsync(genO()); 279 res.then((x) => { 280 assertEq(x[0], obj); 281 assertEq(myThenCalled, true); 282 }); 283 284 drainJobQueue(); 285 286 function* thrower() { 287 throw new Error(); 288 } 289 290 g2.thrower = thrower; 291 var p = g2.evaluate(`Array.fromAsync(thrower())`) 292 p.catch((e) => { 293 assertEq(e instanceof Error, true, "Should throw an error from the current global"); 294 }) 295 drainJobQueue(); 296 297 p = g2.evaluate(`Array.fromAsync(thrower, 1)`); 298 p.catch((e) => assertEq(e instanceof g2.Error, true, "Should throw error from g2")) 299 drainJobQueue(); 300 301 if (typeof reportCompare === 'function') 302 reportCompare(true, true);