如何阻塞正在被调用的方法,等前一次调用结束后才执行

发布于 2022-09-12 23:56:56 字数 278 浏览 16 评论 0

如何写一个阻塞的 javascript 方法,如果有其他正在调用则被阻塞直到前一次方法调用完成才执行。

var foobar = function(){
    setTimeout(function(){ console.log("hello"); },2000);
}

for(let i = 0;i< 1000; i++){
    foobar();
}

比如我想让上面的for循环每2000ms才执行一次循环

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

我ぃ本無心為│何有愛 2022-09-19 23:56:56

你的意思就是js实现睡眠吧

同步版本

var foobar = function(){
    console.log("hello")
}
const sleepSync = (ms) => {
  const end = new Date().getTime() + ms;
  while (new Date().getTime() < end) {}
}
for(let i = 0;i< 1000; i++){
    foobar();
    sleepSync(2000);
}

异步版本

const sleep = (ms) =>
  new Promise(resolve => setTimeout(resolve, ms));

const sayHello = async() => {
  for(let i = 0;i< 1000; i++){
    foobar();
    await sleep(2000);
  }
};

sayHello(); 
天暗了我发光 2022-09-19 23:56:56

一般程序不需要主动阻塞,这样会使程序变得低效,不能充分利用cpu时间,阻塞的场景一般都是IO调用如网络请求等。如果需要主动阻塞,通常是逻辑设计有问题。

如果是需要顺序调用接口,前一次接口调用返回后再调用下一个接口,建议可以这样改逻辑:

  • await写法
(async () => {
    let r1 = await fetch('http://api.example.com/api1')
    // 处理你的第一次请求
    let r2 = await fetch('http://api.example.com/api2')
    // 处理你的第二次请求
})()
  • Promise写法
fetch('http://api.example.com/api1').then(r => {
    // 处理你的第一次请求
    
    return fetch('http://api.example.com/api2')
}).then(r => {
   // 处理你的第二次请求
})
  • callback写法
fetch('http://api.example.com/api1', r1 => {
    // 处理你的第一次请求
    fetch('http://api.example.com/api2', r2 => {
        // 处理你的第二次请求
    })
})

如果不关系请求返回,可以将请求做成发送队列,调用的位置不要直接发请求,而是将请求和请求参数作为一个任务加入到发送队列中,然后在队列中完成发送

从来不烧饼 2022-09-19 23:56:56

看了楼上的评论区才知道你的需求。。。正确的提问方式应该是把这个需求描述放到问题中去,这样回答者能够给你更好、更直接的解决方案。
比如你这个单信道复用的实现,最好的方案就是设置一个缓冲队列,每次有数据要发送时,并不是直接去调用发送数据的方法,而是把数据写入缓冲队列,等前一个请求完成的时候,把队列最前面的数据发送出去。
以等待2秒为例,差不多就是这样实现:

const addTask = (function(){
    const taskQueue = [];
    let pendingFlag = false;
    
    function handler(task){
        setTimeout(() => {
            task();
            consumer();
        }, 2e3);
    }
    
    function consumer(){
        if(!taskQueue.length){
            pendingFlag = false;
            return
        }
        handler(taskQueue.shift());
    }
    
    return function addTask(task){
        taskQueue.push(task);
        if(!pendingFlag) {
            pendingFlag = true;
            consumer();
        }
    }
})();

for(let i = 0;i< 10; i++){
    addTask(() => console.log(`这是第${i + 1}个任务`));
}

但上面这是一个定制性的方案,每管理一种任务就需要完整地抄一遍,为了避免这种重复,我们可以写一个更加通用的解决方案:

function queueGen(processor){
    const taskQueue = [];
    let pendingFlag = false;
    
    async function handler([param, callback, errorHandler]){
        try{
            callback(await processor(param));
        } catch(err) {
            if(errorHandler){
                errorHandler(err)
            }
        }
        consumer();
    }
    
    function consumer(){
        if(!taskQueue.length){
            pendingFlag = false;
            return
        }
        handler(taskQueue.shift());
    }
    
    return function addTask(callback, param, errorHandler){
        taskQueue.push([param, callback, errorHandler]);
        if(!pendingFlag) {
            pendingFlag = true;
            consumer();
        }
    }
}

const addTask = queueGen(function asyncTimeOut(delay = 1e3){
    return new Promise(function(resolve, reject){
        setTimeout(resolve, delay)
    });
});

for(let i = 0;i< 10; i++){
    const delay = (10 % i) * 1e8
    addTask(() => console.log(`第${i + 1}个任务,延迟了${delay}毫秒`), delay);
}

剩下的任务就是把你的通信接口写成 Promise 风格交给 queueGen 处理一下,然后随时随地 addTask 就行了。

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文