User:Bluedeck/serve/todo.js

维基百科,自由的百科全书

注意:保存之后,你必须清除浏览器缓存才能看到做出的更改。Google ChromeFirefoxMicrosoft EdgeSafari:按住⇧ Shift键并单击工具栏的“刷新”按钮。参阅Help:绕过浏览器缓存以获取更多帮助。

// Author: Bluedeck <bluedeck@outlook.com>
// Licence: CC-BY-SA
// Do NOT import this script. It is not designed to function on its own. You may encounter serious error
// ... were this included imporperly. Consult author before attempting.

function Todo(fn)
{
    this.description = "Async todo list - An easy way around call back hell";
    this.todo = [];
    this.once = false;
    this.finalize = null;  // insert finalizer function here.

    if(fn)
    {
        this.chain.apply(this, arguments);
    }
}

Todo.go = function()
{
    if(this.instance.once && this.initiated)
    {
        return;
    }

    this.initiated = true;
    var target = this.target;
    if(this.instance.todo[target])
    {
        this.instance.todo[target].action.apply(this, arguments);
    }
    else if(typeof this.instance.finalize === "function")
    {
        this.instance.finalize.apply(this, arguments);
    }
};

// Report itself to be done.
Todo.im_done = function(result)
{
    this.done = true;
    this.return = result;

    if(this.instance.all_done() && typeof this.instance.finalize === "function")
    {
        this.instance.finalize();
    }
};

// Add a chain.
Todo.prototype.chain = function(fn)
{
    if(typeof fn === "function" )
    {
        var todo = {};
        todo.target = this.todo.length + 1;
        todo.action = fn.bind(todo);
        todo.go = Todo.go;
        todo.im_done = Todo.im_done;
        todo.instance = this;
        todo.initiated = false;
        todo.done = false;

        this.todo.push(todo);

        if(arguments.length > 1)
        {
            var args = [];
            for(var i=1; i<arguments.length; i++)
            {
                args[i-1] = arguments[i];
            }
            this.chain(args);
        }
    }
    else if(fn.constructor === Array)
    {
        for(var i=0; i<fn.length; i++)
        {
            this.chain(fn[i]);
        }
    }
    else
    {
        throw new TypeError("You can chain one or more functions, an array of functions or nested arrays of " +
            "functions using the chain() function. You see this error because you attempted to chain something else. " +
            "If you chained an array, every functions before this error should have been chained successfully. If " +
            "you catch this error and attempt to go() for it, you might get partial results.");
    }
};

// Execute the first chain.
Todo.prototype.go = function()
{
    if(this.todo[0])
    {
        if(!this.once || !this.todo[0].initiated)
        {
            this.todo[0].initiated = true;
            this.todo[0].action.apply(this, arguments);
        }
    }
};

// Execute the first chain, nothing gets executed more than once.
Todo.prototype.go_once = function()
{
    this.once = true;
    this.go();
};

// Execute all chains in parallel
Todo.prototype.parallel = function()
{
    for(var i=0; i<this.todo.length; i++)
    {
        if(!this.once || !this.todo[i].initiated)
        {
            this.todo[i].initated = true;
            this.todo[i].action.apply(this, arguments);
        }
    }
};

// Do all chains in parallel, but don't repeat any single chain.
Todo.prototype.parallel_once = function()
{
    this.once = true;
    this.parallel();
};

// Chain a finalizer function that gets executed once everyone's done.
Todo.prototype.chain_finalizer = function(fn)
{
    if(typeof fn === "function")
    {
        this.finalize = fn;
    }
};

// All actions done??
Todo.prototype.all_done = function()
{
    for(var i=0; i<this.todo.length; i++)
    {
        if(!this.todo[i].done)
        {
            return false;
        }
    }
    return true;
};

function Todo_nextup()
{
    this.actions = [];
    this.nextup.apply(this, arguments);
}

Todo_nextup.prototype.nextup = function()
{
    if(arguments.length > 0)
    {
        var todo = new Todo();
        todo.im_finalized = false;
        todo.im_initiated = false;
        todo.chain(function(){this.im_initiated = true;}.bind(todo));
        todo.chain.apply(this, arguments);
        todo.chain_finalizer(function(){this.im_finalized = true;}.bind(todo));
        this.actions.push(todo);
    }

    if(this.actions.length === 1)
    {
        this.actions[0].go();
    }
    else
    {
        this.attempt_proceed();
    }
};

Todo_nextup.prototype.attempt_proceed = function()
{
    for(var i=0; i<this.actions.length - 1; i++)
    {
        if(this.actions[i] !== null && this.actions[i].im_finalized === true && this.actions[i+1].im_initiated === false)
        {
            this.actions[i+1].go();
        }
    }
};