فهرست منبع

fix ajaxGet: handle case where download had finished before callback were registered

Alexander Rose 5 سال پیش
والد
کامیت
b70b9c44fc
1فایلهای تغییر یافته به همراه47 افزوده شده و 26 حذف شده
  1. 47 26
      src/mol-util/data-source.ts

+ 47 - 26
src/mol-util/data-source.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -67,8 +67,8 @@ function decompress(buffer: Uint8Array): Uint8Array {
     // return gzip.decompress();
 }
 
-async function processFile(ctx: RuntimeContext, asUint8Array: boolean, compressed: boolean, e: any) {
-    const data = (e.target as FileReader).result;
+async function processFile(ctx: RuntimeContext, asUint8Array: boolean, compressed: boolean, fileReader: FileReader) {
+    const data = fileReader.result;
 
     if (compressed) {
         await ctx.update('Decompressing...');
@@ -84,11 +84,31 @@ async function processFile(ctx: RuntimeContext, asUint8Array: boolean, compresse
     }
 }
 
-function readData(ctx: RuntimeContext, action: string, data: XMLHttpRequest | FileReader, asUint8Array: boolean): Promise<any> {
-    return new Promise<any>((resolve, reject) => {
-        data.onerror = (e: any) => {
+function isDone(data: XMLHttpRequest | FileReader) {
+    if (data instanceof FileReader) {
+        return data.readyState === FileReader.DONE
+    } else if (data instanceof XMLHttpRequest) {
+        return data.readyState === XMLHttpRequest.DONE
+    }
+    throw new Error('unknown data type')
+}
+
+function readData<T extends XMLHttpRequest | FileReader>(ctx: RuntimeContext, action: string, data: T, asUint8Array: boolean): Promise<T> {
+    return new Promise<T>((resolve, reject) => {
+        // first check if data reading is already done
+        if (isDone(data)) {
+            const error = (<FileReader>data).error;
+            if (error) {
+                reject((<FileReader>data).error || 'Failed.');
+            } else {
+                resolve(data);
+            }
+            return
+        }
+
+        data.onerror = (e: ProgressEvent) => {
             const error = (<FileReader>e.target).error;
-            reject(error ? error : 'Failed.');
+            reject(error || 'Failed.');
         };
 
         let hasError = false;
@@ -106,7 +126,10 @@ function readData(ctx: RuntimeContext, action: string, data: XMLHttpRequest | Fi
                 reject(e);
             }
         }
-        data.onload = (e: any) => resolve(e);
+
+        data.onload = (e: ProgressEvent) => {
+            resolve(data);
+        }
     });
 }
 
@@ -121,8 +144,8 @@ function readFromFileInternal(file: File, asUint8Array: boolean): Task<string |
             else reader.readAsBinaryString(file);
 
             ctx.update({ message: 'Opening file...', canAbort: true });
-            const e = await readData(ctx, 'Reading...', reader, asUint8Array);
-            const result = processFile(ctx, asUint8Array, isCompressed, e);
+            const fileReader = await readData(ctx, 'Reading...', reader, asUint8Array);
+            const result = processFile(ctx, asUint8Array, isCompressed, fileReader);
             return result;
         } finally {
             reader = void 0;
@@ -156,27 +179,25 @@ class RequestPool {
     }
 }
 
-async function processAjax(ctx: RuntimeContext, asUint8Array: boolean, decompressGzip: boolean, e: any) {
-    const req = (e.target as XMLHttpRequest);
+async function processAjax(ctx: RuntimeContext, asUint8Array: boolean, decompressGzip: boolean, req: XMLHttpRequest) {
     if (req.status >= 200 && req.status < 400) {
-        if (asUint8Array) {
-            const buff = new Uint8Array(e.target.response);
-            RequestPool.deposit(e.target);
+        if (asUint8Array === true) {
+            const buff = new Uint8Array(req.response);
+            RequestPool.deposit(req);
 
             if (decompressGzip) {
                 return decompress(buff);
             } else {
                 return buff;
             }
-        }
-        else {
-            const text = e.target.responseText;
-            RequestPool.deposit(e.target);
+        } else {
+            const text = req.responseText;
+            RequestPool.deposit(req);
             return text;
         }
     } else {
         const status = req.statusText;
-        RequestPool.deposit(e.target);
+        RequestPool.deposit(req);
         throw status;
     }
 }
@@ -196,23 +217,23 @@ function ajaxGetInternal(title: string | undefined, url: string, type: 'json' |
         xhttp.send(body);
 
         await ctx.update({ message: 'Waiting for server...', canAbort: true });
-        const e = await readData(ctx, 'Downloading...', xhttp, asUint8Array);
-        xhttp = void 0;
-        const result = await processAjax(ctx, asUint8Array, decompressGzip, e)
+        const req = await readData(ctx, 'Downloading...', xhttp, asUint8Array);
+        xhttp = void 0; // guard against reuse, help garbage collector
+        const result = await processAjax(ctx, asUint8Array, decompressGzip, req)
 
         if (type === 'json') {
             await ctx.update({ message: 'Parsing JSON...', canAbort: false });
-            return JSON.parse(result);
+            return JSON.parse(result as string);
         } else if (type === 'xml') {
             await ctx.update({ message: 'Parsing XML...', canAbort: false });
-            return parseXml(result);
+            return parseXml(result as string);
         }
 
         return result;
     }, () => {
         if (xhttp) {
             xhttp.abort();
-            xhttp = void 0;
+            xhttp = void 0; // guard against reuse, help garbage collector
         }
     });
 }