@@ -321,7 +321,7 @@ export class Book extends EventTarget {
321321 * Starts a fetch and progressively loads in the book.
322322 * @returns {Promise<Book> } A Promise that returns this book when all bytes have been fed to it.
323323 */
324- loadFromFetch ( ) {
324+ async loadFromFetch ( ) {
325325 if ( ! this . #needsLoading) {
326326 throw 'Cannot try to load via Fetch when the Book is already loading or loaded' ;
327327 }
@@ -332,88 +332,62 @@ export class Book extends EventTarget {
332332 this . #needsLoading = false ;
333333 this . dispatchEvent ( new BookLoadingStartedEvent ( this ) ) ;
334334
335- return fetch ( this . #request) . then ( response => {
336-
337- let pendingChunks = [ ] ;
338- const reader = response . body . getReader ( ) ;
339- /**
340- * This function receives a chunk. On the first chunk, it starts the async process of creating
341- * a BookBinder (analyzing the bytes and loading a script to instantiate the binder). Until
342- * the binder is created, all chunks are stored in the pendingChunks array. Once the binder
343- * object is created, all pending chunks are processed in order. On the last chunk, send the
344- * BookLoadingComplete event.
345- * @returns {Promise<Book> }
346- */
347- const readAndProcessNextChunk = ( ) => {
348- reader . read ( ) . then ( ( { done, value } ) => {
349- if ( Params [ 'debugFetch' ] === 'true' ) {
350- let str = `debugFetch: readAndProcessNextChunk(), ` ;
351- if ( done ) { str += 'done' ; }
352- else { str += `buffer.byteLength=${ value . buffer . byteLength } ` ; }
353- console . log ( str ) ;
354- }
355- if ( ! done ) {
356- // value is a chunk of the file as a Uint8Array.
357- if ( ! this . #bookBinder) {
358- // It is possible for #startBookBinding() to not set #bookBinder immediately because
359- // the binder script or the unarchiving script needs to load and be connected.
360- if ( this . #startedBinding) {
361- if ( Params [ 'debugFetch' ] === 'true' ) {
362- console . log ( `debugFetch: Found a pending chunk of length ${ value . buffer . byteLength } ` ) ;
363- }
364- pendingChunks . push ( value . buffer ) ;
365- return readAndProcessNextChunk ( ) ;
366- }
367- if ( Params [ 'debugFetch' ] === 'true' ) {
368- console . log ( `debugFetch: Got first chunk of length ${ value . buffer . byteLength } ` ) ;
369- }
370- // Else if binding has not started, start the book binding process (asynchronously).
371- return this . #startBookBinding( this . #name, value . buffer , this . #expectedSize) . then ( ( ) => {
372- if ( ! this . #bookBinder) {
373- throw `bookBinder was not set after startBookBinding()` ;
374- }
375- // After this point, #bookBinder is set.
376- if ( Params [ 'debugFetch' ] === 'true' ) {
377- console . log ( `debugFetch: Book Binder created` ) ;
378- }
379- if ( pendingChunks . length > 0 ) {
380- for ( const chunk of pendingChunks ) {
381- this . appendBytes ( chunk ) ;
382- this . #bookBinder. appendBytes ( chunk ) ;
383- if ( Params [ 'debugFetch' ] === 'true' ) {
384- console . log ( `debugFetch: Processed chunk of length ${ chunk . byteLength } ` ) ;
385- }
386- }
387- pendingChunks = [ ] ;
388- }
389- return readAndProcessNextChunk ( ) ;
390- } )
391- } // if (!this.#bookBinder)
392-
393- if ( Params [ 'debugFetch' ] === 'true' ) {
394- console . log ( `debugFetch: Got a chunk after binding of length ${ value . buffer . byteLength } ` ) ;
395- }
396-
397- // Process the chunk.
398- this . appendBytes ( value . buffer ) ;
399- this . #bookBinder. appendBytes ( value . buffer ) ;
400-
401- return readAndProcessNextChunk ( ) ;
402- } else {
403- if ( Params [ 'debugFetch' ] === 'true' ) {
404- console . log ( `debugFetch: Got to the end, done fetching book chunks` ) ;
405- }
406- this . #finishedLoading = true ;
407- this . dispatchEvent ( new BookLoadingCompleteEvent ( this ) ) ;
408- return this ;
409- }
410- } ) ;
411- } ;
412- return readAndProcessNextChunk ( ) ;
413- } ) . catch ( e => {
335+ /** @type {Response } */
336+ let response ;
337+ try {
338+ response = await fetch ( this . #request) ;
339+ } catch ( e ) {
414340 console . error ( `Error from fetch: ${ e } ` ) ;
415341 throw e ;
416- } ) ;
342+ }
343+
344+ // =============================================================================================
345+ // Option 1: Readable code, fetching chunk by chunk using await.
346+ const reader = response . body . getReader ( ) ;
347+
348+ /**
349+ * Reads one chunk at a time.
350+ * @returns {Promise<ArrayBuffer | null> }
351+ */
352+ const getOneChunk = async ( ) => {
353+ const { done, value } = await reader . read ( ) ;
354+ if ( ! done ) return value . buffer ;
355+ return null ;
356+ } ;
357+
358+ const firstChunk = await getOneChunk ( ) ;
359+ if ( ! firstChunk ) {
360+ throw `Could not get one chunk from fetch()` ;
361+ }
362+ let bytesTotal = firstChunk . byteLength ;
363+
364+ // Asynchronously wait for the BookBinder and its implementation to be connected.
365+ await this . #startBookBinding( this . #name, firstChunk , this . #expectedSize) ;
366+
367+ // Read out all subsequent chunks.
368+ /** @type {ArrayBuffer | null } */
369+ let nextChunk ;
370+ while ( nextChunk = await getOneChunk ( ) ) {
371+ bytesTotal += nextChunk . byteLength ;
372+ this . appendBytes ( nextChunk ) ;
373+ this . #bookBinder. appendBytes ( nextChunk ) ;
374+ }
375+
376+
377+ // =============================================================================================
378+ // Option 2: The XHR way (grab all bytes and only then start book binding).
379+ // const ab = await response.arrayBuffer();
380+ // bytesTotal = ab.byteLength;
381+ // await this.#startBookBinding(this.#name, ab, this.#expectedSize);
382+
383+ // Send out BookLoadingComplete event and return this book.
384+ this . #finishedLoading = true ;
385+ this . dispatchEvent ( new BookLoadingCompleteEvent ( this ) ) ;
386+ if ( Params [ 'debugFetch' ] === 'true' ) {
387+ console . log ( `debugFetch: ArrayBuffers were total length ${ bytesTotal } ` ) ;
388+ }
389+
390+ return this ;
417391 }
418392
419393 /**
0 commit comments