【问题标题】:JavaScript work queueJavaScript 工作队列
【发布时间】:2016-04-22 13:47:58
【问题描述】:

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

它的工作原理是这样的:

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){});

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

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

我能做什么?

【问题讨论】:

标签: javascript promise bluebird


【解决方案1】:

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

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

【讨论】:

  • 哇哦!多么简单的解决方案。一个交易真正意味着什么的尤里卡时刻。谢谢我试图实现同步性。
  • 现在我脑海中的实现是事务应该是一个应该暴露给队列客户端的对象。客户端明确知道将工作添加到哪个事务中。对吗?
  • 视情况而定。如果你想在你的界面中使用单独的 add() 和 commit() 方法,那么可以。如果您将 addWorkAndCommit() 作为一种方法,则调用者不需要了解有关事务的任何信息。
【解决方案2】:

采用 Benjamin Gruenbaum 的建议使用 disposer 模式,这里有一个,写成 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);

无论你写什么,你需要做的就是确保回调函数返回一个 Promise,它是所有组件异步的聚合(组件 Promise)。当聚合 promise 解决时,disposer 将确保事务被提交。

与所有处置器一样,这个处置器不会做任何没有它就做不到的事情。然而它:

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

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

【讨论】:

  • 多么漂亮的解决方案!非常感谢。
  • 事务的概念是否应该扩展到每一层接口?似乎当我有交易时,错误仍然存​​在。工作结果仍可能未定义。
  • 嗯,不太确定。其他解决方案是否也存在同样的问题?
  • 我再次回顾这段代码,当 Promise.resolve(work(queue)) 正在“解决”时,Promise.all(promise1, promise2)` 不是已经完成执行了吗?它们实际上并没有在提交中的某个时刻执行,对吧?
  • 没关系,我的错,我需要刷新一下我的记忆。
猜你喜欢
  • 2018-06-20
  • 2018-09-11
  • 1970-01-01
  • 2018-07-30
  • 2013-05-30
  • 1970-01-01
  • 2015-03-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多