JSlang.dev

JavaScript Deep Dives with Wolfram. Since 2015
Inclusive, Test-Driven, Spec-Focused, Collaborative.

February 14, 2019
TestsSource
Async Iterations (for-await-of)

Source Code

The source code we wrote during the event.

import assert from 'assert';

describe('for-of with async+await', () => {
  it('iterate over array of async, using for-of, each values resolves', async () => {
    const p1 = Promise.resolve(1);
    const p2 = Promise.resolve(2);
    const ps = [p1, p2];
    const results = [];
    for (const x of ps) {
      results.push(await x);
    }
    assert.deepEqual([1, 2], results);
  });
  it('order will remain, no matter the timing', async () => {
    const p1 = new Promise((resolve) => { setTimeout(() => resolve(1), 100) });
    const p2 = Promise.resolve(2);
    const ps = [p1, p2];
    const results = [];
    for (const x of ps) {
      results.push(await x);
    }
    assert.deepEqual([1, 2], results);
  });
  it('rejecting a promise, will break the execution of `for` loop', async () => {
    const p1 = Promise.reject(1);
    const p2 = Promise.resolve(2);
    const ps = [p1, p2];
    const results = [];
    try {
      for (const x of ps) {
        results.push(await x);
      }
      assert(false, 'The code should never get here, because the for loop should throw');
    } catch (e) {
      assert.deepEqual([], results);
    }
  });
  it('fill an array until a rejected promise', async () => {
    const p1 = Promise.resolve(1);
    const p2 = Promise.reject(2);
    const ps = [p1, p2];
    const results = [];
    try {
      for (const x of ps) {
        results.push(await x);
      }
      assert(false, 'The code should never get here, because the for loop should throw');
    } catch (e) {
      assert.deepEqual([1], results);
    }
  });
  it('fill an array until a rejected promise (and not beyond)', async () => {
    const p1 = Promise.resolve(1);
    const p2 = Promise.reject(2);
    const p3 = Promise.resolve(1);
    const ps = [p1, p2, p3];
    const results = [];
    try {
      for (const x of ps) {
        results.push(await x);
      }
      assert(false, 'The code should never get here, because the for loop should throw');
    } catch (e) {
      assert.deepEqual([1], results);
    }
  });
});

describe('having `await` inside `forEach`', () => {
  it('`forEach` over array of promise, expect NO elements to return', async () => {
    const p1 = Promise.resolve(1);
    const p2 = Promise.resolve(2);
    const ps = [p1, p2];
    const results = [];
    ps.forEach(async (p) => { results.push(await p) });
    assert.deepEqual([], results);
  });
  it('`forEach` over array of promise, expect all elements to return, WHEN we delay the assert', async () => {
    const p1 = Promise.resolve(1);
    const p2 = Promise.resolve(2);
    const ps = [p1, p2];
    const results = [];
    ps.forEach(async (p) => { results.push(await p) });
    await new Promise(resolve => setTimeout(resolve, 0));
    assert.deepEqual([1,2], results);
  });
  it('`forEach` over array of promise, expect all elements to return, WHEN we delay the assert, even 1000 times', async () => {
    let x = 0;
    while (x++ < 100) {
      const p1 = Promise.resolve(1);
      const p2 = Promise.resolve(2);
      const ps = [p1, p2];
      const results = [];
      ps.forEach(async (p) => { results.push(await p) });
      await new Promise(resolve => setTimeout(resolve, 0));
      assert.deepEqual([1, 2], results);
    }
  });
  it('two delayed promises (with the same delay) the order stays (also many times)', async () => {
    let x = 0;
    while (x++ < 200) {
      const p1 = new Promise(resolve => setTimeout(() => resolve(1), 2));
      const p2 = new Promise(resolve => setTimeout(() => resolve(2), 2));
      const ps = [p1, p2];
      const results = [];
      ps.forEach(async (p) => { results.push(await p) });
      await new Promise(resolve => setTimeout(resolve, 2));
      assert.deepEqual([1, 2], results);
    }
  }).timeout(10000);
  it('dont wait long enough after the `forEach` for the asyncs to finish', async () => {
    const p1 = new Promise(resolve => setTimeout(() => resolve(1), 4));
    const p2 = new Promise(resolve => setTimeout(() => resolve(2), 4));
    const ps = [p1, p2];
    const results = [];
    ps.forEach(async (p) => { results.push(await p) });
    await new Promise(resolve => setTimeout(resolve, 2));
    assert.deepEqual([], results);
  });
  
  // it will endlessly block
  // it('setTimeout is not called when we dont exit the current callstack', async () => {
  //   const p1 = new Promise(resolve => setTimeout(() => resolve(1), 2));
  //   const p2 = new Promise(resolve => setTimeout(() => resolve(2), 2));
  //   const ps = [p1, p2];
  //   const results = [];
  //   ps.forEach(async (p) => { results.push(await p) });
  //   while (results.length < ps.length) {}
  //   assert.deepEqual([1,2], results);
  // });
  
});

describe('iterate over list of promises', () => {
  it('collect the results 2 resolving promises, works', async () => {
    const promises = [Promise.resolve(1), Promise.resolve(2)];
    let results = [];
    for await (const p of promises) {
      results.push(p);
    }
    assert.deepEqual([1,2], results);
  });
  it('reject one promise (of the array), throws', async () => {
    const p1 = Promise.reject(1);
    const p2 = Promise.resolve(2);
    const ps = [p1, p2];
    const results = [];
    try {
      for await (const x of ps) {
        results.push(x);
      }
      assert(false, 'The code should never get here, because the for loop should throw');
    } catch (e) {
      assert.deepEqual([], results);
    }
  });
  it('collect results we await for ... dont do it :)', async () => {
    let p1 = Promise.resolve(1);
    let p2 = Promise.resolve(2);
    const ps = [p1, p2];
    for await (const x of ps) {
      if (ps.length < 10) {
        ps.push(x);
      }
    }
    assert.deepEqual([p1,p2,1,2,1,2,1,2,1,2], ps);
  });
  it('promise order depends on timing', async () => {
    const p1 = new Promise((resolve) => { setTimeout(() => resolve(1), 100) });
    const p2 = Promise.resolve(2);
    const ps = [p1, p2];
    const results = [];
    for await (const x of ps) {
      results.push(x);
    }
    assert.deepEqual([1, 2], results);
  });
  it('We dont expect to get into the for-loop, iterating over empty array', async () => {
    for await (const x of []) {
      assert(false, 'We dont expect to get into the for-loop')
    }
  });
  it('will for-await loop wait for all promises inside of it', async () => {
    try {
      for await (const x of [1]) {
        const f = async () => { throw Error('failsss'); };
        await f();
      }
    } catch (e) {
      assert(true);
    } 
  });
  xit('will for-await loop wait for all promises inside of it', async () => {
    try {
      for await (const x of [1]) {
        const f = async () => { throw Error('failsss'); };
        f();
        assert(true);
      }
    } catch (e) {
      assert(false, 'should not get here');
    } 
  });
});