0

我正在研究一种方法,我可以毫不费力地将一个 php 程序的多个实例更新到相同的版本。

我的软件检查 RSS 提要,其中包括有关最新版本的信息。如果它看到有可用的更新,它会显示一个指向“更新”页面的链接。当用户单击更新链接时,我有一个 jquery 函数,可以一次更新每个需要更新的文件:

$.getJSON('http://softwareserver.com/updateFileList.php?fromver=1.0', function(data) {
    var filesToDo = data.length;

    $.each(data, function(num, file) {
        num = num + 1;
        action = file.charAt(0);
        name = file.substring(1);

        if ( action == "+" )
        {
            $('#upsole').append("<p><strong>Adding</strong> " + name + ". . .</p>");
                $.get("{$var['sbpurl']}/updateAction.php", { action: "add", file: name, revision: "{$TMP['rev']}" } );
        }

        else if ( action == "-" )
        {
            $('#upsole').append("<p><strong>Removing</strong> " + name + ". . .</p>");
                $.get("{$var['sbpurl']}/updateAction.php", { action: "remove", file: name, revision: "{$TMP['rev']}" } );
        }

        else if ( action == "~" )
        {
            $('#upsole').append("<p><strong>Updating</strong> " + name + ". . .</p>");
                $.get("{$var['sbpurl']}/updateAction.php", { action: "update", file: name, revision: "{$TMP['rev']}", version: "{$TMP['ver']}" } );
        }

        var completed = num / filesToDo * 100;

        $('#barcolor').css('width', completed + '%');
        $('#numProgress').html(completed.toFixed(0));

        if ( completed == 100 )
        {
            $('#conutt').html('<a href="index.php" class="button"> Continue > </a>');
            $('#upsole').append("<p><strong>Update Complete</strong></p>");
        }
    });
});

我在 div 容器中显示每个文件名,因为它正在更新;我还有一个进度条,随着每个文件的更新,直到所有文件都被更新,它的进度条从 0-100% 变为 0-100%。但是当它全部完成时,它只是从 0 变为 100。它实际上并没有显示进度,因为每个文件都像我想要的那样更新。

我希望我的进度条从 0% 变为 7%、15% 等,直到达到 100%,但在全部完成之前什么都没有发生。正如我所说,它只是从 0% 变为 100%。

$.each 不是最好的解决方案吗?

4

2 回答 2

1

不,$.each这不是这项工作的正确工具。您的主要问题是浏览器中的 JavaScript 是单线程的,因此您的所有 DOM 都会更新:

  • $('#upsole').append(...)
  • $('#barcolor').css(...)
  • $('#numProgress').html(...)

当浏览器重新获得控制权时,只会排队等待处理。那么浏览器什么时候重新获得控制权呢?当然是完成后$.each。您会看到结果:用户看到事情从一无所有到一步完成。您还需要$.get考虑异步性质。

您需要在每次 DOM 更新后将控制权交还给浏览器。您有一堆$.get调用,因此您可以使用它们的成功回调将您的 DOM 更改与浏览器对这些更改的响应交错。像这样的东西:

$.getJSON('http://softwareserver.com/updateFileList.php?fromver=1.0', function(data) {
    var filesToDo = data.length;
    var next_one  = function() {
        var file = data.shift();
        if(!file) {
            // If `data` is empty then we're all done so we can put the UI into
            // the "update complete" state and end the process by not calling
            // next_one() again.
            $('#conutt').html('<a href="index.php" class="button"> Continue &gt; </a>');
            $('#upsole').append("<p><strong>Update Complete</strong></p>");
            return; 
        }

        // Set up the next iteration by parsing `file`. We put the `$.get` stuff
        // into an object so that we only have one `$.get` call; the reason for
        // this will be clear shortly.
        var action = file.charAt(0);
        var name   = file.substring(1);
        var get    = { };
        if(action == '+') {
            get = {
                msg:    '<p><strong>Adding</strong> ' + name + '...</p>',
                url:    "{$var['sbpurl']}/updateAction.php",
                params: { action: "add", file: name, revision: "{$TMP['rev']}" }   
            };
        }
        else if(action == '-') {
            //...
        }
        else if(action == '~') {
            //...
        }

        // Tell them what we're about to do...
        $('#upsole').append(get.msg);

        // This `$.get` call is asynchronous so the browser will get control here 
        // and any pending DOM updates will be applied.
        $.get(get.url, get.params, function() {
            // Update the DOM with the latest status.
            var completed = (filesToDo - data.length) / filesToDo * 100;
            $('#barcolor').css('width', completed + '%'); 
            $('#numProgress').html(completed.toFixed(0));

            // And trigger the next iteration.
            next_one();
        });
    };

    // Crank it up.
    next_one();
});

基本思想是迭代data直到它为空,每次迭代都会删除第一个元素,data因此我们可以通过查看它何时返回(这是错误的)来检测data(以及我们迭代的结束)的结束。undefined每次迭代都会触发$.get, 将控制权返回给浏览器以允许更新 DOM。然后$.get回调将触发下一次迭代。

诸如错误处理之类的细节留给读者练习。

于 2013-01-05T22:42:26.217 回答
1

c0nfus3d1,我看到你有来自@mu 的答案,但也许你希望看到我在重大中断之前开始准备的解决方案。

据我所知,这.each()对这项工作有好处,您的代码只需要非常轻微的修改即可使其工作。

然而,它也可以从进一步的 mods 中受益,以提供像这个更整洁、更易读的版本:

$.getJSON('http://softwareserver.com/updateFileList.php?fromver=1.0', function(data) {
    var filesToDo = data.length;
    var filesDone = 0;
    var activityStr = "<p><strong>%s1</strong>%s2</p>";//activity message template 

    function showProgress() {
        filesDone++;
        var completed = 100 * filesDone / filesToDo;

        $('#barcolor').css('width', completed + '%');
        $('#numProgress').html(completed.toFixed(0));

        if ( filesDone == filesToDo ) {
            $('#conutt').html('<a href="index.php" class="button"> Continue > </a>');
            $('#upsole').append(activityStr.replace('%s1', 'Update Complete').replace('%s2', ''));
        }
    }

    $.each(data, function(num, file) {
        var dataObj = {//data common to all cases below. 
                file: file.substring(1),
                revision: "{$TMP['rev']}" 
            },
            a, d;

        switch (file.charAt(0)) {
            case "+":
                a = 'Adding';
                d = $.extend({action:"add"}, dataObj);
                break;
            case "-":
                a = "Removing";
                d = $.extend({action:"remove"}, dataObj);
                break;
            case "~":
                a = "Updating";
                d = $.extend({action:"update", version:"{$TMP['ver']}"}, dataObj);
        }
        $('#upsole').append(activityStr.replace('%s1', a).replace('%s2', ' ' + d.file + '. . .'));
        $.get("{$var['sbpurl']}/updateAction.php", d).done(showProgress);
    });
});

未经测试

备注:

  • switch/case 结构取代了 if/else if/else if,并且只关心建立两个变量,这两个变量提供了随后的两个通用表达式。
  • $.extend()用于合并一般和特定于案例的对象,以构建内部$.get数据。
  • 进度消息是通过应用于.replace()字符串模板生成的。
  • showProgress()响应每个内部调用的完成调用该函数.get()
  • 虽然内部.get()请求以非常快的速度连续发出,但响应会在它们从服务器返回时以及何时以任何顺序进行处理。

也许这包括一些你可以使用的想法。

于 2013-01-06T00:54:48.843 回答