concatenate.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. /*
  2. Copyright 2018 Google LLC
  3. Use of this source code is governed by an MIT-style
  4. license that can be found in the LICENSE file or at
  5. https://opensource.org/licenses/MIT.
  6. */
  7. import { logger } from 'workbox-core/_private/logger.js';
  8. import { assert } from 'workbox-core/_private/assert.js';
  9. import { Deferred } from 'workbox-core/_private/Deferred.js';
  10. import './_version.js';
  11. /**
  12. * Takes either a Response, a ReadableStream, or a
  13. * [BodyInit](https://fetch.spec.whatwg.org/#bodyinit) and returns the
  14. * ReadableStreamReader object associated with it.
  15. *
  16. * @param {module:workbox-streams.StreamSource} source
  17. * @return {ReadableStreamReader}
  18. * @private
  19. */
  20. function _getReaderFromSource(source) {
  21. if (source instanceof Response) {
  22. return source.body.getReader();
  23. }
  24. if (source instanceof ReadableStream) {
  25. return source.getReader();
  26. }
  27. return new Response(source).body.getReader();
  28. }
  29. /**
  30. * Takes multiple source Promises, each of which could resolve to a Response, a
  31. * ReadableStream, or a [BodyInit](https://fetch.spec.whatwg.org/#bodyinit).
  32. *
  33. * Returns an object exposing a ReadableStream with each individual stream's
  34. * data returned in sequence, along with a Promise which signals when the
  35. * stream is finished (useful for passing to a FetchEvent's waitUntil()).
  36. *
  37. * @param {Array<Promise<module:workbox-streams.StreamSource>>} sourcePromises
  38. * @return {Object<{done: Promise, stream: ReadableStream}>}
  39. *
  40. * @memberof module:workbox-streams
  41. */
  42. function concatenate(sourcePromises) {
  43. if (process.env.NODE_ENV !== 'production') {
  44. assert.isArray(sourcePromises, {
  45. moduleName: 'workbox-streams',
  46. funcName: 'concatenate',
  47. paramName: 'sourcePromises',
  48. });
  49. }
  50. const readerPromises = sourcePromises.map((sourcePromise) => {
  51. return Promise.resolve(sourcePromise).then((source) => {
  52. return _getReaderFromSource(source);
  53. });
  54. });
  55. const streamDeferred = new Deferred();
  56. let i = 0;
  57. const logMessages = [];
  58. const stream = new ReadableStream({
  59. pull(controller) {
  60. return readerPromises[i]
  61. .then((reader) => reader.read())
  62. .then((result) => {
  63. if (result.done) {
  64. if (process.env.NODE_ENV !== 'production') {
  65. logMessages.push(['Reached the end of source:',
  66. sourcePromises[i]]);
  67. }
  68. i++;
  69. if (i >= readerPromises.length) {
  70. // Log all the messages in the group at once in a single group.
  71. if (process.env.NODE_ENV !== 'production') {
  72. logger.groupCollapsed(`Concatenating ${readerPromises.length} sources.`);
  73. for (const message of logMessages) {
  74. if (Array.isArray(message)) {
  75. logger.log(...message);
  76. }
  77. else {
  78. logger.log(message);
  79. }
  80. }
  81. logger.log('Finished reading all sources.');
  82. logger.groupEnd();
  83. }
  84. controller.close();
  85. streamDeferred.resolve();
  86. return;
  87. }
  88. // The `pull` method is defined because we're inside it.
  89. return this.pull(controller);
  90. }
  91. else {
  92. controller.enqueue(result.value);
  93. }
  94. }).catch((error) => {
  95. if (process.env.NODE_ENV !== 'production') {
  96. logger.error('An error occurred:', error);
  97. }
  98. streamDeferred.reject(error);
  99. throw error;
  100. });
  101. },
  102. cancel() {
  103. if (process.env.NODE_ENV !== 'production') {
  104. logger.warn('The ReadableStream was cancelled.');
  105. }
  106. streamDeferred.resolve();
  107. },
  108. });
  109. return { done: streamDeferred.promise, stream };
  110. }
  111. export { concatenate };