我创建了这个包含数组的对象,该数组充当工作队列。
有点像这样:
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()
实现将add
和commit
包装在一起,但仍然无法保证因为addWorkAndCommit()
在某种意义上不能是“原子”的,因为它们涉及异步调用。因此,即使对addWorkAndCommit()
的两次调用也可能失败。 (我不知道如何用“atomic”来描述它,因为JavaScript是单线程的,但是这个问题就出现了。)
我该怎么办?
答案 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()
。