5

First of all rollback is something that I do not care about.

I would like to be able to lock a sequence of async functions/promises/tasks (let's call it a "transaction") with a name/id (or array of names), so that they happen in sequence, and so that any other "transaction" with the same name(s) that are run by another part of the system are delayed from starting until the running transaction using the same name(s) has completed. So it basically is queueing the sequences of async tasks, or "transaction"s.

Here is some example code of the situation:

function a()
{
  // do stuff
  return new Promise(/*...*/);
}


function b()
{
  // do stuff
  return new Promise(/*...*/);
}



function c()
{
  // do stuff
  return a.then(() => b());
}

Now at any time the system could call a, b, or c, and when it does I don't want c and b running at the same time, but obvious c depends on b.

I've been looking for a package on npm to help with this but I haven't found anything, I wonder if anyone can suggest something that I might have missed that would help with this?

4
  • 1
    I don't really see what the question is. If you call c().then(...), does that not do exactly what you want? Also, I think the body of c() is supposed to be return a().then(() => b()), not return a.then(() => b()) Commented Jul 3, 2017 at 18:27
  • Why not just using an shared-scope variable as a flag to know if there are already a B or C function executing? Commented Jul 4, 2017 at 12:02
  • If there are conditions upon calling a function, sounds like what you need is a delegator. Since b returns a promise, I'm guessing what you want is for a call to b to wait until c is no longer being processed. You'd then have the delegator wait for a flag before calling b. Commented Jul 6, 2017 at 20:01
  • Is this for one of those web pages that sometimes don't load and get stuck. Commented Jul 6, 2017 at 21:23

6 Answers 6

8
+25

I think gulp tasks can help you out of the box. This guarantees that c always run after b and so b after a

const gulp = require('gulp');
gulp.task('a', done => {
  // do stuff
  console.log('a');
  done();
});

gulp.task('b', ['a'], done => {
  // do stuff
  console.log('b');
  done();
});

gulp.task('c', ['b'], done => {
  // do more stuff
  console.log('c');
  done();
});

gulp.start('c'); // Logs a, b, c

Try it!

Sign up to request clarification or add additional context in comments.

Comments

4

You could write your own little transaction manager.

const transactions = {};

function doTransaction(name, promiseFunc) {
  transactions[name] = (transactions[name] || Promise.resolve()).then(promiseFunc);
}

5 Comments

I have a chain of promises that make up the transaction though, not just one promise.
I added a code example of what I am describing to the question.
I don't understand. doTransaction can be called any number of times, and promises with the same name are executed in sequence, as I thought you wanted.
so in my example if b calls doTransaction and c calls doTransaction, then c will never resolve, because when c is called, it will start a transaction, then b is called within c and it starts a transaction, then the second transaction will hang waiting for the first, and the first transaction will hang waiting for the second. So c never resolves.
Each transaction, a, b, and c would get it's own name. c would call doTransaction with the names for a and b, thus preventing the loop you have described.
4

Use async/await and have babel transpile it. Async Babel Docs

function a()
{
  // do stuff
  return new Promise(/*...*/);
}


async function b()
{
  const aData = await a();
  // do stuff
  return new Promise(/*...*/);
}



async function c()
{
  const bData = await b();
  // do stuff
  return bData;
}

Comments

3

You can go for https://github.com/Reactive-Extensions/RxJS

They have many functions to handle single/multiple/dependent/parallel async calls.

1 Comment

hmm I'm not understanding how RxJS handles my issues after briefly looking at the docs, maybe you could provide some more insight for me? maybe a code example?
2
function a()
{
  // do stuff
  return new Promise(/*...*/);
}

function b()
{
  // do stuff
  return new Promise(/*...*/);
}

function c()
{
  // do stuff
  return new Value;
}

a().then(function(data_a) {
  // you can make use of return value (which is data_a) here or as an argument for function b or even function c
  b().then(function(data_b) {
    // you can make use of return value (which is data_b) here or as an argument for function c
    c().then(function(data_c) {
      // do your coding here
    });
  });
});

you can check this link for reference : https://spring.io/understanding/javascript-promises

Comments

2

Ok, here's my take. You use a wrapper for your function b which returns and object with 2 methods: doCall and wait. The wrapper should be called only once.

doCall will call your function and trace its completion for the wait() function.

wait() will wait for the completion and always resolve when doCall() finishes

Now for the code, also on CodePen (see developer console):

function wrapPromiseFn(fn) {
  var prev = null;
  var doCall = function() {
    var retValue;

    prev = new Promise(function(resolve, reject) {
      retValue = fn();
      retValue.then(function(result) {
        resolve(true);
      });
      retValue.catch(function(result) {
        resolve(true);
      });
    });

    return retValue;
  };

  var wait = function() {
    return prev ? prev : Promise.resolve(false);
  };

  return {
    doCall: doCall,
    wait: wait
  };
}

function a() {
  return Promise.resolve(42);
}

function b() {
  //return Promise.reject(new Date());
  return Promise.resolve(new Date().getTime());
}

var wrappedB = wrapPromiseFn(b);

function doStuff() {
  return wrappedB.wait().then(function(didWait) {
    return a().then(function(_a) {
      return wrappedB.doCall().then(function(_b) {
        console.log("didWait, a, b: ", didWait, _a, _b);
      });
    });
  });
}

//run it twice to prove it did wait
doStuff().then(doStuff)

It proves the concept, of course it would need some polish to pass arguments from doCall to the wrapped function.

1 Comment

another usage for making sure b does not run more than once at any given time (i.e. concurrency=1): wrappedB.wait().then(wrappedB.doCall())

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.