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()
将 add
和 commit
包装在一起,但仍然不能保证,因为 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()
。
我创建了这个对象,它包含一个数组,用作工作队列。
它的工作原理是这样的:
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()
将 add
和 commit
包装在一起,但仍然不能保证,因为 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()
。