Dose pixi.js can provide a async parallel loader ?
See original GitHub issueHi~ everyone. I’m a new user on pixijs.
I learned that the pixi.loaders.Loader
cannot load resources when the other are loading. and if one resources are loaded, load twice will throw a exception that cannot be catched.
So, Is it able to provide a async parallel loader that can cache the parallel load task then serialize execute it?
follow is my simple and ugly implement on a demo project.
###the resourcesLoader class implement###
import * as pixi from "pixi.js";
import * as _ from "lodash";
class ResourcesLoaderParams {
loaderParams: string | any | any[];
promises: Promise<PIXI.loaders.Resource>[] = [];
resolves: ((value?: PIXI.loaders.Resource | PromiseLike<PIXI.loaders.Resource>) => void)[] = [];
rejects: ((reason?: any) => void)[] = [];
}
export class resourcesLoader {
constructor(public loader: pixi.loaders.Loader) {
}
loaderOptions: pixi.loaders.LoaderOptions;
waitingList: ResourcesLoaderParams[] = [];
loadingList: ResourcesLoaderParams[] = [];
checkExist(urls: string): boolean {
return !_.isNil(this.loader.resources[urls]);
}
get(urls: string) {
return this.loader.resources[urls];
}
checkAndGet(urls: string): Promise<PIXI.loaders.Resource> {
if (this.checkExist(urls)) {
return Promise.resolve(this.get(urls));
}
return Promise.reject(this.get(urls));
}
loadResources(urls: string): Promise<PIXI.loaders.Resource> {
// trim existed
if (this.checkExist(urls)) {
return this.checkAndGet(urls);
}
// check is in loading or waiting, then, merge task
// otherwise, create new loading task
let Li = this.waitingList.find(T => T.loaderParams == urls);
if (_.isNil(Li)) {
Li = this.loadingList.find(T => T.loaderParams == urls);
}
let thisPromise = undefined;
if (!_.isNil(Li)) {
thisPromise = new Promise<PIXI.loaders.Resource>((resolve, reject) => {
Li.resolves.push(resolve);
Li.rejects.push(reject);
});
} else {
let p = new ResourcesLoaderParams();
p.loaderParams = urls;
thisPromise = new Promise<PIXI.loaders.Resource>((resolve, reject) => {
p.resolves.push(resolve);
p.rejects.push(reject);
});
p.promises.push(thisPromise);
if (this.waitingList.length == 0 && this.loadingList.length == 0) {
this.waitingList.push(p);
this.emitLoader();
} else {
this.waitingList.push(p);
}
}
return thisPromise;
}
private emitLoader() {
if (this.waitingList.length === 0) {
return;
}
let list: ResourcesLoaderParams[] = [];
let tempList = [];
if (_.isArray(this.waitingList[0].loaderParams)) {
list = [this.waitingList[0]];
for (let i = 1; i != this.waitingList.length; ++i) {
tempList.push(this.waitingList[i]);
}
} else {
// first item confident not array
let flag = false;
for (let i = 0; i != this.waitingList.length; ++i) {
if (!flag) {
if (_.isArray(this.waitingList[i].loaderParams)) {
--i;
flag = true;
continue;
}
list.push(this.waitingList[i]);
} else {
tempList.push(this.waitingList[i]);
}
}
}
this.waitingList = tempList;
this.loadingList = list;
// trim the loaded item
this.loadingList = this.loadingList.filter(T => {
if (this.checkExist(T.loaderParams)) {
T.resolves.forEach(Tr => Tr(this.get(T.loaderParams)));
return false;
}
return true;
});
if (this.loadingList.length === 0) {
if (this.waitingList.length !== 0) {
this.emitLoader();
}
return;
}
let param: any;
if (this.loadingList.length === 1) {
param = this.loadingList[0].loaderParams;
} else {
param = this.loadingList.map(T => T.loaderParams);
}
let loadingLoader = this.loader.add(param, this.loaderOptions).load(() => {
this.loadingList.forEach(T => {
console.log(T.loaderParams);
T.resolves.forEach(Tr => Tr(this.loader.resources[T.loaderParams]));
});
this.loadingList = [];
this.emitLoader();
});
// try catch error, example double load
// but seemingly cannot catch it
loadingLoader.on("error", () => {
this.loadingList.forEach(T => {
console.log(T.loaderParams);
T.rejects.forEach(Tr => Tr(T.loaderParams));
});
this.loadingList = [];
this.emitLoader();
});
loadingLoader.onError.once(() => {
this.loadingList.forEach(T => {
console.log(T.loaderParams);
T.rejects.forEach(Tr => Tr(T.loaderParams));
});
this.loadingList = [];
this.emitLoader();
});
}
}
follow is the usage:
parallel create load request and serialize load it then async call the promise “then” callback at the resource loaded immediately. and if a resource be loaded, the “resourcesLoader” will fast resolve that request with not wait. and if a resource are loading or waiting, the new same request will merge to the loading or waiting task.
this.loader = new resourcesLoader(pixi.loader);
this.loader.loadResources("/api/static/1.png").then(T => {
this.image1 = new pixi.Sprite(T.texture);
// ......
});
this.loader.loadResources("/api/static/2.png").then(T => {
this.image2 = new pixi.Sprite(T.texture);
// ......
});
this.loader.loadResources("/api/static/3.png").then(T => {
this.image3 = new pixi.Sprite(T.texture);
// ......
});
// ........
check and fast read/load resources
updateImage(imageUrl: string) {
this.loader.checkAndGet(imageUrl).catch(E => {
return this.loader.loadResources(imageUrl);
}).then(T => {
this.image = new pixi.Sprite(T.texture);
// .......
});
}
this simple implement on above only can load url now. but i think it can be upgrade to similar to the original loader API.
So, Can pixi.js offical be provide a async parallel loader like this?
Issue Analytics
- State:
- Created 6 years ago
- Comments:28 (5 by maintainers)
Top GitHub Comments
Yeps If you want to use the PIXI version with it’s own middleware, then you’d use
Loaders are very lightweight, so don’t worry about having multiple of them.
The only thing I’d be aware of, is that the loaders let you set how many assets it can concurrently download, which has a default of 10. If you had 3 loaders, you could be effectively trying to load up to 30 resources at once (tho I’m sure browser would limit lower than that). So maybe you’d create each loader with a lower limit? Or maybe your primary loader has a large limit to get the initial assets down, but your 'in-game-streaming- asset loaders have a lower limit to focus on getting each individual asset down faster.
@ivanpopelyshev I like to do that, but maybe no time give me to do it. in now day, this wrapper
resourcesLoader
are enough to this demo. and i will try to read the loader code on my free time. thank you very much.