Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/454.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
单线程JavaScript下的动画_Javascript_Jquery Animate_Single Threaded - Fatal编程技术网

单线程JavaScript下的动画

单线程JavaScript下的动画,javascript,jquery-animate,single-threaded,Javascript,Jquery Animate,Single Threaded,JavaScript是一种单线程语言,因此它一次只执行一个命令。异步编程是通过浏览器管理的Web API(DOM用于事件处理,XMLHttpRequest用于AJAX调用,WindowTimer用于setTimeout)和事件队列来实现的。到目前为止,一切都很好!现在考虑下面的非常简单的代码: $('#mybox').hide(17000); console.log('Previous command has not yet terminated!'); ... 有人能给我解释一下上述的潜在

JavaScript是一种单线程语言,因此它一次只执行一个命令。异步编程是通过浏览器管理的Web API(DOM用于事件处理,XMLHttpRequest用于AJAX调用,WindowTimer用于setTimeout)和事件队列来实现的。到目前为止,一切都很好!现在考虑下面的非常简单的代码:

$('#mybox').hide(17000);
console.log('Previous command has not yet terminated!');
... 
有人能给我解释一下上述的潜在机制吗?由于.hide()尚未完成(动画持续17秒),JS引擎正在处理它,并且它能够一次执行一个命令,因此它以何种方式转到下一行并继续运行剩余的代码

如果您的答案是动画创建承诺,那么问题仍然是相同的:JavaScript如何同时处理多个事情(执行动画本身,在承诺的情况下观察动画队列并继续执行下面的代码…)


此外,我无法解释jQuery中的承诺是如何工作的,如果它们必须监视它们的父延迟对象,直到它被解析或拒绝,这意味着代码执行,同时执行剩余的代码。在单线程方法中如何实现这一点?我理解AJAX调用没有问题,因为我知道它们是从JS引擎中删除的…

javascript中有几种函数: 阻塞和非阻塞

非阻塞函数将立即返回,事件循环在后台等待调用回调函数时继续执行(如Ajax承诺)

动画依赖于setInterval和/或setTimeout,这两种方法立即返回,允许代码继续。回调被推回事件循环堆栈,执行,主循环继续

希望这会有帮助

您可以获得更多信息或

事件循环 JavaScript使用的是所谓的。事件循环类似于
while(true)
循环

为了简化它,假设JavaScript有一个巨大的数组,它在其中存储所有事件。事件循环在此事件循环中循环,从最早的事件开始循环到最新的事件。也就是说,JavaScript执行如下操作:

while (true) {
     var event = eventsArray.unshift();

     if (event) {
       event.process();
     }
}
如果在事件处理过程中(
event.process
),触发了一个新事件(我们称之为
eventA
),则新事件将保存在
eventsArray
中,并继续执行当前事件。当前事件处理完成后,将处理下一个事件,依此类推,直到到达
eventA

来到您的示例代码

$('#mybox').hide(17000);
console.log('Previous command has not yet terminated!');
执行第一行时,将创建事件侦听器并启动计时器。假设jQuery使用100ms帧。创建了一个100ms的计时器,带有回调函数。计时器开始在后台运行(该实现在浏览器内部),同时控件返回给脚本。因此,当计时器在后台运行时,脚本继续执行第二行。100毫秒后,计时器结束,并触发一个事件。此事件保存在上面的
eventsArray
中,不会立即执行。代码执行完毕后,JavaScript会检查
eventsArray
,发现有一个新事件,然后执行它

然后运行事件,您的div或任何元素移动几个像素,新的100ms计时器启动


请注意,这是一个简化,而不是整个事情的实际工作。整件事有一些复杂的地方,比如堆栈和所有的东西。有关更多信息,请参阅MDN文章。

tl;博士如果没有外部帮助,在严格的单线程环境中是不可能实现的


我想我理解你的问题。让我们把一些事情弄清楚:

JavaScript总是同步的 语言规范中未定义异步API。所有函数,如
Array.prototype.map
String.fromCharCode
始终同步运行*

代码将始终运行到完成。代码在被返回、隐式返回(到达代码末尾)或抛出(突然)终止之前不会停止运行

JavaScript存在于一个平台中 JavaScript语言定义了一个称为:

通过这种方式,可以说现有系统提供了对象和设施的主机环境,从而完善了脚本语言的功能

在浏览器中运行JavaScript的主机环境称为或文档对象模型。它指定浏览器窗口如何与JavaScript语言交互。例如,在NodeJS中,主机环境是完全不同的

虽然所有JavaScript对象和函数都同步运行直至完成,但主机环境可能会公开自己的函数,这些函数不一定在JavaScript中定义。它们没有与标准JavaScript代码相同的限制,并且可能定义不同的行为-例如
文档的结果。getElementsByClassName
是一个活动DOM,它的行为与普通JavaScript代码非常不同:

var els = document.getElementsByClassName("foo"); 
var n = document.createElement("div");
n.className = "foo";
document.body.appendChild(n);
els.length; // this increased in 1, it keeps track of the elements on the page
            // it behaves differently from a JavaScript array for example. 
其中一些主机功能必须执行I/O操作,如调度计时器、执行网络请求或执行文件访问。与所有其他API一样,这些API必须运行到完成。这些API由主机平台调用,它们调用代码不具备的功能——通常(但不一定),它们是用C++编写的,并使用线程和操作系统工具同时并行运行。这种并发性可以是后台工作(比如调度计时器)或实际的并行性(同样是DOM的一部分,而不是JavaScript)

所以,当你调用
var els = document.getElementsByClassName("foo"); 
var n = document.createElement("div");
n.className = "foo";
document.body.appendChild(n);
els.length; // this increased in 1, it keeps track of the elements on the page
            // it behaves differently from a JavaScript array for example. 
setTimeout(function() {
   console.log("World");
});
console.log("Hello");
$('#mybox').hide(17000);
console.log('Previous command has not yet terminated!');
function hide(element, howLong) {
    var o = 16 / howLong; // calculate how much opacity to reduce each time
    //  ask the host environment to call us every 16ms
    var t = setInterval(function
        // make the element a little more transparent
        element.style.opacity = (parseInt(element.style.opacity) || 1) - o;
        if(parseInt(element.style.opacity) < o) { // last step
           clearInterval(t); // ask the platform to stop calling us
           o.style.display = "none"; // mark the element as hidden
        }
    ,16);
}
function (speed, easing, callback) {
    return speed == null || typeof speed === "boolean" 
    ? cssFn.apply(this, arguments) 
    : this.animate(genFx(name, true), speed, easing, callback);
}
function animate(prop, speed, easing, callback) {
    var empty = jQuery.isEmptyObject(prop),
        optall = jQuery.speed(speed, easing, callback),
        doAnimation = function () {
        // Operate on a copy of prop so per-property easing won't be lost
        var anim = Animation(this, jQuery.extend({},
        prop), optall);

        // Empty animations, or finishing resolves immediately
        if (empty || jQuery._data(this, "finish")) {
            anim.stop(true);
        }
    };
    doAnimation.finish = doAnimation;

    return empty || optall.queue === false ? this.each(doAnimation) : this.queue(optall.queue, doAnimation);
}
function Animation(elem, properties, options) {
    var result, stopped, index = 0,
        length = animationPrefilters.length,
        deferred = jQuery.Deferred().always(function () {
        // don't match elem in the :animated selector
        delete tick.elem;
    }),
        tick = function () {
        if (stopped) {
            return false;
        }
        var currentTime = fxNow || createFxNow(),
            remaining = Math.max(0, animation.startTime + animation.duration - currentTime),
        // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
        temp = remaining / animation.duration || 0,
            percent = 1 - temp,
            index = 0,
            length = animation.tweens.length;

        for (; index < length; index++) {
            animation.tweens[index].run(percent);
        }

        deferred.notifyWith(elem, [animation, percent, remaining]);

        if (percent < 1 && length) {
            return remaining;
        } else {
            deferred.resolveWith(elem, [animation]);
            return false;
        }
    },
        animation = deferred.promise({
        elem: elem,
        props: jQuery.extend({},
        properties),
        opts: jQuery.extend(true, {
            specialEasing: {}
        },
        options),
        originalProperties: properties,
        originalOptions: options,
        startTime: fxNow || createFxNow(),
        duration: options.duration,
        tweens: [],
        createTween: function (prop, end) {
            var tween = jQuery.Tween(elem, animation.opts, prop, end, animation.opts.specialEasing[prop] || animation.opts.easing);
            animation.tweens.push(tween);
            return tween;
        },
        stop: function (gotoEnd) {
            var index = 0,
            // if we are going to the end, we want to run all the tweens
            // otherwise we skip this part
            length = gotoEnd ? animation.tweens.length : 0;
            if (stopped) {
                return this;
            }
            stopped = true;
            for (; index < length; index++) {
                animation.tweens[index].run(1);
            }

            // resolve when we played the last frame
            // otherwise, reject
            if (gotoEnd) {
                deferred.resolveWith(elem, [animation, gotoEnd]);
            } else {
                deferred.rejectWith(elem, [animation, gotoEnd]);
            }
            return this;
        }
    }),
        props = animation.props;

    propFilter(props, animation.opts.specialEasing);

    for (; index < length; index++) {
        result = animationPrefilters[index].call(animation, elem, props, animation.opts);
        if (result) {
            return result;
        }
    }

    jQuery.map(props, createTween, animation);

    if (jQuery.isFunction(animation.opts.start)) {
        animation.opts.start.call(elem, animation);
    }

    jQuery.fx.timer(
    jQuery.extend(tick, {
        elem: elem,
        anim: animation,
        queue: animation.opts.queue
    }));

    // attach callbacks from options
    return animation.progress(animation.opts.progress).done(animation.opts.done, animation.opts.complete).fail(animation.opts.fail).always(animation.opts.always);
}