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

Javascript 同步访问由异步请求填充的结构,javascript,jquery,asynchronous,Javascript,Jquery,Asynchronous,我试图为JS/jQuery中实现的应用程序提供一个状态值字典。这些值必须从服务器(ajax)获取。我希望以异步的方式完成这项工作,以便在初始化期间启动该请求。以后应该同步访问这些值,以使代码易于理解。 目前简化的代码如下所示: 初始化: $(document).ready(function(){ Status.fetch(); }) 结构: Status:{ Valid:new $.Deferred(), Server:{}, /* ... */ fet

我试图为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...

在异步和同步处理之间建立“桥梁”的诀窍是什么

$.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);
        });

让getServerAspect简单地返回承诺,然后使用它

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!
    }
});

很难调用异步进程并使其同步,最简单的方法是将函数分成两部分;一个在异步调用之前,一个在异步调用之后

我发现最好的方法是使用promise模式,或者您可以尝试一个中介,并向其传递一系列由流程的每个步骤触发的事件:


据我所知,JavaScript并不像其他语言那样具有暂停或等待功能。

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

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

所需的只是实现一个阻塞例程,确保仅当异步检索的数据结构可用时才返回请求的值。因此,这里没有什么神奇之处,最后只是一个简单的“等待”策略:

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 */
}

这将起作用,因为setTimeout(…)调用(异步时)仅确保Status.Valid.state();电话不是经常打。这仍然是一个繁忙的等待,它不仅会阻塞线程(因此,不应该在WebWorker之外执行),而且还会占用CPU的空闲时间。事实上,与使用这种方法不同,使用同步AJAX请求是更好的解决方案。在为呼叫者阻塞时,等待不会很忙。但是,如果可以,您仍然应该使用纯异步实现

在获得数据之前,您使用什么来“阻止”?据我所知,除了ajax之外,在现代浏览器中这是不可能的sync@KevinB我尝试使用一个由异步获取过程解析的延迟对象。稍后,我尝试通过在
$中计算延迟对象来阻止访问。至少这是个想法…对,但这不是阻塞,它是异步的。您无法从
.done()
回调返回数据。您需要设置async参数并将其设置为false,以允许ajax在继续执行其余代码之前完成其请求,或者使ajax同步,或者使代码异步。你不能两者兼得,但我不希望ajax请求是同步的。它应该在初始化期间触发,并等待时间。将其设置为false,这将需要时间,在它完成之前不会执行任何其他操作finished@CR41G14async:false在web workers之外是一种非常糟糕的做法,因为它会影响用户体验。@Kevin B当代码的其余部分依赖于ajax请求时,通常会有更好的方法使用async。当然,这是我通常做的(我写的)。但这使得条件语句复杂且容易出错。我正在寻找一种解决方案,使条件语句与异步语句保持一致。我的想法是将异步内容移动到“包装器”方法中。但是怎么做呢?当然,但是它是