diff --git a/docs/api/en/loaders/Loader.html b/docs/api/en/loaders/Loader.html index 7fb38e41f50b1f..4c718fb9e706fc 100644 --- a/docs/api/en/loaders/Loader.html +++ b/docs/api/en/loaders/Loader.html @@ -60,6 +60,11 @@
+ An [link:https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal AbortSignal] instance used to cancel the requests. See [page:.setAbortSignal]. Default is null. +
+
+ [page:AbortSignal abortSignal] - Set the [link:https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal AbortSignal] instance used to cancel the requests.
+ Warning: once the signal has been triggered, the loader will always immediately cancel new requests, you must then provide a new signal (from a new AbortController).
+
+ const controller = new AbortController();
+ const loader = new FileLoader();
+ loader.setAbortSignal(controller.signal);
+
+ loader.load(/* .... */);
+ controller.abort();
+
+
diff --git a/examples/jsm/loaders/3DMLoader.js b/examples/jsm/loaders/3DMLoader.js index 19bbbd6aefac35..f1a8d80c863320 100644 --- a/examples/jsm/loaders/3DMLoader.js +++ b/examples/jsm/loaders/3DMLoader.js @@ -73,6 +73,7 @@ class Rhino3dmLoader extends Loader { loader.setPath( this.path ); loader.setResponseType( 'arraybuffer' ); loader.setRequestHeader( this.requestHeader ); + loader.setAbortSignal( this.abortSignal ); this.url = url; @@ -124,6 +125,7 @@ class Rhino3dmLoader extends Loader { worker._callbacks[ taskID ] = { resolve, reject }; + // TODO if abortSignal is defined, listen to it to cancel the worker worker.postMessage( { type: 'decode', id: taskID, buffer }, [ buffer ] ); // this.debug(); @@ -671,21 +673,17 @@ class Rhino3dmLoader extends Loader { // Load rhino3dm wrapper. const jsLoader = new FileLoader( this.manager ); jsLoader.setPath( this.libraryPath ); - const jsContent = new Promise( ( resolve, reject ) => { + jsLoader.setAbortSignal( this.abortSignal ); - jsLoader.load( 'rhino3dm.js', resolve, undefined, reject ); - - } ); + const jsContent = jsLoader.loadAsync( 'rhino3dm.js' ); // Load rhino3dm WASM binary. const binaryLoader = new FileLoader( this.manager ); binaryLoader.setPath( this.libraryPath ); binaryLoader.setResponseType( 'arraybuffer' ); - const binaryContent = new Promise( ( resolve, reject ) => { + binaryLoader.setAbortSignal( this.abortSignal ); - binaryLoader.load( 'rhino3dm.wasm', resolve, undefined, reject ); - - } ); + const binaryContent = binaryLoader.loadAsync( 'rhino3dm.wasm' ); this.libraryPending = Promise.all( [ jsContent, binaryContent ] ) .then( ( [ jsContent, binaryContent ] ) => { @@ -704,6 +702,18 @@ class Rhino3dmLoader extends Loader { this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) ); + } ) + .catch( ( error ) => { + + // on user abort + if ( error.name === 'AbortError' ) { + + this.libraryPending = null; + + } + + throw error; + } ); } diff --git a/examples/jsm/loaders/3MFLoader.js b/examples/jsm/loaders/3MFLoader.js index e8e94f132af0ef..76a04472fac53a 100644 --- a/examples/jsm/loaders/3MFLoader.js +++ b/examples/jsm/loaders/3MFLoader.js @@ -58,6 +58,8 @@ class ThreeMFLoader extends Loader { loader.setResponseType( 'arraybuffer' ); loader.setRequestHeader( scope.requestHeader ); loader.setWithCredentials( scope.withCredentials ); + loader.setAbortSignal( scope.abortSignal ); + loader.load( url, function ( buffer ) { try { @@ -88,6 +90,8 @@ class ThreeMFLoader extends Loader { const scope = this; const textureLoader = new TextureLoader( this.manager ); + textureLoader.setCrossOrigin( this.crossOrigin ); + textureLoader.setAbortSignal( this.abortSignal ); function loadDocument( data ) { diff --git a/examples/jsm/loaders/AMFLoader.js b/examples/jsm/loaders/AMFLoader.js index a8ac15e090e4ed..82cea4424a9f72 100644 --- a/examples/jsm/loaders/AMFLoader.js +++ b/examples/jsm/loaders/AMFLoader.js @@ -44,6 +44,8 @@ class AMFLoader extends Loader { loader.setResponseType( 'arraybuffer' ); loader.setRequestHeader( scope.requestHeader ); loader.setWithCredentials( scope.withCredentials ); + loader.setAbortSignal( scope.abortSignal ); + loader.load( url, function ( text ) { try { diff --git a/examples/jsm/loaders/BVHLoader.js b/examples/jsm/loaders/BVHLoader.js index b916e86338496a..70302ae388b86a 100644 --- a/examples/jsm/loaders/BVHLoader.js +++ b/examples/jsm/loaders/BVHLoader.js @@ -36,6 +36,8 @@ class BVHLoader extends Loader { loader.setPath( scope.path ); loader.setRequestHeader( scope.requestHeader ); loader.setWithCredentials( scope.withCredentials ); + loader.setAbortSignal( scope.abortSignal ); + loader.load( url, function ( text ) { try { diff --git a/examples/jsm/loaders/BasisTextureLoader.js b/examples/jsm/loaders/BasisTextureLoader.js index e71a7210c483eb..b3a832cc1a23e6 100644 --- a/examples/jsm/loaders/BasisTextureLoader.js +++ b/examples/jsm/loaders/BasisTextureLoader.js @@ -92,9 +92,9 @@ class BasisTextureLoader extends Loader { load( url, onLoad, onProgress, onError ) { const loader = new FileLoader( this.manager ); - loader.setResponseType( 'arraybuffer' ); loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); const texture = new CompressedTexture(); @@ -173,6 +173,7 @@ class BasisTextureLoader extends Loader { worker._callbacks[ taskID ] = { resolve, reject }; + // TODO if abortSignal is defined, listen to it to cancel the worker worker.postMessage( { type: 'transcode', id: taskID, buffers: buffers, taskConfig: taskConfig }, buffers ); } ); @@ -221,22 +222,18 @@ class BasisTextureLoader extends Loader { const jsLoader = new FileLoader( this.manager ); jsLoader.setPath( this.transcoderPath ); jsLoader.setWithCredentials( this.withCredentials ); - const jsContent = new Promise( ( resolve, reject ) => { - - jsLoader.load( 'basis_transcoder.js', resolve, undefined, reject ); + jsLoader.setAbortSignal( this.abortSignal ); - } ); + const jsContent = jsLoader.loadAsync( 'basis_transcoder.js' ); // Load transcoder WASM binary. const binaryLoader = new FileLoader( this.manager ); binaryLoader.setPath( this.transcoderPath ); binaryLoader.setResponseType( 'arraybuffer' ); binaryLoader.setWithCredentials( this.withCredentials ); - const binaryContent = new Promise( ( resolve, reject ) => { + binaryLoader.setAbortSignal( this.abortSignal ); - binaryLoader.load( 'basis_transcoder.wasm', resolve, undefined, reject ); - - } ); + const binaryContent = binaryLoader.loadAsync( 'basis_transcoder.wasm' ); this.transcoderPending = Promise.all( [ jsContent, binaryContent ] ) .then( ( [ jsContent, binaryContent ] ) => { @@ -257,6 +254,18 @@ class BasisTextureLoader extends Loader { this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) ); this.transcoderBinary = binaryContent; + } ) + .catch( ( error ) => { + + // on user abort + if ( error.name === 'AbortError' ) { + + this.transcoderPending = null; + + } + + throw error; + } ); } diff --git a/examples/jsm/loaders/ColladaLoader.js b/examples/jsm/loaders/ColladaLoader.js index b755954a15a4b5..1089d345df76ba 100644 --- a/examples/jsm/loaders/ColladaLoader.js +++ b/examples/jsm/loaders/ColladaLoader.js @@ -58,6 +58,8 @@ class ColladaLoader extends Loader { loader.setPath( scope.path ); loader.setRequestHeader( scope.requestHeader ); loader.setWithCredentials( scope.withCredentials ); + loader.setAbortSignal( scope.abortSignal ); + loader.load( url, function ( text ) { try { diff --git a/examples/jsm/loaders/DRACOLoader.js b/examples/jsm/loaders/DRACOLoader.js index e9f3ca48a39f8a..332f91211d04a9 100644 --- a/examples/jsm/loaders/DRACOLoader.js +++ b/examples/jsm/loaders/DRACOLoader.js @@ -65,11 +65,11 @@ class DRACOLoader extends Loader { load( url, onLoad, onProgress, onError ) { const loader = new FileLoader( this.manager ); - loader.setPath( this.path ); loader.setResponseType( 'arraybuffer' ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); loader.load( url, ( buffer ) => { @@ -165,6 +165,7 @@ class DRACOLoader extends Loader { worker._callbacks[ taskID ] = { resolve, reject }; + // TODO if abortSignal is defined, listen to it to cancel the worker worker.postMessage( { type: 'decode', id: taskID, taskConfig, buffer }, [ buffer ] ); // this.debug(); @@ -233,12 +234,9 @@ class DRACOLoader extends Loader { loader.setPath( this.decoderPath ); loader.setResponseType( responseType ); loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); - return new Promise( ( resolve, reject ) => { - - loader.load( url, resolve, undefined, reject ); - - } ); + return loader.loadAsync( url ); } @@ -291,6 +289,18 @@ class DRACOLoader extends Loader { this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) ); + } ) + .catch( ( error ) => { + + // on user abort + if ( error.name === 'AbortError' ) { + + this.decoderPending = null; + + } + + throw error; + } ); return this.decoderPending; diff --git a/examples/jsm/loaders/FBXLoader.js b/examples/jsm/loaders/FBXLoader.js index 5cd7c8b10466be..dac698a934be83 100644 --- a/examples/jsm/loaders/FBXLoader.js +++ b/examples/jsm/loaders/FBXLoader.js @@ -83,6 +83,7 @@ class FBXLoader extends Loader { loader.setResponseType( 'arraybuffer' ); loader.setRequestHeader( scope.requestHeader ); loader.setWithCredentials( scope.withCredentials ); + loader.setAbortSignal( scope.abortSignal ); loader.load( url, function ( buffer ) { @@ -138,7 +139,10 @@ class FBXLoader extends Loader { // console.log( fbxTree ); - const textureLoader = new TextureLoader( this.manager ).setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin ); + const textureLoader = new TextureLoader( this.manager ); + textureLoader.setPath( this.resourcePath || path ); + textureLoader.setCrossOrigin( this.crossOrigin ); + textureLoader.setAbortSignal( this.abortSignal ); return new FBXTreeParser( textureLoader, this.manager ).parse( fbxTree ); diff --git a/examples/jsm/loaders/FontLoader.js b/examples/jsm/loaders/FontLoader.js index e24ef963f58d4f..044d049c369d13 100644 --- a/examples/jsm/loaders/FontLoader.js +++ b/examples/jsm/loaders/FontLoader.js @@ -19,7 +19,9 @@ class FontLoader extends Loader { const loader = new FileLoader( this.manager ); loader.setPath( this.path ); loader.setRequestHeader( this.requestHeader ); - loader.setWithCredentials( scope.withCredentials ); + loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); + loader.load( url, function ( text ) { let json; diff --git a/examples/jsm/loaders/GCodeLoader.js b/examples/jsm/loaders/GCodeLoader.js index 03e3de3b3e8d1a..1948d6af24ac32 100644 --- a/examples/jsm/loaders/GCodeLoader.js +++ b/examples/jsm/loaders/GCodeLoader.js @@ -36,6 +36,8 @@ class GCodeLoader extends Loader { loader.setPath( scope.path ); loader.setRequestHeader( scope.requestHeader ); loader.setWithCredentials( scope.withCredentials ); + loader.setAbortSignal( scope.abortSignal ); + loader.load( url, function ( text ) { try { diff --git a/examples/jsm/loaders/GLTFLoader.js b/examples/jsm/loaders/GLTFLoader.js index a04bb707e03b5f..de92c0d3fd6ee7 100644 --- a/examples/jsm/loaders/GLTFLoader.js +++ b/examples/jsm/loaders/GLTFLoader.js @@ -185,6 +185,7 @@ class GLTFLoader extends Loader { loader.setResponseType( 'arraybuffer' ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); loader.load( url, function ( data ) { @@ -314,14 +315,13 @@ class GLTFLoader extends Loader { path: path || this.resourcePath || '', crossOrigin: this.crossOrigin, requestHeader: this.requestHeader, + abortSignal: this.abortSignal, manager: this.manager, ktx2Loader: this.ktx2Loader, meshoptDecoder: this.meshoptDecoder } ); - parser.fileLoader.setRequestHeader( this.requestHeader ); - for ( let i = 0; i < this.pluginCallbacks.length; i ++ ) { const plugin = this.pluginCallbacks[ i ]( parser ); @@ -2263,9 +2263,12 @@ class GLTFParser { this.textureLoader.setCrossOrigin( this.options.crossOrigin ); this.textureLoader.setRequestHeader( this.options.requestHeader ); + this.textureLoader.setAbortSignal( this.options.abortSignal ); this.fileLoader = new FileLoader( this.options.manager ); this.fileLoader.setResponseType( 'arraybuffer' ); + this.fileLoader.setRequestHeader( this.options.requestHeader ); + this.fileLoader.setAbortSignal( this.options.abortSignal ); if ( this.options.crossOrigin === 'use-credentials' ) { diff --git a/examples/jsm/loaders/HDRCubeTextureLoader.js b/examples/jsm/loaders/HDRCubeTextureLoader.js index d55fec78987d5d..1d08760964bf7a 100644 --- a/examples/jsm/loaders/HDRCubeTextureLoader.js +++ b/examples/jsm/loaders/HDRCubeTextureLoader.js @@ -73,6 +73,7 @@ class HDRCubeTextureLoader extends Loader { .setPath( scope.path ) .setResponseType( 'arraybuffer' ) .setWithCredentials( scope.withCredentials ) + .setAbortSignal( scope.abortSignal ) .load( urls[ i ], function ( buffer ) { loaded ++; diff --git a/examples/jsm/loaders/IFCLoader.js b/examples/jsm/loaders/IFCLoader.js index 5187eb72cd2755..01b1692e4b856c 100644 --- a/examples/jsm/loaders/IFCLoader.js +++ b/examples/jsm/loaders/IFCLoader.js @@ -2387,6 +2387,8 @@ class IFCLoader extends Loader { loader.setResponseType( 'arraybuffer' ); loader.setRequestHeader( scope.requestHeader ); loader.setWithCredentials( scope.withCredentials ); + loader.setAbortSignal( scope.abortSignal ); + loader.load( url, async function ( buffer ) { try { diff --git a/examples/jsm/loaders/KMZLoader.js b/examples/jsm/loaders/KMZLoader.js index 0f451422c5a8e2..da546d07a0118d 100644 --- a/examples/jsm/loaders/KMZLoader.js +++ b/examples/jsm/loaders/KMZLoader.js @@ -24,6 +24,8 @@ class KMZLoader extends Loader { loader.setResponseType( 'arraybuffer' ); loader.setRequestHeader( scope.requestHeader ); loader.setWithCredentials( scope.withCredentials ); + loader.setAbortSignal( scope.abortSignal ); + loader.load( url, function ( text ) { try { diff --git a/examples/jsm/loaders/KTX2Loader.js b/examples/jsm/loaders/KTX2Loader.js index a72e44154c2862..b4b83d7a089b99 100644 --- a/examples/jsm/loaders/KTX2Loader.js +++ b/examples/jsm/loaders/KTX2Loader.js @@ -123,6 +123,8 @@ class KTX2Loader extends Loader { const jsLoader = new FileLoader( this.manager ); jsLoader.setPath( this.transcoderPath ); jsLoader.setWithCredentials( this.withCredentials ); + jsLoader.setAbortSignal( this.abortSignal ); + const jsContent = jsLoader.loadAsync( 'basis_transcoder.js' ); // Load transcoder WASM binary. @@ -130,6 +132,8 @@ class KTX2Loader extends Loader { binaryLoader.setPath( this.transcoderPath ); binaryLoader.setResponseType( 'arraybuffer' ); binaryLoader.setWithCredentials( this.withCredentials ); + binaryLoader.setAbortSignal( this.abortSignal ); + const binaryContent = binaryLoader.loadAsync( 'basis_transcoder.wasm' ); this.transcoderPending = Promise.all( [ jsContent, binaryContent ] ) @@ -162,6 +166,18 @@ class KTX2Loader extends Loader { } ); + } ) + .catch( ( error ) => { + + // on user abort + if ( error.name === 'AbortError' ) { + + this.transcoderPending = null; + + } + + throw error; + } ); if ( _activeLoaders > 0 ) { @@ -194,9 +210,9 @@ class KTX2Loader extends Loader { } const loader = new FileLoader( this.manager ); - loader.setResponseType( 'arraybuffer' ); loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); const texture = new CompressedTexture(); @@ -257,6 +273,7 @@ class KTX2Loader extends Loader { const taskConfig = config; const texturePending = this.init().then( () => { + // TODO if abortSignal is defined, listen to it to cancel the worker return this.workerPool.postMessage( { type: 'transcode', buffers, taskConfig: taskConfig }, buffers ); } ).then( ( e ) => this._createTextureFrom( e.data ) ); diff --git a/examples/jsm/loaders/LDrawLoader.js b/examples/jsm/loaders/LDrawLoader.js index 9e222287573bab..52e2190f239dd1 100644 --- a/examples/jsm/loaders/LDrawLoader.js +++ b/examples/jsm/loaders/LDrawLoader.js @@ -746,6 +746,7 @@ class LDrawParsedCache { fileLoader.setPath( loader.partsLibraryPath ); fileLoader.setRequestHeader( loader.requestHeader ); fileLoader.setWithCredentials( loader.withCredentials ); + fileLoader.setAbortSignal( loader.abortSignal ); try { @@ -1558,6 +1559,8 @@ class LDrawLoader extends Loader { fileLoader.setPath( this.path ); fileLoader.setRequestHeader( this.requestHeader ); fileLoader.setWithCredentials( this.withCredentials ); + fileLoader.setAbortSignal( this.abortSignal ); + fileLoader.load( url, text => { const parsedInfo = this.parseCache.parse( text ); diff --git a/examples/jsm/loaders/LUT3dlLoader.js b/examples/jsm/loaders/LUT3dlLoader.js index c6791a41dff934..c949d84d5f5736 100644 --- a/examples/jsm/loaders/LUT3dlLoader.js +++ b/examples/jsm/loaders/LUT3dlLoader.js @@ -18,6 +18,9 @@ export class LUT3dlLoader extends Loader { const loader = new FileLoader( this.manager ); loader.setPath( this.path ); loader.setResponseType( 'text' ); + loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); + loader.load( url, text => { try { diff --git a/examples/jsm/loaders/LUTCubeLoader.js b/examples/jsm/loaders/LUTCubeLoader.js index 92f29cef308d6b..6ab47d28f43fba 100644 --- a/examples/jsm/loaders/LUTCubeLoader.js +++ b/examples/jsm/loaders/LUTCubeLoader.js @@ -19,6 +19,9 @@ export class LUTCubeLoader extends Loader { const loader = new FileLoader( this.manager ); loader.setPath( this.path ); loader.setResponseType( 'text' ); + loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); + loader.load( url, text => { try { diff --git a/examples/jsm/loaders/LWOLoader.js b/examples/jsm/loaders/LWOLoader.js index fdc6d9a46945b8..34019e4cfdc827 100644 --- a/examples/jsm/loaders/LWOLoader.js +++ b/examples/jsm/loaders/LWOLoader.js @@ -65,6 +65,8 @@ class LWOLoader extends Loader { const loader = new FileLoader( this.manager ); loader.setPath( scope.path ); loader.setResponseType( 'arraybuffer' ); + loader.setWithCredentials( scope.withCredentials ); + loader.setAbortSignal( scope.abortSignal ); loader.load( url, function ( buffer ) { @@ -102,7 +104,10 @@ class LWOLoader extends Loader { // console.log( 'lwoTree', lwoTree ); - const textureLoader = new TextureLoader( this.manager ).setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin ); + const textureLoader = new TextureLoader( this.manager ); + textureLoader.setPath( this.resourcePath || path ); + textureLoader.setCrossOrigin( this.crossOrigin ); + textureLoader.setAbortSignal( this.abortSignal ); return new LWOTreeParser( textureLoader ).parse( modelName ); diff --git a/examples/jsm/loaders/LottieLoader.js b/examples/jsm/loaders/LottieLoader.js index ab14db6733c879..3806f188c3af8b 100644 --- a/examples/jsm/loaders/LottieLoader.js +++ b/examples/jsm/loaders/LottieLoader.js @@ -23,6 +23,7 @@ class LottieLoader extends Loader { const loader = new FileLoader( this.manager ); loader.setPath( this.path ); loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); loader.load( url, function ( text ) { diff --git a/examples/jsm/loaders/MD2Loader.js b/examples/jsm/loaders/MD2Loader.js index 670b8faa5715ec..31e3c9238a222c 100644 --- a/examples/jsm/loaders/MD2Loader.js +++ b/examples/jsm/loaders/MD2Loader.js @@ -108,6 +108,8 @@ class MD2Loader extends Loader { loader.setResponseType( 'arraybuffer' ); loader.setRequestHeader( scope.requestHeader ); loader.setWithCredentials( scope.withCredentials ); + loader.setAbortSignal( scope.abortSignal ); + loader.load( url, function ( buffer ) { try { diff --git a/examples/jsm/loaders/MDDLoader.js b/examples/jsm/loaders/MDDLoader.js index 1f1007fbc43d84..a64a5dbdd4a534 100644 --- a/examples/jsm/loaders/MDDLoader.js +++ b/examples/jsm/loaders/MDDLoader.js @@ -33,6 +33,9 @@ class MDDLoader extends Loader { const loader = new FileLoader( this.manager ); loader.setPath( this.path ); loader.setResponseType( 'arraybuffer' ); + loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); + loader.load( url, function ( data ) { onLoad( scope.parse( data ) ); diff --git a/examples/jsm/loaders/MMDLoader.js b/examples/jsm/loaders/MMDLoader.js index 9e00f5bedc12ba..9326c703eb6e0f 100644 --- a/examples/jsm/loaders/MMDLoader.js +++ b/examples/jsm/loaders/MMDLoader.js @@ -110,7 +110,9 @@ class MMDLoader extends Loader { */ load( url, onLoad, onProgress, onError ) { - const builder = this.meshBuilder.setCrossOrigin( this.crossOrigin ); + const builder = this.meshBuilder + .setCrossOrigin( this.crossOrigin ) + .setAbortSignal( this.abortSignal ); // resource path @@ -223,6 +225,7 @@ class MMDLoader extends Loader { .setResponseType( 'arraybuffer' ) .setRequestHeader( this.requestHeader ) .setWithCredentials( this.withCredentials ) + .setAbortSignal( this.abortSignal ) .load( url, function ( buffer ) { onLoad( parser.parsePmd( buffer, true ) ); @@ -249,6 +252,7 @@ class MMDLoader extends Loader { .setResponseType( 'arraybuffer' ) .setRequestHeader( this.requestHeader ) .setWithCredentials( this.withCredentials ) + .setAbortSignal( this.abortSignal ) .load( url, function ( buffer ) { onLoad( parser.parsePmx( buffer, true ) ); @@ -280,7 +284,8 @@ class MMDLoader extends Loader { .setPath( this.animationPath ) .setResponseType( 'arraybuffer' ) .setRequestHeader( this.requestHeader ) - .setWithCredentials( this.withCredentials ); + .setWithCredentials( this.withCredentials ) + .setAbortSignal( this.abortSignal ); for ( let i = 0, il = urls.length; i < il; i ++ ) { @@ -315,6 +320,7 @@ class MMDLoader extends Loader { .setResponseType( 'text' ) .setRequestHeader( this.requestHeader ) .setWithCredentials( this.withCredentials ) + .setAbortSignal( this.abortSignal ) .load( url, function ( text ) { onLoad( parser.parseVpd( text, true ) ); @@ -390,6 +396,7 @@ class MeshBuilder { constructor( manager ) { this.crossOrigin = 'anonymous'; + this.abortSignal = null; this.geometryBuilder = new GeometryBuilder(); this.materialBuilder = new MaterialBuilder( manager ); @@ -406,6 +413,17 @@ class MeshBuilder { } + /** + * @param {AbortSignal} abortSignal + * @return {MeshBuilder} + */ + setAbortSignal( abortSignal ) { + + this.abortSignal = abortSignal; + return this; + + } + /** * @param {Object} data - parsed PMD/PMX data * @param {string} resourcePath @@ -418,6 +436,7 @@ class MeshBuilder { const geometry = this.geometryBuilder.build( data ); const material = this.materialBuilder .setCrossOrigin( this.crossOrigin ) + .setAbortSignal( this.abortSignal ) .setResourcePath( resourcePath ) .build( data, geometry, onProgress, onError ); @@ -1046,6 +1065,7 @@ class MaterialBuilder { this.tgaLoader = null; // lazy generation this.crossOrigin = 'anonymous'; + this.abortSignal = null; this.resourcePath = undefined; } @@ -1061,6 +1081,17 @@ class MaterialBuilder { } + /** + * @param {AbortSignal} abortSignal + * @return {MaterialBuilder} + */ + setAbortSignal( abortSignal ) { + + this.abortSignal = abortSignal; + return this; + + } + /** * @param {string} resourcePath * @return {MaterialBuilder} @@ -1086,6 +1117,7 @@ class MaterialBuilder { const textures = {}; this.textureLoader.setCrossOrigin( this.crossOrigin ); + this.textureLoader.setAbortSignal( this.abortSignal ); // materials @@ -1348,6 +1380,9 @@ class MaterialBuilder { } + this.tgaLoader.setWithCredentials( this.crossOrigin === 'use-credentials' ); + this.tgaLoader.setAbortSignal( this.abortSignal ); + return this.tgaLoader; } diff --git a/examples/jsm/loaders/MTLLoader.js b/examples/jsm/loaders/MTLLoader.js index fba96470d7dd5c..44edd1b5b58f51 100644 --- a/examples/jsm/loaders/MTLLoader.js +++ b/examples/jsm/loaders/MTLLoader.js @@ -46,6 +46,8 @@ class MTLLoader extends Loader { loader.setPath( this.path ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); + loader.load( url, function ( text ) { try { diff --git a/examples/jsm/loaders/NRRDLoader.js b/examples/jsm/loaders/NRRDLoader.js index 512d6d11a0ea56..e02d386dcb3f9d 100644 --- a/examples/jsm/loaders/NRRDLoader.js +++ b/examples/jsm/loaders/NRRDLoader.js @@ -24,6 +24,8 @@ class NRRDLoader extends Loader { loader.setResponseType( 'arraybuffer' ); loader.setRequestHeader( scope.requestHeader ); loader.setWithCredentials( scope.withCredentials ); + loader.setAbortSignal( scope.abortSignal ); + loader.load( url, function ( data ) { try { diff --git a/examples/jsm/loaders/NodeMaterialLoader.js b/examples/jsm/loaders/NodeMaterialLoader.js index 8a64d60db6b3f3..7ed3b655745096 100644 --- a/examples/jsm/loaders/NodeMaterialLoader.js +++ b/examples/jsm/loaders/NodeMaterialLoader.js @@ -25,6 +25,10 @@ class NodeMaterialLoader extends Loader { const loader = new FileLoader( scope.manager ); loader.setPath( scope.path ); + loader.setRequestHeader( scope.requestHeader ); + loader.setWithCredentials( scope.withCredentials ); + loader.setAbortSignal( scope.abortSignal ); + loader.load( url, function ( text ) { onLoad( scope.parse( JSON.parse( text ) ) ); diff --git a/examples/jsm/loaders/OBJLoader.js b/examples/jsm/loaders/OBJLoader.js index 883bd194cd74a9..578fb6f8c89988 100644 --- a/examples/jsm/loaders/OBJLoader.js +++ b/examples/jsm/loaders/OBJLoader.js @@ -447,6 +447,8 @@ class OBJLoader extends Loader { loader.setPath( this.path ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); + loader.load( url, function ( text ) { try { diff --git a/examples/jsm/loaders/PCDLoader.js b/examples/jsm/loaders/PCDLoader.js index dd48c091ddb549..c3125b2cd0a8cd 100644 --- a/examples/jsm/loaders/PCDLoader.js +++ b/examples/jsm/loaders/PCDLoader.js @@ -27,6 +27,8 @@ class PCDLoader extends Loader { loader.setResponseType( 'arraybuffer' ); loader.setRequestHeader( scope.requestHeader ); loader.setWithCredentials( scope.withCredentials ); + loader.setAbortSignal( scope.abortSignal ); + loader.load( url, function ( data ) { try { diff --git a/examples/jsm/loaders/PDBLoader.js b/examples/jsm/loaders/PDBLoader.js index 2477969e61f034..3b685edea64668 100644 --- a/examples/jsm/loaders/PDBLoader.js +++ b/examples/jsm/loaders/PDBLoader.js @@ -21,6 +21,8 @@ class PDBLoader extends Loader { loader.setPath( scope.path ); loader.setRequestHeader( scope.requestHeader ); loader.setWithCredentials( scope.withCredentials ); + loader.setAbortSignal( scope.abortSignal ); + loader.load( url, function ( text ) { try { diff --git a/examples/jsm/loaders/PLYLoader.js b/examples/jsm/loaders/PLYLoader.js index 759652e1dc3d94..2583b5ab3f8385 100644 --- a/examples/jsm/loaders/PLYLoader.js +++ b/examples/jsm/loaders/PLYLoader.js @@ -52,6 +52,8 @@ class PLYLoader extends Loader { loader.setResponseType( 'arraybuffer' ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); + loader.load( url, function ( text ) { try { diff --git a/examples/jsm/loaders/PRWMLoader.js b/examples/jsm/loaders/PRWMLoader.js index 46ed2bfee114c1..c5c73f8d7dc1c2 100644 --- a/examples/jsm/loaders/PRWMLoader.js +++ b/examples/jsm/loaders/PRWMLoader.js @@ -236,6 +236,7 @@ class PRWMLoader extends Loader { loader.setResponseType( 'arraybuffer' ); loader.setRequestHeader( scope.requestHeader ); loader.setWithCredentials( scope.withCredentials ); + loader.setAbortSignal( scope.abortSignal ); url = url.replace( /\*/g, isBigEndianPlatform() ? 'be' : 'le' ); diff --git a/examples/jsm/loaders/STLLoader.js b/examples/jsm/loaders/STLLoader.js index 00d91d313f3612..b78eae73001cae 100644 --- a/examples/jsm/loaders/STLLoader.js +++ b/examples/jsm/loaders/STLLoader.js @@ -77,6 +77,7 @@ class STLLoader extends Loader { loader.setResponseType( 'arraybuffer' ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); loader.load( url, function ( text ) { diff --git a/examples/jsm/loaders/SVGLoader.js b/examples/jsm/loaders/SVGLoader.js index 98727fc13f9b8b..8138e1012352cf 100644 --- a/examples/jsm/loaders/SVGLoader.js +++ b/examples/jsm/loaders/SVGLoader.js @@ -35,6 +35,8 @@ class SVGLoader extends Loader { loader.setPath( scope.path ); loader.setRequestHeader( scope.requestHeader ); loader.setWithCredentials( scope.withCredentials ); + loader.setAbortSignal( scope.abortSignal ); + loader.load( url, function ( text ) { try { diff --git a/examples/jsm/loaders/TDSLoader.js b/examples/jsm/loaders/TDSLoader.js index 82a1e98a5736e6..0f8709d86b4331 100644 --- a/examples/jsm/loaders/TDSLoader.js +++ b/examples/jsm/loaders/TDSLoader.js @@ -58,6 +58,7 @@ class TDSLoader extends Loader { loader.setResponseType( 'arraybuffer' ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); loader.load( url, function ( data ) { diff --git a/examples/jsm/loaders/TTFLoader.js b/examples/jsm/loaders/TTFLoader.js index 42d33ab64b8eec..d7a0ccd4166e80 100644 --- a/examples/jsm/loaders/TTFLoader.js +++ b/examples/jsm/loaders/TTFLoader.js @@ -29,6 +29,8 @@ class TTFLoader extends Loader { loader.setResponseType( 'arraybuffer' ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); + loader.load( url, function ( buffer ) { try { diff --git a/examples/jsm/loaders/TiltLoader.js b/examples/jsm/loaders/TiltLoader.js index 32716a1cea0e81..4bef8aa2fea750 100644 --- a/examples/jsm/loaders/TiltLoader.js +++ b/examples/jsm/loaders/TiltLoader.js @@ -24,6 +24,7 @@ class TiltLoader extends Loader { loader.setPath( this.path ); loader.setResponseType( 'arraybuffer' ); loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); loader.load( url, function ( buffer ) { diff --git a/examples/jsm/loaders/VOXLoader.js b/examples/jsm/loaders/VOXLoader.js index 16247f7a8c4fc1..32bcb250815d5a 100644 --- a/examples/jsm/loaders/VOXLoader.js +++ b/examples/jsm/loaders/VOXLoader.js @@ -21,6 +21,8 @@ class VOXLoader extends Loader { loader.setPath( scope.path ); loader.setResponseType( 'arraybuffer' ); loader.setRequestHeader( scope.requestHeader ); + loader.setAbortSignal( scope.abortSignal ); + loader.load( url, function ( buffer ) { try { diff --git a/examples/jsm/loaders/VRMLLoader.js b/examples/jsm/loaders/VRMLLoader.js index e31b898db55d13..31cecccc995b9b 100644 --- a/examples/jsm/loaders/VRMLLoader.js +++ b/examples/jsm/loaders/VRMLLoader.js @@ -63,6 +63,8 @@ class VRMLLoader extends Loader { loader.setPath( scope.path ); loader.setRequestHeader( scope.requestHeader ); loader.setWithCredentials( scope.withCredentials ); + loader.setAbortSignal( scope.abortSignal ); + loader.load( url, function ( text ) { try { @@ -3189,7 +3191,9 @@ class VRMLLoader extends Loader { // const textureLoader = new TextureLoader( this.manager ); - textureLoader.setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin ); + textureLoader.setPath( this.resourcePath || path ); + textureLoader.setCrossOrigin( this.crossOrigin ); + textureLoader.setAbortSignal( this.abortSignal ); // check version (only 2.0 is supported) diff --git a/examples/jsm/loaders/VRMLoader.js b/examples/jsm/loaders/VRMLoader.js index cf8c87e1e4bed9..341a76d71d76ec 100644 --- a/examples/jsm/loaders/VRMLoader.js +++ b/examples/jsm/loaders/VRMLoader.js @@ -28,6 +28,10 @@ class VRMLoader extends Loader { const scope = this; + this.gltfLoader.setRequestHeader( this.requestHeader ); + this.gltfLoader.setWithCredentials( this.withCredentials ); + this.gltfLoader.setAbortSignal( this.abortSignal ); + this.gltfLoader.load( url, function ( gltf ) { try { diff --git a/examples/jsm/loaders/VTKLoader.js b/examples/jsm/loaders/VTKLoader.js index 11004ed31bb0c2..a614f70c1b4bc3 100644 --- a/examples/jsm/loaders/VTKLoader.js +++ b/examples/jsm/loaders/VTKLoader.js @@ -25,6 +25,8 @@ class VTKLoader extends Loader { loader.setResponseType( 'arraybuffer' ); loader.setRequestHeader( scope.requestHeader ); loader.setWithCredentials( scope.withCredentials ); + loader.setAbortSignal( scope.abortSignal ); + loader.load( url, function ( text ) { try { diff --git a/examples/jsm/loaders/XYZLoader.js b/examples/jsm/loaders/XYZLoader.js index e7df119d2827bd..a8f813f9921a34 100644 --- a/examples/jsm/loaders/XYZLoader.js +++ b/examples/jsm/loaders/XYZLoader.js @@ -15,6 +15,8 @@ class XYZLoader extends Loader { loader.setPath( this.path ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); + loader.load( url, function ( text ) { try { diff --git a/src/loaders/AnimationLoader.js b/src/loaders/AnimationLoader.js index 661dab954034cd..e814002990ef49 100644 --- a/src/loaders/AnimationLoader.js +++ b/src/loaders/AnimationLoader.js @@ -18,6 +18,8 @@ class AnimationLoader extends Loader { loader.setPath( this.path ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); + loader.load( url, function ( text ) { try { diff --git a/src/loaders/AudioLoader.js b/src/loaders/AudioLoader.js index 857394e4812f71..0d6a26dc0bc5d4 100644 --- a/src/loaders/AudioLoader.js +++ b/src/loaders/AudioLoader.js @@ -19,6 +19,8 @@ class AudioLoader extends Loader { loader.setPath( this.path ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); + loader.load( url, function ( buffer ) { try { diff --git a/src/loaders/BufferGeometryLoader.js b/src/loaders/BufferGeometryLoader.js index 5e9b19dfacfa48..1f112e800f5b02 100644 --- a/src/loaders/BufferGeometryLoader.js +++ b/src/loaders/BufferGeometryLoader.js @@ -26,6 +26,8 @@ class BufferGeometryLoader extends Loader { loader.setPath( scope.path ); loader.setRequestHeader( scope.requestHeader ); loader.setWithCredentials( scope.withCredentials ); + loader.setAbortSignal( this.abortSignal ); + loader.load( url, function ( text ) { try { diff --git a/src/loaders/CompressedTextureLoader.js b/src/loaders/CompressedTextureLoader.js index 02ef233412d314..05aa3135e7a032 100644 --- a/src/loaders/CompressedTextureLoader.js +++ b/src/loaders/CompressedTextureLoader.js @@ -30,6 +30,7 @@ class CompressedTextureLoader extends Loader { loader.setResponseType( 'arraybuffer' ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( scope.withCredentials ); + loader.setAbortSignal( this.abortSignal ); let loaded = 0; diff --git a/src/loaders/CubeTextureLoader.js b/src/loaders/CubeTextureLoader.js index 2e784a221e8d27..a1daa480a0ab20 100644 --- a/src/loaders/CubeTextureLoader.js +++ b/src/loaders/CubeTextureLoader.js @@ -17,6 +17,7 @@ class CubeTextureLoader extends Loader { const loader = new ImageLoader( this.manager ); loader.setCrossOrigin( this.crossOrigin ); loader.setPath( this.path ); + loader.setAbortSignal( this.abortSignal ); let loaded = 0; diff --git a/src/loaders/DataTextureLoader.js b/src/loaders/DataTextureLoader.js index e584dbb9412ac8..bd46a65d01351d 100644 --- a/src/loaders/DataTextureLoader.js +++ b/src/loaders/DataTextureLoader.js @@ -27,7 +27,9 @@ class DataTextureLoader extends Loader { loader.setResponseType( 'arraybuffer' ); loader.setRequestHeader( this.requestHeader ); loader.setPath( this.path ); - loader.setWithCredentials( scope.withCredentials ); + loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); + loader.load( url, function ( buffer ) { const texData = scope.parse( buffer ); diff --git a/src/loaders/FileLoader.js b/src/loaders/FileLoader.js index b42e55bc752ebf..b9ea91a84ca5f7 100644 --- a/src/loaders/FileLoader.js +++ b/src/loaders/FileLoader.js @@ -66,11 +66,12 @@ class FileLoader extends Loader { const req = new Request( url, { headers: new Headers( this.requestHeader ), credentials: this.withCredentials ? 'include' : 'same-origin', - // An abort controller could be added within a future PR } ); // start the fetch - fetch( req ) + fetch( req, { + signal: this.abortSignal, + } ) .then( response => { if ( response.status === 200 || response.status === 0 ) { @@ -128,7 +129,12 @@ class FileLoader extends Loader { } - } ); + } ) + .catch( ( err ) => { + + onError( err ); + + }); } diff --git a/src/loaders/ImageBitmapLoader.js b/src/loaders/ImageBitmapLoader.js index 2a9d15a3742840..ab7e58ec8ae2a8 100644 --- a/src/loaders/ImageBitmapLoader.js +++ b/src/loaders/ImageBitmapLoader.js @@ -59,11 +59,14 @@ class ImageBitmapLoader extends Loader { } - const fetchOptions = {}; - fetchOptions.credentials = ( this.crossOrigin === 'anonymous' ) ? 'same-origin' : 'include'; - fetchOptions.headers = this.requestHeader; + const req = new Request( url, { + credentials: ( this.crossOrigin === 'anonymous' ) ? 'same-origin' : 'include', + headers: new Headers( this.requestHeader ), + } ); - fetch( url, fetchOptions ).then( function ( res ) { + fetch( req, { + signal: this.abortSignal, + } ).then( function ( res ) { return res.blob(); diff --git a/src/loaders/ImageLoader.js b/src/loaders/ImageLoader.js index 48a98c0f61e17b..5b09d6a6c8a053 100644 --- a/src/loaders/ImageLoader.js +++ b/src/loaders/ImageLoader.js @@ -54,23 +54,58 @@ class ImageLoader extends Loader { removeEventListeners(); - if ( onError ) onError( event ); + if ( onError ) { + + if ( this.abortSignal && this.abortSignal.aborted ) { + + // Simulate an error similar to the DOMException thrown by the Fetch API + // (DOMException is not instanciable) + const e = new Error(); + e.name = 'AbortError'; + e.message = 'The operation was aborted.'; + onError( e ); + + } else { + + onError( event ); + + } + + } scope.manager.itemError( url ); scope.manager.itemEnd( url ); } + function onAbortSignal() { + + image.src = ''; + + } + function removeEventListeners() { image.removeEventListener( 'load', onImageLoad, false ); image.removeEventListener( 'error', onImageError, false ); + if ( this.abortSignal ) { + + this.abortSignal.removeEventListener( 'abort', onAbortSignal, false ); + + } + } image.addEventListener( 'load', onImageLoad, false ); image.addEventListener( 'error', onImageError, false ); + if ( this.abortSignal ) { + + this.abortSignal.addEventListener( 'abort', onAbortSignal, false ); + + } + if ( url.substr( 0, 5 ) !== 'data:' ) { if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; diff --git a/src/loaders/Loader.js b/src/loaders/Loader.js index 3d161b5d6f62b5..62740e39210cfc 100644 --- a/src/loaders/Loader.js +++ b/src/loaders/Loader.js @@ -11,6 +11,7 @@ class Loader { this.path = ''; this.resourcePath = ''; this.requestHeader = {}; + this.abortSignal = null; } @@ -65,6 +66,13 @@ class Loader { } + setAbortSignal( abortSignal ) { + + this.abortSignal = abortSignal; + return this; + + } + } export { Loader }; diff --git a/src/loaders/MaterialLoader.js b/src/loaders/MaterialLoader.js index 2af3967528bfc6..4d6f34e3e54b13 100644 --- a/src/loaders/MaterialLoader.js +++ b/src/loaders/MaterialLoader.js @@ -25,6 +25,8 @@ class MaterialLoader extends Loader { loader.setPath( scope.path ); loader.setRequestHeader( scope.requestHeader ); loader.setWithCredentials( scope.withCredentials ); + loader.setAbortSignal( this.abortSignal ); + loader.load( url, function ( text ) { try { diff --git a/src/loaders/ObjectLoader.js b/src/loaders/ObjectLoader.js index f263ad885f10b1..9dfbc18e574e52 100644 --- a/src/loaders/ObjectLoader.js +++ b/src/loaders/ObjectLoader.js @@ -79,6 +79,8 @@ class ObjectLoader extends Loader { loader.setPath( this.path ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); + loader.load( url, function ( text ) { let json = null; @@ -123,6 +125,7 @@ class ObjectLoader extends Loader { loader.setPath( this.path ); loader.setRequestHeader( this.requestHeader ); loader.setWithCredentials( this.withCredentials ); + loader.setAbortSignal( this.abortSignal ); const text = await loader.loadAsync( url, onProgress ); @@ -451,6 +454,7 @@ class ObjectLoader extends Loader { loader = new ImageLoader( manager ); loader.setCrossOrigin( this.crossOrigin ); + loader.setAbortSignal( this.abortSignal ); for ( let i = 0, il = json.length; i < il; i ++ ) { @@ -550,6 +554,7 @@ class ObjectLoader extends Loader { loader = new ImageLoader( this.manager ); loader.setCrossOrigin( this.crossOrigin ); + loader.setAbortSignal( this.abortSignal ); for ( let i = 0, il = json.length; i < il; i ++ ) { diff --git a/src/loaders/TextureLoader.js b/src/loaders/TextureLoader.js index 4adc1ec8fa4aed..6ceccdfb88db21 100644 --- a/src/loaders/TextureLoader.js +++ b/src/loaders/TextureLoader.js @@ -17,6 +17,7 @@ class TextureLoader extends Loader { const loader = new ImageLoader( this.manager ); loader.setCrossOrigin( this.crossOrigin ); loader.setPath( this.path ); + loader.setAbortSignal( this.abortSignal ); loader.load( url, function ( image ) {