【问题标题】:How would I design a client-side Queue system?我将如何设计客户端队列系统?
【发布时间】:2011-06-07 15:47:51
【问题描述】:

概览

我正在处理一个项目,但我遇到了一些问题,因为事情没有按照我希望的顺序发生。所以我一直在考虑设计某种队列,我可以用它来组织函数调用和其他在启动期间使用的杂项 JavaScript/jQuery 指令,即在页面加载时。我要找的不一定是 Queue 数据结构,而是某种系统,可以确保事情按照我指定的顺序执行,并且只有在前一个任务完成后才能开始新任务。

我已经简要地查看了jQuery QueueAjaxQueue,但我真的不知道它们是如何工作的,所以我不确定这是否是我想要采取的方法......但我会继续阅读有关这些工具的更多信息。

具体说明

目前,我已经进行了一些设置,以便在$(document).ready(function() {...}); 内部进行一些工作,而在$(window).load(function() {...}); 内部进行其他工作。例如,

<head>
  <script type="text/javascript">    

    // I want this to happen 1st
    $().LoadJavaScript();
    // ... do some basic configuration for the stuff that needs to happen later...

    // I want this to happen 2nd
    $(document).ready(function() {

      // ... do some work that depends on the previous work do have been completed
      var script = document.createElement("script");
      // ... do some more work...
    });

    // I want this to happen 3rd
    $(window).load(function() {

      // ... do some work that depends on the previous work do have been completed
      $().InitializeSymbols();
      $().InitializeBlock();
      // ... other work ... etc...
    });
  </script>
</head>

...这真的很乏味和丑陋,更不用说糟糕的设计了。因此,我不想处理这种混乱,而是想设计一个非常通用的系统,以便我可以,例如,排队 $().LoadJavaScript();,然后是 var script = document.createElement("script");,然后是 $().InitializeSymbols();,然后是 $().InitializeBlock();,等等......然后Queue 将执行函数调用和指令,这样在一条指令执行完成后,另一条指令可以启动,直到 Queue 为空,而不是我重复调用 dequeue。

这背后的原因是,由于依赖于已完成的配置和初始化步骤,因此需要先进行一些工作,例如配置和初始化,然后才能开始其他工作。如果这听起来不是一个好的解决方案,请告诉我:)

一些基本工作

我已经为基本队列which can be found here 编写了一些代码,但我希望扩展它的功能以便我可以存储各种类型的“对象”,例如单独的 JavaScript/jQuery 指令和函数调用,基本上是我想要执行的代码片段。

更新

使用我已经实现的当前队列,看起来我可以存储函数并在以后执行它们,例如:

// a JS file... 
$.fn.LoadJavaScript = function() {

    $.getScript("js/Symbols/Symbol.js");
    $.getScript("js/Structures/Structure.js");
};

// another JS file...
function init() { // symbols and structures };

// index.html
var theQueue = new Queue();
theQueue.enqueue($().LoadJavaScript);
theQueue.enqueue(init);

var LJS = theQueue.dequeue();
var INIT = theQueue.dequeue();

LJS();
INIT();

我还认为我已经弄清楚了如何存储单个指令,例如 $('#equation').html(""); 或者甚至 if-else 语句或循环,将它们包装成这样:

theQueue.enqueue(function() { $('#equation').html(""); // other instructions, etc... });

但这种方法需要我等到队列完成其工作后才能继续工作。这似乎是一个不正确的设计。有没有更聪明的方法呢?另外,我怎么知道某个函数已经执行完毕,这样队列才能知道继续前进?是否有某种可以等待的返回值或可以为队列中的每个任务指定的回调函数?

整理

由于我在客户端做所有事情并且我不能让队列独立做自己的事情(根据下面的答案),有没有比我等待队列完成工作更聪明的解决方案?

由于这更像是一个设计问题而不是特定的代码问题,我正在寻找解决我的问题的方法的建议,关于我应该如何设计这个系统的建议,但我绝对欢迎,并希望看到,支持建议的代码 :) 我也欢迎对我在上面链接到的 Queue.js 文件和/或我对我的问题的描述以及我计划采取的解决方法的任何批评。

谢谢,赫里斯托

【问题讨论】:

  • 如需了解队列,请查看Can somebody explain jQuery queue to me?。那里有一些使用自定义队列的好例子。
  • 是的,我实际上一直在阅读它以及另一篇文章,但我不确定它是否是我要找的。​​span>
  • 就个人而言,我认为当您开始提出“如何重新组织 javascript 的运行方式(以及顺序)”问题时,是时候重新考虑您的逻辑了。有些事情告诉我你需要退后一步,重新考虑你的过程和方法,并得出一个更好的结论。总有另一种方法可以完成您需要实现的目标(尽管您很具体,但这个问题仍然太模糊,无法在不直接解决手头的“制作队列系统”任务的情况下给您一个结论性的答案)。
  • 我认为你是对的,这就是我发布这个问题的原因......作为重新思考逻辑的第一步。我发现我走错了路,我正试图在为时已晚之前纠正它。话虽如此,我并不是在寻找你给我这个系统,而是帮助我提出我不熟悉的设计理念,你可能会对我所描述的问题有益。我正在与这个社区联系,以集思广益一个好的方法,就像你说的那样,得出一个更好的结论。

标签: javascript jquery queue client-side


【解决方案1】:
**The main functions**

**From there we can define the main elements required:**

var q=[];//our queue container
var paused=false; // a boolean flag
function queue() {}
function dequeue() {}
function next() {}
function flush() {}
function clear() {}

**you may also want to 'pause' the queue. We will therefore use a boolean flag too. 
Now let's see the implementation, this is going to be very straightforward:**

var q      = [];
var paused = false;

function queue() {
 for(var i=0;i< arguments.length;i++)
     q.push(arguments[i]);
}
function dequeue() {
    if(!empty()) q.pop();
}
function next() {
    if(empty()) return; //check that we have something in the queue
    paused=false; //if we call the next function, set to false the paused
    q.shift()(); // the same as var func = q.shift(); func();
}
function flush () {
    paused=false;
    while(!empty()) next(); //call all stored elements
}
function empty() {  //helper function
    if(q.length==0) return true;
    return false;
}
function clear() {
    q=[];
}

**And here we have our basic queue system!
let's see how we can use it:**

queue(function() { alert(1)},function(){ alert(2)},function(){alert(3)});
next(); // alert 1
dequeue(); // the last function, alerting 3 is removed
flush(); // call everything, here alert 2
clear(); // the queue is already empty in that case but anyway...

【讨论】:

    【解决方案2】:

    我建议使用http://headjs.com/ 它允许您并行加载 js 文件,但顺序执行它们,本质上与您想要做的事情相同。它很小,您可以随时从中获取灵感。

    我还要提到依赖执行顺序的处理程序不是好的设计。我总是能够将我所有的引导代码放在准备好的事件处理程序中。在某些情况下,如果您需要访问图像,则需要使用负载处理程序,但对我来说并不经常。

    【讨论】:

    • 我需要执行顺序,因为我使用的第三方库需要在页面加载时进行配置,然后才能使用该库完成任何工作。
    • head.js 应该可以工作,它会加载所有脚本并按顺序执行它们。只需确保所有脚本都是外部脚本并使用 head.js 加载它们
    • .. 这看起来很酷。我一定会读一遍并玩它。不过,目前我想开发自己的解决方案,纯粹是因为它是我学习过程的一部分。我确信 Head.js 比我提供的更高效和更好的解决方案,但在此过程中也能从错误中吸取教训。
    • ...我的队列失败了:(我给 HEAD.js 一个机会!:)
    • 你总是可以通过挖掘他们的代码来使用 head.js 来获得灵感
    【解决方案3】:

    这里有一些可能有用的东西,这是你想要的吗?

    var q = (function(){
        var queue = [];
        var enqueue = function(fnc){
            if(typeof fnc === "function"){
                queue.push(fnc);
            }
        };
    
        var executeAll = function(){
            var someVariable = "Inside the Queue";
            while(queue.length>0){
                queue.shift()();
            }
        };
    
        return {
            enqueue:enqueue,
            executeAll:executeAll 
        };
    
    }());
    
    var someVariable = "Outside!"
    q.enqueue(function(){alert("hi");});
    q.enqueue(function(){alert(someVariable);});
    q.enqueue(function(){alert("bye");});
    alert("test");
    q.executeAll();
    

    alert("test"); 在您放入队列中的任何内容之前运行。

    【讨论】:

    • 是的,我想这与我正在寻找的类似,但我已经实现了一个队列,可以完成您向我展示的内容。但在我看来,如果我想执行任何单独的指令,我必须像你一样将它们包装在 function() {...} 中。
    • 将它们包装在function(){} 中还有一个额外的好处是可以创建一个闭包,这样您就可以保持正确的范围。我将编辑该示例以说明为什么这很重要。在那里,即使您在队列中运行该函数,它仍会访问您在外部声明的正确 someVariable
    • 不错!看起来很棒。谢谢!这样就回答了我关于“如何使用队列运行代码?”的问题。我想剩下的就是回答“我怎样才能使队列自动化,以便这个 executeAll 方法知道一个执行何时完全完成,然后才能开始下一个执行?”。有关该问题的更多详细信息,请参阅 WRAP-UP 部分之前的段落。
    • 这是一个有点奇怪的问题,因为 javascript 是单线程的。您的 executeAll 方法知道一次执行何时完全完成,因为下一行(在您调用队列中的函数之后)将执行。如果您希望队列中的函数能够运行回调和事情,或者使用 setTimeout/setInterval,那么事情就会变得棘手。
    • 明白了...我想这就是我要问的。假设我有以下逻辑,var test = theQueue.dequeue(); test(); var init = theQueue.dequeue();,那么只有在test() 运行完成后才会发生第二次出队,对吗?
    【解决方案4】:

    如何将代码片段存储在队列中并让它稍后执行

    您当前的实现已经适用于此。 JavaScript 中没有声明类型,因此您的队列可以保存任何内容,包括函数对象:

    queue.enqueue(myfunc);
    var f = queue.dequeue();
    f();
    

    如何让队列独立做自己的事情

    JavaScript 本质上是单线程的,这意味着任何时候都只能执行一件事。所以队列不能真正“独立”于你的代码的其余部分运行,如果这就是你的意思的话。

    你基本上有两种选择:

    1. 一次性运行所有排队的函数,一个接一个,这甚至不需要队列,因为它与直接将函数调用直接放在代码中相同。

    2. 使用定时事件:一次运行一个函数,一旦完成,设置超时以在一定间隔后执行下一个排队的函数。下面是一个例子。


    function run() {
      var func = this.dequeue();
      func();
    
      var self = this;
      setTimeout(function() { self.run(); }, 1000);
    }
    

    如果func 是一个异步请求,您必须将setTimeout 移动到回调函数中。

    【讨论】:

    • 这看起来不错,谢谢!我有几个问题......首先,如果队列中的元素看起来像var script = document.createElement("script");,那么这并不是一个真正的函数。我将如何执行此指令?另外,将setTimeout移到回调函数中是什么意思...哪个回调函数?
    • @Hristo:我不太确定您对 script 示例的意思。你能把它放到一个匿名函数中吗? var script = function(){document.createElement("script");};
    • @musicfreak ...我当然可以存储函数并在以后执行它们。但我要问的是,我可以存储单独的指令吗,比如 $('#equation').html("");var targetID = $('#equation').find('.active').attr("id"); 或 if-else 语句或 for/while 循环?我想我可以将所有内容都包装在匿名函数中,但这似乎不实用?
    • @casablanca... 你能解释一下var self = this; 的作用吗?我知道这很像一个递归函数,因为 run() 每秒都会被调用,但什么是“自我”?
    • @Hristo:对于第一个问题,您始终可以将单行或循环或其他任何内容包装到匿名函数中:function() { something; }。小心变量——如果你将它们标记为var,它们将是函数的本地变量,否则是全局变量。关于var self = this,那只是对队列对象this的附加引用,因为在setTimeout内部,this指的是窗口。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-20
    相关资源
    最近更新 更多