code: async function* generateTimeseries(
      lineDescriptors, revisions, levelOfDetail) {
    const readers = [];
    const timeseriesesByLine = [];
  
    for (const lineDescriptor of lineDescriptors) {
      const fetchDescriptors = ChartTimeseries.createFetchDescriptors(
          lineDescriptor, levelOfDetail);
      const timeserieses = new Array(fetchDescriptors.length);
      timeseriesesByLine.push({lineDescriptor, timeserieses});
  
      for (let fetchIndex = 0; fetchIndex < fetchDescriptors.length;
        ++fetchIndex) {
        readers.push((async function* () {
          const request = new TimeseriesRequest({
            ...fetchDescriptors[fetchIndex],
            ...revisions,
          });
  
          for await (const timeseries of request.reader()) {
            // Replace any previous timeseries from this reader.
            // TimeseriesCacheRequest merges results progressively.
            timeserieses[fetchIndex] = timeseries;
            yield {/* Pump BatchIterator. See timeseriesesByLine. */};
          }
        })());
      }
    }
  
    // Use BatchIterator only to batch result *events*, not the results
    // themselves. Manually collate results above to keep track of which line
    // and request go with each timeseries.
  
    for await (const {results, errors} of new BatchIterator(readers)) {
      const filtered = filterTimeseriesesByLine(timeseriesesByLine);
      yield {timeseriesesByLine: filtered, errors};
    }
  }
docs: Each lineDescriptor may require data from one or more fetchDescriptors. Fetch one or more fetchDescriptors per line, batch the readers, collate the data. Yields {timeseriesesByLine: [{lineDescriptor, timeserieses}], errors}.