如何将多个promises操作相同的DOM元素作为副作用并获得一致的结果?

时间:2017-06-01 14:41:04

标签: javascript ajax caching dojo

我有以下Dojo 1.9代码,它异步加载2个表(某些名称已更改):

function loadPanel1() {                                              

    // set 'Loading Tool' status message                                                     
    var actionStatus = dom.byId("globalError");                                              
    var oldStatus = actionStatus.innerHTML;                                                  
    var oldStatusClass = actionStatus.className;                                             
    actionStatus.className = "globalInfo";                                                   
    actionStatus.innerHTML = globalInfoMessage                                               
            + loadingMessage;                                                  

    // Post the data to the server                                                           
    request                                                                                  
            .post("loadPanel1.action", {                                     
                data : {                                                                     
                    "projectOid" : projectOid,                                               
                    "oid" : projectStreamOid                                                 
                }                                                                            
            })                                                                               
            .then(                                                                           
                    function(response) {                                                     
                        if (CommonUtils.checkForLoginPage(response)) {                       
                            return;                                                          
                        }                                                                    

                        dom.byId("Panel1").innerHTML = response;     

                        // restore previous action status                                    
                        var actionStatus = dom.byId("globalError");                          
                        actionStatus.innerHTML = oldStatus;                                  
                        actionStatus.className = oldStatusClass;                             
                        connectLinks();                                                      
                    },                                                                       
                    function(error) {                                                        
                        if (error instanceof SyntaxError) {                                  
                            location.reload(true);                                           
                        } else if (error instanceof Error) {                                 
                            var actionStatus = dom                                           
                                    .byId("globalError");                                    
                            actionStatus.innerHTML = globalErrorMessage                      
                                    + loadingErrorMessage;                     
                            actionStatus.className = "globalError";                          
                        }                                                                    
                    });                                                                      
}

//connectLinks connects event handlers to several links in Panel1 that reload the panel using loadPanel1() when successful.

/**                                                                                          
 * Posts the search form criteria to the server via ajax call.                               
 */                                                                                          
function loadPanel2() {                                               

    // set 'Loading Tool' status message                                                     
    var actionStatus = dom.byId("globalError");                                              
    var oldStatus = actionStatus.innerHTML;                                                  
    var oldStatusClass = actionStatus.className;                                             
    actionStatus.className = "globalInfo";                                                   
    actionStatus.innerHTML = globalInfoMessage                                               
            + loadingMessage;                                                  

    // Post the data to the server                                                           
    request                                                                                  
            .post("loadPanel2.action", {                                      
                data : {                                                                     
                    "projectOid" : projectOid,                                               
                    "oid" : projectStreamOid                                                 
                }                                                                            
            })                                                                               
            .then(                                                                           
                    function(response) {                                                     
                        if (CommonUtils.checkForLoginPage(response)) {                       
                            return;                                                          
                        }                                                                    

                        dom.byId("Panel2").innerHTML = response;      

                        // restore previous action status                                    
                        var actionStatus = dom.byId("globalError");                          
                        actionStatus.innerHTML = oldStatus;                                  
                        actionStatus.className = oldStatusClass;                             
                    },                                                                       
                    function(error) {                                                        
                        if (error instanceof SyntaxError) {                                  
                            location.reload(true);                                           
                        } else if (error instanceof Error) {                                 
                            var actionStatus = dom                                           
                                    .byId("globalError");                                    
                            actionStatus.innerHTML = globalErrorMessage                      
                                    + loadingErrorMessage;                     
                            actionStatus.className = "globalError";                          
                        }                                                                    
                    });                                                                    
}

我遇到的问题基本上是竞争条件:

  1. loadPanel1启动并在本地缓存旧状态;
  2. loadPanel1显示状态为
  3. 的加载消息
  4. loadPanel1开始加载面板;
  5. loadPanel2启动并缓存loadPanel1;
  6. 放置的加载消息
  7. loadPanel2使用相同的消息替换加载消息;
  8. loadPanel2开始加载Panel 2;
  9. loadPanel1完成加载面板并将其缓存的旧状态放回;
  10. loadPanel2完成加载并用缓存的加载消息替换旧的状态loadPanel1!
  11. 有一个复杂因素是,有时,当页面加载时,globalError已经显示状态,并且不应该通过loadPanel方法擦除该状态。因此,无论loadPanel1()还是loadPanel2()最后完成,都需要保留该状态。此外,Panel1本身有几个链接,可以改变Panel1的内容,然后添加状态消息并重新加载面板(之后仍然应该保留消息)。 Panel2在加载后不会重新加载。

    所以有几个工作流程:

    1. 没有状态消息的页面加载,两个面板都已加载,无论何时完成,都不会显示状态消息;
    2. 页面加载WITH状态消息,两个面板都已加载,无论哪一个完成,原始状态消息都应保留;
    3. 已加载页面和面板。用户执行更改panel1内部状态的操作。该操作显示状态消息,重新加载panel1,并且在加载面板后,操作显示的状态消息应保留。
    4. 加载面板时出现问题,在这种情况下,错误消息应显示为状态消息,无论何时,哪些面板和多少面板无法加载。
    5. 我不禁认为有一个优雅的解决方案,使用外部方法来缓存操作消息,但谷歌并没有真正帮助我。大多数关于使用加载消息进行多次异步加载的文章都涉及在加载最终内容的同一位置显示加载消息,这在这种情况下无济于事。

2 个答案:

答案 0 :(得分:1)

解决方案是没有您要替换的单个状态消息。相反,将其设为一个列表,每个组件都可以附加,更改或删除自己的消息。你的逻辑应该像

load(x) {
    const message = MessagePanel.newMessage();
    message.setContent(loader);
    message.show();
    fetch(x).then(res => {
        if (res.isMessage)
            message.setContent(res);
        else
            message.hide();
    }, err => {
        message.setContent(err);
    });
}

在暂停或用户互动之后,您会想要再次隐藏消息,以便列表无限增长。

如果您真的想/只能显示一条消息,请让MessagePanel管理状态。 load函数应该完全相同,但面板可能决定只显示列表中的顶部消息,或者最近显示的消息。

答案 1 :(得分:0)

在与我的同事讨论最佳方法之后,我决定让异步加载的组件使用单独的消息显示,而不是试图在同一显示中处理消息。