【问题标题】:Executing JavaScript in Order按顺序执行 JavaScript
【发布时间】:2014-08-03 18:41:02
【问题描述】:

我正在尝试一次打印一个 div 字符。它有效,但是,它同时运行两条线,所以我得到的只是一团糟。

如何让命令一个接一个地运行?

function print(str){
    var arr = str.split("");
    var i = 0;
    function write(){
        setTimeout(function(){
            if(i < arr.length){
                var cont = $(".content").html();
                cont = cont.replace("_","");
                $(".content").html(cont + arr[i] + "_");
                i++;
                write();
            }
        },30);
    }
    write();
}

var str = [
    "I am the egg man",
    "I am the walrus"
];

for(x in str){
    print(str[x];
}

jsFiddle:http://jsfiddle.net/PscNC/1/

【问题讨论】:

  • 你有两个异步函数,你同时启动它们。如果您希望第一个在开始第二个之前完成,那么您需要在第一个完成后收到通知,以便您可以开始第二个。
  • 顺便说一句,Javascript 中的 for-in 循环不打算用作 for-each 循环,并且标准不保证迭代键的顺序。使用for(var i=0; i&lt;strs.length; i++)

标签: javascript


【解决方案1】:

您有两个异步函数,一个接一个地启动,这样它们就可以并行运行。如果您希望它们连续运行,那么您必须在第一个完成时创建某种通知,以便您可以触发下一个的开始,依此类推。这可以通过多种方式完成(我在下面展示了三种方式)。您可以使用回调,可以使用 Promise,并且可以完全避免对异步操作进行排序。

方法 #1 - 完成回调

这里为您的打印函数添加一个回调,然后使用它来触发下一个字符串,然后更改您的字符串迭代以使用回调:

工作演示:http://jsfiddle.net/jfriend00/Lyu5V/

$(function() {
    function print(str, fn) {
        var i = 0;
        var items = $(".content");

        function write() {
            setTimeout(function() {
                if (i < str.length) {
                    items.html(items.html().replace("_", "") + str.charAt(i) + "_");
                    i++;
                    write();
                } else {
                    fn();
                }
            }, 100);
        }
        write();
    }

    var data = [
            "I am the egg man...",
            "I am the walrus"
        ];

    var i = 0;
    function next() {
        if (i < data.length) {
            print(data[i++], next);
        }
    }
    next();
});

仅供参考,确实没有理由将您的字符串拆分为数组。您可以使用.charAt(index) 方法访问字符串的各个字符。


方法 #2 - 承诺 - 使用 .then() 对操作进行排序

而且,这是您的代码的一个版本,它使用 Promise 而不是传递回调:

工作演示:http://jsfiddle.net/jfriend00/97UtX/

$(function() {
    function print(str) {
        var i = 0, items = $(".content"), def = $.Deferred();

        function write() {
            setTimeout(function() {
                if (i < str.length) {
                    items.html(items.html().replace("_", "") + str.charAt(i) + "_");
                    i++;
                    write();
                } else {
                    def.resolve();
                }
            }, 100);
        }
        write();
        return def.promise();
    }

    var data = [
            "I am the egg man..",
            "I am the walrus"
    ];

    data.reduce(function(p, item) {
        return p.then(function() {
            return print(item);
        });
    }, $.Deferred().resolve());

});

方法 #3 - 通过将数据组合到一个操作中来避免排序

而且,如果您想稍微简化/干燥它,您可以这样做,这样就可以避免通过将连续操作变成一个更长的操作来对连续操作进行排序,我对您的代码做了一些简化:

工作演示:http://jsfiddle.net/jfriend00/TL8pP/

$(function() {
    function print(str) {
        var i = 0, items = $(".content");

        function write() {
            setTimeout(function() {
                if (i < str.length) {
                    items.html(items.html().replace("_", "") + str.charAt(i) + "_");
                    i++;
                    write();
                }
            }, 100);
        }
        write();
    }

    var data = [
            "I am the egg man..",
            "I am the walrus"
    ];
    print(data.join(""));

});

【讨论】:

    【解决方案2】:

    这是基于 jfriend 的回答,但它使用带承诺的原语,而不是高级别的承诺。我相信这会使代码更简洁。

    首先,让我们编写一个表示延迟的函数:

    function delay(ms){ // generic delay function
         var d = $.Deferred();
         setTimeout(d.resolve, ms);
         return d;
    }
    

    接下来,让我们充分利用 Promise

    var delay100 = delay.bind(null, 100); // a 100 ms delay
    
    function write(el, str, initial) { // write a single word
        return [].reduce.call(str, function (prev, cur) { // reduce is generic
            return prev.then(delay100).then(function (letter) {
                initial += cur;
                el.text(initial + "_");
            });
        }, $.when());
    }
    data.reduce(function (p, item) {
        return p.then(function () { // when the last action is done, write the next
            return write($(".content"), item, ""); // might want to cache this
        });
    }, $.ready.promise()); // we don't need `$(function(){})` 
    

    这是一个说明此解决方案的小提琴:http://jsfiddle.net/feq89/

    只是为了好玩,这里是一个没有 jQuery 的 ES6 解决方案:

    var delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
    
    var write = (el, str, initial) => 
        [].reduce.call(str, (prev, cur) =>
            prev.then(() => delay(100)).then(() => {
              initial += cur;
              el.textContent = initial + "_";
            });
        }, Promise.resolve());
    
    var content = document.querySelector(".content");
    data.reduce((p, item) => p.then(() => write(content, item, "")));
    

    【讨论】:

    • 如果你想一个接一个地写句子而不是用新的替换最后一个 - 你所要做的就是将p.then中的参数传递给initial值并以空的"" 开头data.reduce。
    • 没有检查,据我所知 - 不,不是,并且有关于将其正式记录到 API 中的讨论它一直存在 :)
    • 是的,这也是我的理解。
    • BTW - 可以通过在数组中构建字符串并使用.join('_') 输出来避免终端“_”。也让 GC 做的更少。
    • @Roamer-1888 我认为终端 _ 插入符号应该在那里(就像你在文字处理器中输入时一样)。可以.thenreduce 调用并手动将其删除。至少在v8 中,这段代码相当慢(因为.reduce、字符串属性访问等等),因为我假设您同时执行此操作的次数不超过 1000 次。我不确定您对.join 的建议如何运作,介意摆弄一下吗?
    【解决方案3】:

    bobef 是对的。

    为 print 添加另一个参数,这是一个回调。 你应该在另一个递归方法中调用 print 方法,而不是循环。

    function print(str, _cb){
        var arr = str.split("");
        var i = 0;
        function write(){
            setTimeout(function(){
                if(i < arr.length){
                    var cont = $(".content").html();
                    cont = cont.replace("_","");
                    $(".content").html(cont + arr[i] + "_");
                    i++;
                    write();
                } else {
                    _cb();
                }
            },30);
        }
        write();
    }
    
    var str = [
        "I am the egg man",
        "I am the walrus"
    ];
    
    var j = 0,
        callback = function () {
            if(j < str.length){
                print (str[j++], callback);
            }
        };
    
    callback();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-23
      • 2021-03-16
      • 1970-01-01
      相关资源
      最近更新 更多