I want to display elements from json based on their time and duration and interval is interupted by settimeout

谁都会走 提交于 2019-12-11 10:14:43

问题


I have a json file that has a structure like this:

[{"text_content":"aaaaa","text_duration":15,"start_time":"2015-10-07 23:25:29"},
{"text_content":"aawwaaaaa","text_duration":15,"start_time":"2015-10-07 23:25:44"},
{"text_content":"bbaawwaaaaa","text_duration":15,"start_time":"2015-10-07 23:25:59"}, etc.

I have a div on my webpage user-text and I want to fill it with strings taken from this json. But there's a condition - I want to display the first text at the time assigned to it and for the period of duration assigned to it. So based on my example I want to display aaaaa for 15 seconds at 2015-10-07 23:25:29, etc. I wrote a jquery script:

    var results =[];
    var cursor = 0;

    function myFunction () {
        $.getJSON('get_json.php', function(json) {
            results = json;
            cursor = 0;

            // Now start printing
            if(results.length == 0){
                $('#user-text').hide('fast', function() {
                    $('#user-text').html("there is no text at the moment");
                    $('#user-text').show('fast');
                });
            }else{
                printNext();
            }
        });
    }

    function printNext(){
        if(cursor == results.length){
            // Reset the cursor back to the beginning.
            cursor = 0;
        }

        var textDate = results[cursor].start_time;
        var textMsg = results[cursor].text_content;
        var dateOfText = new Date(textDate).setMilliseconds(0);
        var duration = results[cursor].text_duration;
        //now we're checking every second if there is any text to display at that time
        var myInterval = setInterval(check, 1000);

        function check() {

            var currentDate = new Date().setMilliseconds(0);

            if (currentDate - dateOfText ==0) {
            clearInterval(myInterval);
                $('#user-text').hide('fast', function() {
                    $('#user-text').html(textMsg);
                    $('#user-text').show('fast');
                });

            } else{

                $('#user-text').html("there is no text to display at the moment");
            }
        }

        // Set a delay for the current item to stay
        // Delay is key2 * 1000 seconds
        setTimeout(function(){
            printNext();
        }, results[cursor].text_duration * 1000);

        // Advance the cursor.
        cursor++;
    }

but somehow it doesn't work. What happens is that the inside loop that goes every second is disturbed by the set timeout of the whole function. So even if we find a match here if (currentDate - dateOfText ==0) { - it won't appear on the screen for its all duration, because it will be interupted by this:

setTimeout(function(){
            printNext();
        }, results[cursor].text_duration * 1000);

I don't know exactly how to proceed at this point and how to make the logic work so that each text is played for its whole duration. Could you help me with that?


回答1:


Because you have the else condition inside check to hide the text. I'm assuming these messages to display are going to arrive well in advance, so you're going to have three instances of check running all at the same time by the time the messages are ready to be displayed.

Since each one of those in the else condition is going to show the "there is no text to display" message every second, even when the first check finally decides to show the first message, the other two are going to quickly overrun it and say no messages.

You might be able to just move the setTimeout calling printNext to inside the check function in the true branch of the if.

I've built a small demo with an alternate approach: http://jsfiddle.net/nauzilus/rqctam5r/

Rather than iterating over the messages array, it will just focus on the first one, and shift it off once done.

You can push new entries into messages and they'll be queued to display when appropriate.

Also, this assumes messages are always going to be loaded in advance; any messages with dates in the past will be processed as though they're ready to be viewed right now.

I haven't used jQuery, just vanilla JavaScript but you should be able to adapt it easily enough. Also, I'm checking every 500ms, and displaying when message is within 1000ms of schedule just to avoid any missed messages due to browser lag or blocking. Timers and schedules aren't guaranteed to run exactly when you think they will, so if a time takes 1001ms instead of 1000ms, you're going to miss a message!




回答2:


The call to setTimeout is badly placed. Put

    setTimeout(printNext, results[cursor].text_duration * 1000);

in the same {}code block as clearInterval(myInterval); That is, stop the interval timer, show the text, and start a timer to call back printNext to erase the text and check for new.

Replace this block of code in printNext:

    setTimeout(function(){
        printNext();
    }, results[cursor].text_duration * 1000);

with just

     check();

to ensure text is erased in the callback.




回答3:


An alternate approach:

//global variable used to generate the element id for each new message
var message_index = 0;

function get_schedule() {
    $.getJSON('get_json.php', function(json) {
        schedule_messages(json);
    });
}

function schedule_messages(schedule) {
    var current_time = new Date();

    //iterate through each message event
    for(var i = 0; i < schedule.length; i++) {
        var start_time = new Date(schedule[i].start_time);

        //make sure the scheduled message time has not passed
        if(start_time > current_time) {

            var duration = schedule[i].text_duration * 1000;

            //calculate time until message placement and removal
            var display_delay = start_time - current_time;
            var remove_delay = start_time - current_time + duration;

            //schedule placement of the message
            (function(delay, message, index) {
                setTimeout(function() {
                    display_message(message, index);
                }, delay);
            })(display_delay, schedule[i].text_content, message_index);

            //schedule removal of the message
            (function(delay, index) {
                setTimeout(function() {
                    remove_message(index);
                }, delay);
            })(remove_delay, message_index);

            //increment global message index to use for next message
            message_index++;
        }
    }
}

//do your fancy jQuery effect(s) in here...
function display_message(message, index) {
    $('#user-text').append(
        $("<p>")
            .html(message)
            .attr("id", "message-" + index)
    );
}

//... and here
function remove_message(index) {
    $("#message-" + index).remove();
}


来源:https://stackoverflow.com/questions/33002880/i-want-to-display-elements-from-json-based-on-their-time-and-duration-and-interv

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!