question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Dose pixi.js can provide a async parallel loader ?

See original GitHub issue

Hi~ 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:closed
  • Created 6 years ago
  • Comments:28 (5 by maintainers)

github_iconTop GitHub Comments

3reactions
themoonratcommented, Feb 2, 2018

Yeps If you want to use the PIXI version with it’s own middleware, then you’d use

const loader = new PIXI.loaders.Loader();

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.

1reaction
Lyoko-Jeremiecommented, Feb 2, 2018

@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.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Dose pixi.js can provide a async parallel loader ? · Issue #4653
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...
Read more >
Loader is async, right? So not sure how you'd structure this
If your game is in a JS file, and you do var myGame = new CoolGame(); myGame.Init(); myGame.Start(); for example, then CoolGame does...
Read more >
PixiJS API Documentation
This interface describes all the DOM dependent calls that Pixi makes throughout its codebase. Implementations of this interface can be used to make...
Read more >
PixiJS: How to use its ResizePlugin - Stack Overflow
I'm using the latest PixiJS release (as of today). Is there something I need to do before calling the queueResize() function? javascript ·...
Read more >
PIXI.AsyncQueue
pixi.js. pixi.js-legacy. pixi.js-monorepo. @pixi/loaders > PIXI > AsyncQueue. PIXI.AsyncQueue. private class AsyncQueue ... eachSeries AsyncQueue.ts:252
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found