Nodragem

How to Load Synchronously a Text/CSV/JSON file?

Recommended Posts

I would like to be able to load a text file (or json, csv, and other text-based files) as simply as I do with textures. Something like that

this.dictionary = new BABYLON.TextFile("./assets/data/dictionary.txt");
console.log(this.dictionary);

I would like  my text file to de loaded synchronously so that I am sure I can read its content once I run the line that loaded it.

But it seems that there is no way to do so 😕 that just drives me crazy.

Share this post


Link to post
Share on other sites

LoadFile is what gets run for things like a .babylon or sound file.  The second arg is a success  function callback.  You cannot really do any synchronous file loading except for script & other files listed in a html file.  Even then, it is not really synchronous, just performed before control is passed.  Do Something like:

BABYLON.Tools.LoadFile("./assets/data/dictionary.txt", (data) => {
     this.dictionary = data;
     console.log(data);
});

 

Share this post


Link to post
Share on other sites

You can do this this way to load a json file synchronously (with $.ajaxSetup({ async: false});)

function getJson() {
    var text = "";
    $.ajaxSetup({ async: false});
    $.getJSON(dictionary.json?" + Date.now(), (data) => {
        text = data;
    });
    $.ajaxSetup({ async: true});
    return text;
}

var myText = getJson();

 

Share this post


Link to post
Share on other sites
1 hour ago, JCPalmer said:

LoadFile is what gets run for things like a .babylon or sound file.  The second arg is a success  function callback.  You cannot really do any synchronous file loading except for script & other files listed in a html file.  Even then, it is not really synchronous, just performed before control is passed.  Do Something like:


BABYLON.Tools.LoadFile("./assets/data/dictionary.txt", (data) => {
     this.dictionary = data;
     console.log(data);
});

 

The issue of this solution is that this is not synchronous. Hence when I try to use my dictionary from an other object, I get an error because the dictionary is still "undefined".

@Dad72 I am new in web development, I am using Typescript. Is there big drawback in using jQuery? In term of memory usage and mobile web?

Thank you for your answers! :)

Share this post


Link to post
Share on other sites

No, there is no inconvenience, Jquery is very good, performing, but I do not know how to use it with typescript, but I guess it should not be a problem. try.

Share this post


Link to post
Share on other sites

@JohnK That's not really synchronous, but yes, I thought of a similar solution. Ultimately I thought I will have to use the AssetManager and start the game when it is loaded and then access the assets by name from the Scene...

However, you can't access a Text-based asset by name from the Scene. Here is the list of things you can retrieve by name:

image.png.a820ca5a016f0774592546642d57345c.png

 

Async / Await as a solution

I started to play with async / await, which are very similar to Coroutine / Yield in C# (or python).

Basically you can start an "async" function (Coroutine), in which you can use "await" to block the code execution until a Promise is resolved (Yield).

Instead of using BABYLON.Tools.loadFile(), I use d3-fetch, which is part of d3.js, a library for handling and visualizing data. As d3-fetch is based on fetch API and Promises, it is compatible with async / await. As far as I understand, BABYLON.Tools.LoadFile() returns a IFileRequest and hence, it is not compatible with async / await.

Here is the code I came up with:

class LetterData {
    
    letterTable:any;
    
    async loadData(){
        let s:any = await d3f.csv("./assets/data/letter-frequency.csv");
        this.letterTable = s;
    }   
}

class Game {
    constructor(canvasElement : string) {
        // Create canvas and engine.
        this._canvas = document.getElementById(canvasElement) as HTMLCanvasElement;
        this._engine = new BABYLON.Engine(this._canvas, true);
        
        this.start(); // this is equivalent to a Coroutine

        // I could display a loading screen here in parallel to this.start() ...      
    }

    async start(){
        // here every single functions wait for the previous one to be terminated before to start
        await this.initScene();
        await this.loadUI(); // we are 100% sure that the scene was loaded when we start making the UI
        await this.initRendering(); 
    }

 
    async initScene() {
        console.log("Loading...")
        this._scene = new BABYLON.Scene(this._engine);
        await new Promise((resolve, reject) => {
            window.setTimeout(() => resolve("simulating loading time here!"), 1000)
          });  
        console.log("Loading done!")  
    }

    
    async loadUI() {        
        let letterData = new LetterData();
        await letterData.loadData();
        // I am 100% sure that the data is accessible now:
        console.log(letterData.getEnglishLetterDistribution());

    }

    async initRendering() {}
}

 

The only drawback on this await / async solution is that I need to add "es2015" to "lib" in my tsconfig.json. After this change, "console.log" was not recognised by typescript anymore and I needed to add "dom" to "lib". One drawback I can think of is that I have no idea if that makes the code incompatible with some browsers, or make the dependencies heavier. But I can say for definite that await / async is a very nice things to have for game dev (to create a game loop coroutine for instance).

EDIT: So I checked, and it seems we can add lib = ["es2015", "dom"] to the tsconfig.json without messing up compatibility with browsers, as the target stays the same. ... Any one to confirm?

Share this post


Link to post
Share on other sites

async await WON'T work on old browsers (but most will be fine)

as an intermediate solution you can uses promises (Babylon.js will provide a fallback for you)

 

so instead of await loadData() you will end up wtih loadData().then(...) but at least it will work even on ie 11

Share this post


Link to post
Share on other sites

Actually I found this link in the documentation which speaks about Async/Await: https://doc.babylonjs.com/how_to/promises

On 11/5/2018 at 6:26 PM, Deltakosh said:

async await WON'T work on old browsers (but most will be fine)

as an intermediate solution you can uses promises (Babylon.js will provide a fallback for you)

 

so instead of await loadData() you will end up wtih loadData().then(...) but at least it will work even on ie 11

I am not sure that .then(...) avoids "having to deal with pyramids of callbacks intricated in a non easy to maintain way." (cited from the documentation link above).

I get the impression I will end up doing Promise.then().then().then().then() 😕 

So... I guess that means I won't support old browsers 😕 thank you for letting me know!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.