JavaScript Work Queue

I created this object that contains an array that serves as a work queue.

It’s kind of like:

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

It works in this case, and I can complete several tasks before I call commit.

However, if so:

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

Something may go wrong, because if the first commit is called after the second commit (two jobs / tasks have already been added), the first transaction handler expects a result, but they all go to the second transaction handler.

The task includes reading the Web SQL database and may also include access to the network. Thus, this is basically a complicated procedure, so the problem described above may arise. If I could implement addWorkAndCommit() , which combines add and commit together, but still there is no guarantee, because addWorkAndCommit() cannot be "atomic" in a sense, because they include asynchronous calls. Thus, even two calls to addWorkAndCommit() may fail. (I don’t know how to describe it other than “atomic”, since JavaScript is single-threaded, but this question arises).

What can I do?

+5
source share
2 answers

The problem is that there is commit() , but there is no transaction concept, so you cannot explicitly have two isolated transactions running in parallel. In my opinion, the Javascript Workqueue is the proxy server for the remote queue, and the add() and commit() calls are mapped directly to some remote procedure calls that have a similar interface without transactions. I also understand that you would not care if the second add() really happened after the first commit() , you just want to write two simple subsequent addWorkAndCommit() statements without synchronizing the base calls in the client code.

What you can do is write a wrapper around the local Workqueue (or change it directly if this is your code), so that each queue update creates a new transaction, and commit() always refers to one such transaction. The shell then delays new updates until all previous transactions are completed (or rolled back).

+3
source

Having accepted Benjamin Grunbaum’s recommendation to use the deletion pattern, here is one written as an adapter method for 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(); }); } 

Now you can write:

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

In practice, an error handler should be included as follows: Workqueue.transaction(function() {...}).catch(errorHandler);

All you write, all you have to do is make sure that the callback function returns a promise, which is the collection of all component asynchronizations (component promises). When the collective promise is resolved, the manager will ensure that the transaction is completed.

Like all stewards, this one does nothing that you cannot do without it. However, this:

  • serves as a reminder of what you are doing by providing a named .transaction() method,
  • applies the concept of a single transaction, restricting Workqueue.instance() to one commit.

If for some reason you ever need to execute two or more transactions in the same queue (why?), You can always return directly to the Workqueue.instance() call.

+1
source

Source: https://habr.com/ru/post/1240794/


All Articles