按顺序执行JavaScript

时间:2014-08-03 04:47:26

标签: javascript

我正在尝试一次打印一个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/

3 个答案:

答案 0 :(得分:1)

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

方法#1 - 完成回叫

这里为你的print函数添加一个回调,然后使用它来触发下一个字符串,然后改变你的字符串迭代来使用回调:

工作演示: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()对操作进行排序

而且,这是使用promises而不是传递回调的代码版本:

工作演示: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(""));

});

答案 1 :(得分:1)

这是基于jfriend的答案,但它使用的是带有承诺的原语而不是高级承诺。我相信这可以使代码更清晰。

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

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

接下来,让我们充分利用承诺

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, "")));

答案 2 :(得分:0)

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();