异步加载模块属性

时间:2014-01-27 20:12:23

标签: javascript requirejs amd ractivejs

我已经定义了一个模块(module1),它应该异步加载属性的值。如何在定义定义后立即在我的应用中使用此属性?

我的设置(简化)

V1

app.js

require(['module1'], function(mod) {
    document.getElementById("greeting").value = mod.getPersonName();
});

module1.js

define(['jquery'], function($) {
    _person;

    $.get('values/person.json')
        .done(function(data) {
            _person = data
        });

    return {
        getPersonName: function() { return _person.name; }
    }

值/ person.json

{ name: 'John Doe', age: 34 }    

这仅在GET几乎瞬间发生时有效,否则失败,因为在调用getPersonName时_person未定义。

V2

为了解决这个问题,我想我会注册一个回调来在人员加载时通知应用程序。

app.js

require(['module1'], function(mod) {
    mod.onPersonLoaded(function() {
        document.getElementById("greeting").value = mod.getPersonName();
    });
});

module1.js

define(['jquery'], function($) {
    _person;
    _onLoaded;

    $.get('values/person.json')
        .done(function(data) {
            _person = data;
            _onLoaded();
        });

    return {
        getPersonName: function() { return _person.name; },
        onPersonLoaded: function(cb) { _onLoaded = cb; }
    }
}

如果GET很慢,这是有效的,但是,如果在调用.done()时快速_onLoaded是未定义的。

有没有一种很好的方法可以在定义app_js时立即使用_person值,只有定义它们一次?

我正在使用RequireJS,但我的问题通常适用于AMD。

修改

在简化我的示例中,我删除了一个可能很重要的图层。我正在使用RactiveJS作为用户界面。

设置(略微简化)

app.js

require(['Ractive', 'module1'], function(Ractive, mod) {

    var ractive = new Ractive({
        ...
        data : {
            name: mod.getPersonName()
        }
    });

    ractive.observe(...);
    ractive.on(...);

});

编辑2

我目前的解决方案,可能会有变化。注册一个在人员加载时通知app.js的回调。如果在注册回调时已经加载了人,则立即调用回调。

app.js

require(['Ractive', 'module1'], function(Ractive, mod) {

    var ractive = new Ractive({
        ...
        data : {}
    });

    mod.watchPerson(function() {
        ractive.set('person.name', mod.getPersonName());
    });

    ractive.observe(...);
    ractive.on(...);

});

module1.js

define(['jquery'], function($) {
    _person;
    _onLoaded;

    $.get('values/person.json')
        .done(function(data) {
            _person = data;
            try {
                _onLoaded();
            } catch (e) {
                // that was fast!
                // callback will be called when it is registered
        });

    return {
        getPersonName: function() { return _person.name; },
        watchPerson: function(cb) { 
            _onLoaded = cb;
            if(_person != null) {
                _onLoaded();
            }
        }
    }
}

2 个答案:

答案 0 :(得分:2)

Promise是一个很好的选择,因为回调是始终异步调用 - 你永远不会遇到_onLoaded()在设计之前被调用的混乱局面。

不幸的是,jQuery的承诺实现不符合Promises/A+ specification,因此您无法获得该保证。如果可能,您可以使用替代的AJAX库,如Reqwest,并执行类似

的操作

<强> app.js

define(['module1'], function (mod) {
  mod.then(function(person) {
    // do something with person.name
  }
});

<强> module1.js

define(['reqwest'], function (reqwest) {
  return reqwest('values/person.json');
});

使用文本加载器插件

另一个选择,因为您已经在使用AMD,将使用加载程序插件,例如text

<强> app.js

define(['module1'], function (person) {
  // do something with person.name
});

<强> module1.js

define(['text!values/person.json'], function (personJSON) {
  return JSON.parse(personJSON);
});

使用文本加载器插件

实际上甚至还有一个JSON plugin,因此在这个示例情况下你可以完全取消module1:

<强> app.js

define(['json!values/person'], function (person) {
  // do something with person.name
});

答案 1 :(得分:0)

我就是这样做的。基本上,它与你的V2没什么不同,但它增加了更多的包装。

<强> app.js

require(['module1'], function(mod) {
    mod.loadPerson(function(person) {
        document.getElementById("greeting").value = person.getPersonName();
    });
});

<强> module1.js

define(['jquery'], function($) {
    return {
        loadPerson : function(callback) {
            $.get('values/person.json').done(function(data) {
                  _person = data;

                 callback({
                    getPersonName: function() { return _person.name; }
                 });
            });

        }
    }
}

你也可以使用promises promises而不是简单的回调。