对由异步请求填充的结构的同步访问

时间:2013-02-12 15:47:22

标签: javascript jquery asynchronous

我正在尝试为JS / jQuery中实现的应用程序提供状态值字典。必须从服务器(ajax)获取值。我想以异步方式执行此操作,以便我可以在初始化期间启动该请求。实际访问值应该在以后同步完成,以保持代码易于理解。 (很多)简化代码目前看起来像这样:

初始化:

$(document).ready(function(){
    Status.fetch();
})

结构:

Status:{
    Valid:new $.Deferred(),
    Server:{},
    /* ... */
    fetch:function(){
        if (Status.Valid.isResolved()){
            // status already present due to a past request
            return Status.Valid.promise();
        }else{
            // fetch status information from server
            $.ajax({
                type:     'GET',
                url:      'status.php'),
                cache:    true,
                dataType: 'json'
            }).done(function(response){
                $.each(response,function(key,val){
                    Status.Server[key]=val;
                });
                Status.Valid.resolve(Status.Server);
            }).fail(function(response){
                Status.Valid.reject(NaN);
            });
            return Status.Valid.promise();
        }
    }, // Status.fetch
    getServerAspect:function(aspect){
        $.when(
            Status.fetch()
        ).done(function(server){
            return server[aspect];
        }).fail(function(server){
            return NaN;
        })
        /* whyever: processing always comes here, which I don't understand... */
    }, // Status.getServerAspect
} // Status

Status.Server被ajax调用填充(有效)。 Status.getServerAspect()是一个允许同步访问Status.Server结构中存储的方面(工作)的方法的示例。

基本思想是结构的异步填充在访问结构之前运行。每个(同步)访问都要么立即返回引用的方面,要么阻塞直到该值存在。但无论我在Status.getServerAspect()内尝试什么样的风格,该方法都会立即返回,而不会在调用范围内使用任何方面值。

我显然有一个问题需要理解使用延迟对象的基本概念。我一直用它们进行同步处理,就像魅力一样。但这意味着您必须在整个应用程序中使用异步样式。对于这个特殊功能,我更喜欢同步样式,以便条件表达式保持简单:

if ('somevalue'==Status.getServerAspect('someaspect'))
    ...do something...

在异步和同步处理之间构建“桥”的技巧是什么?

5 个答案:

答案 0 :(得分:1)

这将起作用,因为setTimeout(...)调用(异步)只是确保Status.Valid.state();呼叫并非常常执行。它仍然是一个繁忙的等待,它不仅会阻塞线程(因此不应该在WebWorker之外完成),而且还会从CPU中窃取空闲时间。事实上,不是使用这种方法,而是寻求同步AJAX请求是更好的解决方案。在阻止呼叫者的同时,它不会是忙碌的等待。但是,如果可以,您仍然应该进行纯粹的异步实现。

答案 1 :(得分:0)

让getServerAspect简单地返回promise,然后使用它。

getServerAspect: function(){
    return Status.Valid.promise();
}

现在您可以将其用于:

Status.getServerAspect().done(function(data){
    if (data.someaspect == 'somevalue') {
        // do something, but don't return!
    }
})

这是异步代码的工作方式。

你也可以在函数后面抽象它,但我发现它更难阅读。

Status.getServerAspect: function(aspect,callback){
    Status.Valid.promise().done(function(data){
        callback(data[aspect]);
    })
}

现在您可以使用它:

Status.getServerAspect('someaspect',function(value) {
    if ( 'somevalue' == value ) {
        // do something but don't return!
    }
});

答案 2 :(得分:0)

很难调用异步过程并尝试使其同步,最简单的方法是将功能分成两部分;一个在异步电话之前,一个在之后。

我找到的最好的方法是使用promise模式,或者你可以尝试一个中介并将一系列事件传递给你的过程的每一步触发:

Good pattern to use for multiple xmlhttprequests used by different processes

据我所知,JavaScript并没有像其他语言一样暂停或等待的功能。

答案 3 :(得分:-1)

$.ajax请求中,您可以将async属性设置为true或false,或将其传递给您的函数

     $.when(
     Status.fetch(true)

示例见下文

        Status:{
          Valid:new $.Deferred(),
           Server:{},
           /* ... */
          fetch:function(asyncType){      /// boolean passed as param
             if (Status.Valid.isResolved()){
             // status already present due to a past request
             return Status.Valid.promise();
             }else{
            // fetch status information from server
        $.ajax({
            type:     'GET',
            url:      'status.php'),
            cache:    true,
            dataType: 'json',
            async: asyncType /////// will be true or false
        }).done(function(response){
            $.each(response,function(key,val){
                Status.Server[key]=val;
            });
            Status.Valid.resolve(Status.Server);
        }).fail(function(response){
            Status.Valid.reject(NaN);
        });

答案 4 :(得分:-2)

我想分享我最终找到的解决方案。这不是一件优雅的事情,但它以积极的方式回答了原始问题。因此,它提供了一种根据请求组合异步和同步访问的方法。

我认为添加这个答案是有意义的,因为所有其他贡献者声称这根本不可能。

所需要的只是实现一个阻塞例程,确保只有在异步检索的数据结构可用时才返回所请求的值。所以这里没有什么魔力,最后只是一个简单的“等待”策略:

getServerAspect:function(aspect){
    /* the state of the asynchronous retrieval attempt */
    var resolution=Status.Valid.state();
    while ("pending"==resolution) {
        setTimeout(function(){resolution=Status.Valid.state();},500);
    }
    /* asynchronous retrieval has finished */
    if ("resolved"==resolution){
        /* return vaule should be available */
        return server[aspect];
    }else{
        /* data could not be retrieved, something is borked */
        return NaN;
    }
}, // Status.getServerAspect

[这个功能很简单,从内存写入,因为我的真实解决方案更复杂。 ]

实际上,该功能在等待分辨率时阻止gui。这不是优雅的,但请注意,在原始版本中明确要求阻止行为。在正常情况下,检索应该在请求服务器方面之前完成,因此“通常”没有阻塞。对于不是这种情况的情况,我宁愿等待半秒钟,因为即使在异步处理中,最终操作也会延迟,直到检索完成。

这种阻止策略允许我保持代码的其余部分更简单,更容易阅读,因为我不必一直使用匿名函数。相反,我可以简单地在一个简单的条件语句中访问值:

if ("expected"==Status.getServerAspect('someaspect')){
    /* do something */
}