在JavaScript/jQuery中处理多线程问题?

4
$(".getDetails").click(function() {
    // some stuff like fetching response from server
})

当用户在UI上连续点击getDetails按钮时,jquery会为click函数生成两个调用,我的逻辑会失败。我认为解决方法是在第一次点击时禁用按钮(这样用户就不能多次点击)。一旦我获得响应或者在从click方法返回之前,我将其启用。有更好的解决方案吗?
如果没有,如何使按钮在用户第一次单击按钮后立即禁用。我认为需要在调用click方法之前或某个html元素中执行此操作。
Java提供了synchronized关键字,以便只有一个线程进入方法,我不确定JavaScript中是否存在类似的东西。

点击处理程序中执行了什么逻辑?我猜测这是一个 AJAX 请求。 - Rory McCrossan
2
是的。您应该在第一次点击时禁用按钮,以防止连续点击。 - James
@james 关于我的问题,我需要在什么时间和地点进行?你能给一些例子吗? - user3198603
6个回答

2

假设单击处理程序执行 AJAX 请求,您可以在发出请求之前将按钮设置为禁用状态,然后在请求完成后再次启用它。请尝试以下代码:

$(".getDetails").click(function(){}
    var $btn = $(this).prop('disabled', true);

    $.ajax({
        url: '/foo'
        success: function() {
            console.log('It worked!');
        },
        error: function() {
            console.log('It failed!');
        }, 
        complete: function() {
            $btn.prop('disabled', false);
        }
    });
});

抱歉,我不明白JS是单线程的意思。这是否意味着第二个线程在第一个线程完成之前不能进入该方法?如果是的话,我就不应该遇到这个问题了,对吧? - user3198603
Js 只在单线程上运行。它运行在基于事件的系统上 - 这就是导致你的问题的原因。一个事件仍在处理中,而另一个事件已被触发。这个答案解决了这个问题。 - Rory McCrossan
不,多个事件可以同时运行。您似乎对线程的工作方式有些困惑。我建议您进行研究 - 尽管它与JS没有直接关系。 - Rory McCrossan
不是的 - 再次强调,多个事件可以同时处理。单线程并不意味着不能同时处理多个事件。 - Rory McCrossan
好的,看起来我现在开始理解了。这意味着单个线程可以同时以相同的方式运行进程(可能是循环轮换)。希望现在是正确的。 如果是的话,即使我按照您的解决方案方法进行操作,假设用户在 JS 线程执行之前在短时间内连续点击按钮两次,则在这种情况下,JS 仍将继续处理两个事件吗? - user3198603
显示剩余6条评论

0

这样可以确保您的按钮在收到响应之前不会触发异步请求两次。

function doAjaxReq() {
  /*
    Add your ajax operation here
    as a return value of doAjaxReq
    like so:
    
    return $.ajax({
      url: '/foo',
      type: 'POST',
      data: data
    })
    
    Since i can't use ajax here let's smilulate
    it useing a promise.
  */
  promise = new Promise(function(res, rej) {
    setTimeout(function(){
      res({foo: "bar"});
    }, 1000)
  })
  return promise;
}


/* 
Inside here you add the click handlder
only once use `elem.one('click'...`
*/
function addClickHandler(elem) {
  elem.one('click', function() {
    // do your ajax request and when its
    // done run `addClickHanlder` again
    // i'm using `.then` because of the promise,
    // you should be using `.done`.
    doAjaxReq().then(function(data) {
      console.log(data);
      addClickHandler(elem);
    });
  })
}

addClickHandler($(".getDetails"));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="getDetails">Get Details</button>


0

你可以尝试解绑点击事件,然后在 ajax 调用之后重新将点击绑定到那个类

$(".getDetails").click(function(){}
$(".getDetails").unbind('click');
 // some stuff like fetching response from server
 )

0

您可以使用简单的标志来防止多次触发逻辑:

var flag = true    
$(".getDetails").click(function() {

  if (flag) {
     flag = false;
     //your logic...

     //when your code ends (in after-AJAX callback for example)
     flag = true;
  }

});

1
但是在这种情况下,有可能两个线程同时获取到值为true,并且都发送请求。 - user3198603
@freedomn-m 抱歉,我没有理解语句“在js中不存在“两个线程””的意思?这是否意味着第二个线程不能进入方法,直到第一个线程完成。如果是的话,我就不应该遇到这个问题了。对吗? - user3198603
不,我的意思是JavaScript是单线程的。只有一个线程。一次只能运行一个“位”JavaScript - 所以,是的,在开始之前必须退出您的方法... 除非您运行某些东西说“稍后运行”,例如setTimeout$.ajax(和衍生品) - freedomn-m
@user3198603 我认为阅读一些关于多线程代码和异步代码的文章可能会对你有所帮助,以便了解它们之间的区别。 - Marcin Dusza

0
$(".getDetails").click(function(e){

  var $target = $(e.currentTarget); 
  // assuming the click listener is on the button

  $target.prop('disabled',true);

  // request, stuff...and when done:
  $target.prop('disabled',false);
})

0

尝试使用Prevent Default并返回false以避免任何其他事件传播,这个解决方案类似于信号量或监视器

var progress = false;
$(".getDetails").on('click', function(e) {
if(!progress){       
progress = true;
// some stuff like fetching response from server
//also  after sucessfull fetch make  true to false again
}else{
   console.log('something in progress'); 
}
   e.preventDefault();
   return false;

})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接