JavaScript 工作队列

JavaScript work queue

我创建了这个对象,它包含一个数组,用作工作队列。

它的工作原理是这样的:

var work1 = new Work();
var work2 = new Work();
var queue = Workqueue.instance();

queue.add(work1) // Bluebird promise.
.then(function addWork2() {
  return queue.add(work2);
})
.then(function toCommit() {
  return queue.commit();
})
.then(function done(results) {
  // obtain results here.
})
.catch(function(err){});

它在那种情况下有效,我可以在调用提交之前提交多个任务。

但是如果是这样的话:

var work1 = new Work();
var work2 = new Work();
var queue = Workqueue.instance();

queue.add(work1)
.then(function toCommit1() {
  return queue.commit();
})
.then(function done1(result1) {
  // obtain result1 here.
})
.catch(function(err){});

queue.add(work2)
.then(function toCommit2() {
  return queue.commit();
})
.then(function done2(result2) {
  // obtain result2 here.
})
.catch(function(err){});

可能会出错,因为如果在第二次提交之后调用第一次提交(已经添加了两个 works/tasks),第一个提交处理程序需要一个结果,但它们都会转到第二个提交处理程序。

该任务涉及Web SQL 数据库读取,也可能涉及网络访问。所以这基本上是一个复杂的过程,所以上述问题可能会浮出水面。如果我能实现一个 addWorkAndCommit()addcommit 包装在一起,但仍然不能保证,因为 addWorkAndCommit() 在某种意义上不能是 "atomic"因为它们涉及异步调用。因此,即使两次调用 addWorkAndCommit() 也可能会失败。 (除了 "atomic",我不知道如何描述它,因为 JavaScript 是单线程的,但这个问题突然出现了)。

我能做什么?

问题是有一个 commit() 但没有事务的概念,因此您不能明确地并行处理两个孤立的事务 运行。根据我的理解,Javascript Workqueue 是远程队列的代理,对 add()commit() 的调用直接映射到某种具有类似接口的远程过程调用,但没有交易。我还了解到您不会关心第二个 add() 是否真的发生在第一个 commit() 之后,您只想编写两个简单的后续 addWorkAndCommit() 语句而不同步客户端代码中的底层调用。

你可以做的是围绕本地 Workqueue 编写一个包装器(或者如果是你的代码则直接更改它),这样队列的每次更新都会创建一个新事务和一个 commit() 总是指一个这样的交易。然后包装器延迟新的更新,直到所有以前的事务都被提交(或回滚)。

采纳 Benjamin Gruenbaum 的建议使用处理器模式,这里是一个,作为 Workqueue.instance() 的适配器方法编写:

Workqueue.transaction = function (work) { // `work` is a function
    var queue = this.instance();
    return Promise.resolve(work(queue)) // `Promise.resolve()` avoids an error if `work()` doesn't return a promise.
    .then(function() {
        return queue.commit();
    });
}

现在你可以写:

// if the order mattters, 
// then add promises sequentially.
Workqueue.transaction(function(queue) {
    var work1 = new Work();
    var work2 = new Work();
    return queue.add(work1)
    .then(function() {
        return queue.add(work2);
    });
});
// if the order doesn't mattter, 
// add promises in parallel.
Workqueue.transaction(function(queue) {
    var work1 = new Work();
    var work2 = new Work();
    var promise1 = queue.add(work1);
    var promise2 = queue.add(work2);
    return Promise.all(promise1, promise2);
});
// you can even pass `queue` around
Workqueue.transaction(function(queue) {
    var work1 = new Work();
    var promise1 = queue.add(work1);
    var promise2 = myCleverObject.doLotsOfAsyncStuff(queue);
    return Promise.all(promise1, promise2);
});

实际上,应该像这样包含一个错误处理程序 - Workqueue.transaction(function() {...}).catch(errorHandler);

无论你写什么,你需要做的就是确保回调函数returns一个承诺,它是所有组件异步的聚合(组件承诺)。当聚合承诺解决时,处理器将确保提交事务。

与所有处理器一样,没有它就不能做任何事情。然而它:

  • 通过提供命名的 .transaction() 方法来提醒您正在做什么,
  • 通过将 Workqueue.instance() 限制为 一个 提交来强制执行单个事务的概念。

如果出于任何原因您需要在同一个队列上执行两次或更多次提交(为什么?),那么您可以随时恢复为直接调用 Workqueue.instance()