javascript javascript中类似信号量的队列?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17528749/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 08:40:29  来源:igfitidea点击:

Semaphore-like queue in javascript?

javascript

提问by Karel Bílek

I have a variable can_run, that can be either 1 or 0, and then I have a queue of functions, that should be run as soon as the variable is switched from 0to 1(but only 1 such function at a time).

我有一个变量can_run,它可以是 1 或 0,然后我有一个函数队列,它应该在变量从 切换0到 时立即运行1(但一次只有 1 个这样的函数)。

Right now, what I do is

现在,我要做的是

var can_run=1;
function wait_until_can_run(callback) {
    if (can_run==1) {
        callback();
    } else {
        window.setTimeout(function(){wait_until_can_run(callback)},100);
    }
}

//...somewhere else...

wait_until_can_run( function(){
   can_run=0;
   //start running something
});

//..somewhere else, as a reaction to the task finishing..
can_run=1;

It works, however, it doesn't strike me as very efficient to have about 100 timeouts continuously running. Something like semaphore would be handy in here; but in general, semaphores are not really needed in JavaScript.

它有效,但是,让我连续运行大约 100 次超时并没有那么高效。像信号量这样的东西在这里会很方便;但总的来说,JavaScript 中并不真正需要信号量。

So, what to use here?

那么,在这里使用什么?

edit: I have written "queue of functions" but as seen here, I don't really care about the order.

编辑:我已经写了“函数队列”,但正如这里所见,我并不真正关心顺序。

回答by Naftali aka Neal

Here is a nice Queue class you can use withoutthe use of timeouts:

这是一个不错的 Queue 类,您可以在使用超时的情况下使用它:

var Queue = (function () {

    Queue.prototype.autorun = true;
    Queue.prototype.running = false;
    Queue.prototype.queue = [];

    function Queue(autorun) {
        if (typeof autorun !== "undefined") {
            this.autorun = autorun;
        }
        this.queue = []; //initialize the queue
    };

    Queue.prototype.add = function (callback) {
        var _this = this;
        //add callback to the queue
        this.queue.push(function () {
            var finished = callback();
            if (typeof finished === "undefined" || finished) {
                //  if callback returns `false`, then you have to 
                //  call `next` somewhere in the callback
                _this.dequeue();
            }
        });

        if (this.autorun && !this.running) {
            // if nothing is running, then start the engines!
            this.dequeue();
        }

        return this; // for chaining fun!
    };

    Queue.prototype.dequeue = function () {
        this.running = false;
        //get the first element off the queue
        var shift = this.queue.shift();
        if (shift) {
            this.running = true;
            shift();
        }
        return shift;
    };

    Queue.prototype.next = Queue.prototype.dequeue;

    return Queue;

})();

It can be used like so:

它可以像这样使用:

// passing false into the constructor makes it so 
// the queue does not start till we tell it to
var q = new Queue(false).add(function () {
    //start running something
}).add(function () {
    //start running something 2
}).add(function () {
    //start running something 3
});

setTimeout(function () {
    // start the queue
    q.next();
}, 2000);

Fiddle Demo: http://jsfiddle.net/maniator/dUVGX/

小提琴演示:http: //jsfiddle.net/maniator/dUVGX/



Updated to use es6 and new es6 Promises:

更新为使用 es6 和新的 es6 Promises:

class Queue {  
  constructor(autorun = true, queue = []) {
    this.running = false;
    this.autorun = autorun;
    this.queue = queue;
  }

  add(cb) {
    this.queue.push((value) => {
        const finished = new Promise((resolve, reject) => {
        const callbackResponse = cb(value);

        if (callbackResponse !== false) {
            resolve(callbackResponse);
        } else {
            reject(callbackResponse);
        }
      });

      finished.then(this.dequeue.bind(this), (() => {}));
    });

    if (this.autorun && !this.running) {
        this.dequeue();
    }

    return this;
  }

  dequeue(value) {
    this.running = this.queue.shift();

    if (this.running) {
        this.running(value);
    }

    return this.running;
  }

  get next() {
    return this.dequeue;
  }
}

It can be used in the same way:

它可以以相同的方式使用:

const q = new Queue(false).add(() => {
    console.log('this is a test');

    return {'banana': 42};
}).add((obj) => {
    console.log('test 2', obj);

    return obj.banana;
}).add((number) => {
    console.log('THIS IS A NUMBER', number)
});

// start the sequence
setTimeout(() => q.next(), 2000);

Although now this time if the values passed are a promise etc or a value, it gets passed to the next function automatically.

虽然现在如果传递的值是承诺等或值,它会自动传递给下一个函数。

Fiddle: http://jsfiddle.net/maniator/toefqpsc/

小提琴:http: //jsfiddle.net/maniator/toefqpsc/

回答by Thinking Sites

I'm not sure the best way to do this in plain JS but many libraries have Deferred implementations which are very useful for this use case.

我不确定在普通 JS 中执行此操作的最佳方法,但许多库都有 Deferred 实现,这对于此用例非常有用。

With jQuery:

使用 jQuery:

var dfd = $.Deferred();
var callback = function() { 
    // do stuff
};
dfd.done(callback);  // when the deferred is resolved, invoke the callback, you can chain many callbacks here if needed
dfd.resolve(); // this will invoke your callback when you're ready

EDITOne of the nice things about these library supported deferreds are that they are normally compatible with Ajax events, and in turn other Deferred objects so you can create complex chains, trigger events on Ajax complete, or trigger the 'done' callback after multiple conditions are met. This is of course more advanced functionality, but it's nice to have in your back pocket.

编辑这些库支持的延迟的好处之一是它们通常与 Ajax 事件兼容,进而与其他延迟对象兼容,因此您可以创建复杂的链,在 Ajax 完成时触发事件,或在多个条件后触发“完成”回调会面。这当然是更高级的功能,但放在后口袋里也不错。

回答by 1983

In addition to the other useful answers here, if you don't need the extras that those solutions provide, then an asynchronous semaphoreis straightforward to implement.

除了这里的其他有用答案之外,如果您不需要这些解决方案提供的额外功能,那么 异步信号量很容易实现。

It's a lower-level concept than the other options presented here though, so you may find those to be more convenient for your needs. Still, I think asynchronous semaphores are worth knowing about, even if you use higher-level abstractions in practice.

不过,与此处介绍的其他选项相比,这是一个较低级别的概念,因此您可能会发现它们更适合您的需求。尽管如此,我认为异步信号量值得了解,即使您在实践中使用更高级别的抽象。

It looks something like this:

它看起来像这样:

var sem = function(f){
    var busy = 0;
    return function(amount){
        busy += amount;
        if(busy === 0){
            f();
        }
    };
};

And you invoke it like this:

你像这样调用它:

var busy = sem(run_me_asap);

busyis a function that maintains an internal counter of asynchronous actions that it is waiting on. When that internal counter reaches zero, it fires the function run_me_asap, which you supply.

busy是一个函数,用于维护它正在等待的异步操作的内部计数器。当该内部计数器达到零时,它会触发run_me_asap您提供的函数 。

You can increment the internal counter prior to running an asynchronous action with busy(1)and, then the asynchronous actions is responsible for decrementing the counter with busy(-1)once it is complete. This is how we can avoid the need for timers. (If you prefer, you could write semso that it returns an object with incand decmethods instead, like in the Wikipedia article; this is just how I do it.)

您可以在运行异步操作之前使用busy(1)and递增内部计数器,然后异步操作负责在完成后递减计数器busy(-1)。这就是我们可以避免使用计时器的方法。(如果你愿意,你可以sem这样写,让它返回一个带有incdec方法的对象,就像在维基百科文章中一样;这就是我这样做的方式。)

And that's all you have to do to create an asynchronous semaphore.

这就是创建异步信号量所需要做的全部工作。

Here's an example of it in use. You might define the function run_me_asapas follows.

这是它的使用示例。您可以run_me_asap按如下方式定义函数 。

var funcs = [func1, func2, func3 /*, ...*/];
var run_me_asap = function(){
    funcs.forEach(function(func){
        func();
    });
});

funcsmight be the list of functions that you wanted to run in your question. (Maybe this isn't quite what you want, but see my 'N.B.' below.)

funcs可能是您要在问题中运行的函数列表。(也许这不是您想要的,但请参阅下面的“注意事项”。)

Then elsewhere:

然后在别处:

var wait_until_ive_finished = function(){
    busy(1);
    do_something_asynchronously_then_run_callback(function(){
        /* ... */
        busy(-1);
    });
    busy(1);
    do_something_else_asynchronously(function(){
        /* ... */
        busy(-1);
    });
};

When both asynchronous operations have completed, busy's counter will be set to zero, and run_me_asapwill be invoked.

当两个异步操作都完成后,busy的计数器将被设置为零,run_me_asap并将被调用。

N.B.How youmight use asynchronous semaphores depends on the architecture of your code and your own requirements; what I've set out may not be exactly what you want. I'm just trying to show you how they work; the rest is up to you!

NB如何,你可能会使用异步信号量取决于您的代码和您自己的要求的架构; 我提出的可能不是你想要的。我只是想向你展示它们是如何工作的;剩下的就靠你了!

And, one word of advice: if you were to use asynchronous semaphores then I'd recommend that you hide their creation and the calls to busybehind higher-level abstractions so that you're not littering your application code with low-level details.

而且,一个忠告:如果您要使用异步信号量,那么我建议您隐藏它们的创建和对busy更高级别抽象背后的调用,这样您就不会用低级细节乱扔应用程序代码。