JavaScript或jQuery中的关键部分

Gre*_*eso 8 javascript jquery semaphore critical-section mutual-exclusion

我有一个网页,其中异步触发某个Ajax事件.这个Ajax部分可以被调用一次或多次.我无法控制触发此事件的次数,也无法控制时间.

此外,Ajax部分中有一些代码应作为关键部分运行,这意味着,当它运行时,不应该运行该代码的其他副本.

这是一个伪代码:

  1. 运行JavaScript或jQuery代码
  2. 输入作为Ajax的关键部分(当某个进程正在等待响应回调时,请不要再次进入此部分,直到此过程完成)
  3. 运行更多JavaScript或jQuery代码

我的问题是,我怎样才能按照上述方式运行第2步?如何使用JavaScript或jQuery创建/保证互斥部分.

我理解理论(信号量,锁,等等),但我无法使用JavaScript或jQuery实现解决方案.

编辑

如果你建议一个布尔变量进入临界区,这将不起作用,下面的行将解释原因.

关键部分的代码如下(使用布尔变量建议):

load_data_from_database = function () {

    // Load data from the database. Only load data if we almost reach the end of the page
    if ( jQuery(window).scrollTop() >= jQuery(document).height() - jQuery(window).height() - 300) {

        // Enter critical section
        if (window.lock == false) {

            // Lock the critical section
            window.lock = true;

            // Make Ajax call
            jQuery.ajax({
                type:        'post',
                dataType:    'json',
                url:         path/to/script.php,
                data:        {
                    action:  'action_load_posts'
                },
                success:     function (response) {
                    // First do some stuff when we get a response

                    // Then we unlock the critical section
                    window.lock = false;
                }

            });


            // End of critical section
        }
    }
};

// The jQuery ready function (start code here)
jQuery(document).ready(function() {
    var window.lock = false; // This is a global lock variable

    jQuery(window).on('scroll', load_data_from_database);
});
Run Code Online (Sandbox Code Playgroud)

现在这是使用布尔变量建议的锁定部分的代码.这不会如下所示:

  1. 用户向下滚动(并且基于该关联,jQuery(window).on('scroll', load_data_from_database);触发多个滚动事件.

  2. 假设几乎在同一时刻触发了两个滚动事件

  3. 两者都叫这个load_data_from_database功能

  4. 第一个事件检查if是否window.lockfalse(答案为true,所以如果语句正确)

  5. 第二个事件检查if是否window.lockfalse(回答为true,所以如果语句正确)

  6. 第一个事件进入if语句

  7. 第二个事件进入if语句

  8. 第一个语句设置window.locktrue

  9. 第二个陈述window.lock设为

  10. 第一个语句运行Ajax关键部分

  11. 第二个语句运行Ajax关键部分.

  12. 两者都完成了代码

正如您所注意到的,两个事件几乎同时被触发,并且都进入临界区.所以无法锁定.

Zig*_*ggy 8

我认为您上面提供的最有用的信息是您对锁定的分析.

  1. 用户向下滚动(并且基于该关联,jQuery(window).on('scroll', load_data_from_database);触发多个滚动事件.

  2. 假设几乎在同一时刻触发了两个滚动事件

  3. 两者都叫这个load_data_from_database功能

  4. 第一个事件检查if是否window.lockfalse(答案为true,所以如果语句正确)

  5. 第二个事件检查if是否window.lockfalse(回答为true,所以如果语句正确)

这就告诉我你已经达成了一个共同的(而且非常直观的)误解.

Javascript是异步的,但异步代码与并发代码不同.据我所知,"异步"意味着函数的子程序不一定按照我们在同步代码中所期望的深度优先顺序进行探索.一些函数调用(你称之为"ajax"的函数)将被放入队列并稍后执行.这可能会导致一些令人困惑的代码,但没有什么比认为您的异步代码同时运行更令人困惑."并发"(如您所知)是来自不同函数的语句可以相互交错的时候.

像锁和信号量这样的解决方案不是考虑异步代码的正确方法.承诺是正确的方式.这使得在网络上编程变得有趣和酷炫的东西.

我不是承诺大师,但这是一个工作小提琴(我认为)演示了一个修复.

load_data_from_database = function () {

    // Load data from the database. Only load data if we almost reach the end of the page
    if ( jQuery(window).scrollTop() >= jQuery(document).height() - jQuery(window).height() - 300) {

        console.log(promise.state());
        if (promise.state() !== "pending") {
            promise = jQuery.ajax({
                type:        'post',
                url:         '/echo/json/',
                data: {
                    json: { name: "BOB" },
                    delay: Math.random() * 10
                },
                success:     function (response) {

                    console.log("DONE");
                }
            });
        }
    }
};

var promise = new $.Deferred().resolve();

// The jQuery ready function (start code here)
jQuery(document).ready(function() {

    jQuery(window).on('scroll', load_data_from_database);
});
Run Code Online (Sandbox Code Playgroud)

我正在使用全局承诺来确保只调用一次事件处理程序的ajax部分.如果您在小提琴中向上和向下滚动,您将看到在处理ajax请求时,将不会发出新请求.ajax请求完成后,可以再次发出新请求.运气好的话,这就是你要找的行为.

但是,我的回答有一个非常重要的警告:jQuery的承诺实现是众所周知的.这不仅仅是人们所说的听起来很聪明的东西,它实际上非常重要.我建议使用不同的promise库并将其与jQuery混合使用.如果您刚刚开始了解承诺,这一点尤为重要.

编辑:就个人而言,我最近和你在同一条船上.差不多3个月前,我认为我使用的一些事件处理程序是交错的.当人们开始告诉我javascript是单线程时,我感到恍惚和不信.帮助我的是了解事件发生时会发生什么.

在同步编码中,我们习惯于"帧"的"堆栈"的概念,每个"帧"表示函数的上下文.在javascript和其他异步编程环境中,堆栈由队列扩充.当您在代码中触发事件或使用类似该$.ajax调用的异步请求时,您会将事件推送到此队列.该事件将在下次清除堆栈时处理.例如,如果你有这个代码:

function () {
    this.on("bob", function () { console.log("hello"); })

    this.do_some_work();
    this.trigger("bob");
    this.do_more_work();
}
Run Code Online (Sandbox Code Playgroud)

这两个函数do_some_workdo_more_work必火一前一后,立即执行.然后该函数将结束,您排队的事件将启动一个新的函数调用(在堆栈上),并且"hello"将出现在控制台中.如果在处理程序中触发事件,或者在子例程中触发事件,事情会变得更复杂.

这一切都很好,但是当你想要处理异常时,事情开始变得非常糟糕.当你进入异步土地的那一刻,你留下了"一个功能应该返回或抛出"的美丽誓言.如果您在事件处理程序中,并且抛出异常,它将被捕获到哪里?这个,

function () {

    try {
        $.get("stuff", function (data) {
            // uh, now call that other API
            $.get("more-stuff", function (data) {
                // hope that worked...
            };
        });
    } catch (e) {
        console.log("pardon me?");
    }
}
Run Code Online (Sandbox Code Playgroud)

现在不会救你 Promise允许你通过给你一种方法将你的回调链接在一起并控制他们返回的地点和时间来收回这个古老而强大的誓言.因此,使用一个很好的promises API(而不是jQuery),你可以用一种方式链接这些回调,让你以你期望的方式冒泡,并控制执行的顺序.在我的理解中,这是承诺的美丽和魔力.

如果我完全离开,有人会阻止我.

  • 这里是!我正在寻找这个:https://github.com/kriskowal/q/wiki/General-Promise-Resources这些视频很棒.我认真推荐观看它们.当你在外面,了解网络的秘密,谷歌"monad"也是.:) (2认同)