JavaScript工作队列

时间:2016-01-17 09:37:54

标签: javascript promise bluebird

我创建了这个包含数组的对象,该数组充当工作队列。

有点像这样:

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()的两次调用也可能失败。 (我不知道如何用“atomic”来描述它,因为JavaScript是单线程的,但是这个问题就出现了。)

我该怎么办?

2 个答案:

答案 0 :(得分:3)

问题是存在commit()但没有事务概念,因此您无法明确地并行运行两个独立的事务。根据我的理解,Javascript Workqueue是远程队列的代理,对add()commit()的调用直接映射到具有类似接口而没有事务的某种远程过程调用。我也明白你不会在意第二个add()实际发生在第一个commit()之后,你只想写两个简单的后续addWorkAndCommit()语句而不同步客户端代码中的底层调用。

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

答案 1 :(得分:1)

采用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);

无论你编写什么,你需要做的就是确保回调函数返回一个promise,它是所有组件异步(组件承诺)的集合。当聚合承诺结算时,处置者将确保交易已经提交。

与所有处理器一样,这个处理器没有做任何你不能做的事情。不过它:

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

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