Search code examples
javascriptasynchronousxmlhttprequestrequirejsamd

Async request in library which support AMD


I have a problem with a library I want to build because of async request I made with XMLHttpRequest. I will explain with some code :

(function(root,factory){
    if( typeof define === "function" && define.amd ){
        // AMD module
        define([], factory);
    }else{
        global.tof = factory();
    }
})(this, () => {
    const obj = {
        datas: {},
        get: function(){}
        ...
    }
    const url = "...";
    let request = new XMLHttpRequest();
    request.open('GET',url);
    request.responseType = 'json';
    request.send();
    request.onload= function(){
        obj.datas = request.response;
    }
    return obj;
});

I worked with callback to be sure I receive data and define my module when data has loaded but when I load the "library" with requireJS it still fires before my module is really defined.

(function(root,factory){

    function myCallback(r){

        if( typeof define === "function" && define.amd ){
            // AMD module
            define('tof',[], factory(function(r){
                return r;
            }));
        }
    }
    factory(myCallback);

})(this, function foo(callback){
    const obj = {
        datas: {},
        get: function(){}
        ...
    }
    const url = "...";
    let request = new XMLHttpRequest();
    request.open('GET',url);
    request.responseType = 'json';
    request.send();
    request.onload= function(){
        obj.datas = request.response;
        callback(obj);
    }
});

My other script I used to test my module with requireJS :

require(['tof'], function(tof){
    // return undefined because of my async request
    console.log(tof);
});

How could I resolve this?


Solution

  • I'm sure you quickly figured out your 1st attempt cannot work, since you cannot use a synchronous return to return a value obtained asynchronously.

    Your 2nd attempt cannot work either. It looks like you'd like the factory function you pass to define to define a module asynchronously but RequireJS does not allow that. What you need to do is design your module in a way that it provides a function that allows the user of the module to retrieve the result of the XMLHTTPRequest asynchronously.

    Here's a sketch of some possibility. I emphasize that this is a sketch. I typed this off-the-cuff. At the very least, you'll need to add error handling code:

    define(function () {
        const promise =  new Promise((resolve, reject) => {
            const request = new XMLHttpRequest();
            request.open('GET',url);
            request.responseType = 'json';
            request.send();
            request.onload = () => {
                obj.datas = request.response;
                resolve(obj);
            };
        });
    
        return {
            getResult: () => promise,
        };
    });
    

    Then you'd use it elsewhere like:

    require(['tof'], function (tof) {
        tof.getResult().then((result) => console.log(result));
    });