简体   繁体   English

按顺序执行JavaScript

[英]Executing JavaScript in Order

I'm trying to print to a div one character at a time. 我正在尝试一次向div打印一个字符。 It works, however, it runs both lines at the same time so that all I get is a jumbled mess. 它有效,但是,它同时运行两行,所以我得到的只是一团糟。

How can I make the commands run one after the other? 如何使命令一个接一个地运行?

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/ jsFiddle: http : //jsfiddle.net/PscNC/1/

You have two asynchronous functions that you start one right after the other so they run in parallel. 您有两个异步函数,它们一个接一个地启动,因此它们可以并行运行。 If you want them to run serially, then you have to create some sort of notification when the first one is done so you then can trigger the start of the next one and so on. 如果希望它们连续运行,则必须在第一个通知完成时创建某种通知,以便随后触发下一个通知的开始,依此类推。 This can be done a number of ways (I show three ways below). 这可以通过多种方式完成(我在下面显示了三种方式)。 You can use a callback, you can use promises and you can avoid having to sequence the async operations at all. 您可以使用回调,可以使用Promise,并且可以完全避免对异步操作进行排序。

Method #1 - Completion Callback 方法1-完成回调

Here's adding a callback to your print function and then use that to trigger the next string to go and then changing your iteration of strings to use the callback: 这里向打印函数添加了一个回调,然后使用该回调来触发下一个字符串,然后更改字符串的迭代以使用该回调:

Working demo: http://jsfiddle.net/jfriend00/Lyu5V/ 工作演示: 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();
});

FYI, there's really no reason to split your string into an array. 仅供参考,实际上没有理由将您的字符串拆分为一个数组。 You can access the individual characters of the string with the .charAt(index) method. 您可以使用.charAt(index)方法访问字符串的各个字符。


Method #2 - Promises - use .then() to sequence operations 方法2-承诺-使用.then()对操作进行排序

And, here's a version of your code using promises instead of passing the callback: 而且,这是使用promise而不是传递回调的代码版本:

Working demo: http://jsfiddle.net/jfriend00/97UtX/ 工作演示: 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());

});

Method #3 - Avoid sequencing by combining data into one single operation 方法3-通过将数据合并到一个操作中避免排序

And, if you want to simplify/DRY it up a bit, you can do this which avoids having to sequence the successive operations by just turning it into one longer operation and I made a few simplifications to your code: 而且,如果您想对其进行一些简化/干燥,可以这样做,从而避免了将连续的操作变成一个更长的操作而不得不对连续的操作进行排序的过程,并且我对代码进行了一些简化:

Working demo: http://jsfiddle.net/jfriend00/TL8pP/ 工作演示: 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(""));

});

This is based on jfriend's answer, but it uses primitives with promises rather than promises at a high level. 这是基于jfriend的答案的,但是它使用带有promise的原语,而不是高级的promise。 I believe this makes for cleaner code. 我相信这可以使代码更简洁。

First, let's write a function that represents a delay with promises: 首先,让我们编写一个表示承诺的延迟的函数:

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

Next, let's use promises to their fullest 接下来,让我们充分利用承诺

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(){})` 

Here is a fiddle illustrating this solution: http://jsfiddle.net/feq89/ 这是说明此解决方案的小提琴: http : //jsfiddle.net/feq89/

Just for fun, here is an ES6 solution without jQuery: 只是为了好玩,这是没有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, "")));

bobef is right. bobef是对的。

Add another argument to print, which is a callback. 向print添加另一个参数,这是一个回调。 And you should call the print method inside another recursive method instead a loop. 您应该在另一个递归方法内调用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();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM