1

I have a batch script written in JS, executed under Node.

This is a very linear, logically synchronous batch script -- Thing A has to happen before Thing B, etc. This thing needs to execute from top to bottom, ideally line by line, synchronously.

(Performance is not that important here. The execution time would be 30 seconds, worst case. Additionally, there's no UI to lock up while the code blocks. This will only ever be run in batch from the command line, and I'll probably be watching it execute every time.)

I've been working through various options:

  • the "async" npm module
  • writing my own Promises
  • using async and await

I really like the last one, just from an understandability standpoint. It "degrades" JavaScript to simple, linear execution, which is frankly what I want.

go()

async function go() {
  await doThingOne()
  await doThingTwo()
}

However, sometimes I have code inside my methods which comes from third-party modules, and is asynchronous with no synchronous option.

Consider:

go()

async function go() {
  await doThingOne()
  await doThingTwo()
}

function doThingOne() {
  // do some things here
  doAsyncThingFromExternalLibrary()
  // do some more things here
}

How do I await that third-party call? It's written asynchronously, has no synchronous option, and is not code that I would want to change.

Is there a way to "wrap" that call so the code stops and waits for it? Should I wrap it in my own Promise and make it thenable?

What's the best way to do this?

Deane
  • 8,269
  • 12
  • 58
  • 108
  • 1
    You would create a wrapper method, that returns a promise. – Get Off My Lawn Jul 23 '18 at 15:49
  • If it doesn't already return a promise, wrapping it in a function that returns a promise is the way I'd do it – Frank Modica Jul 23 '18 at 15:49
  • 1
    If the external function returns a promise, you can `await` it. If it doesn't (say it uses a typical `callback(err, result)` pattern) then wrap it in a promise, ex: `await new Promise((resolve, reject) => { externalFunction(function(err, result) { ... resolve() ... }) });` – Charlie Schliesser Jul 23 '18 at 15:50

2 Answers2

1

Same way you would do for your own code.

(async function() {

    await doThingOne();
    await doThingTwo();

    const result = await new Promise((resolve, reject) => {
        doAsyncThingFromExternalLibrary(function(err, result) {
            if (err) return reject(err);
            resolve(result);
        });
    });

})();

function doThingOne() {
    return Promise.resolve();
}

function doThingTwo() {
    return Promise.resolve();
}

function doAsyncThingFromExternalLibrary(fn) {
    setTimeout(fn, 1000);
}
Charlie Schliesser
  • 7,851
  • 4
  • 46
  • 76
Baruch
  • 2,381
  • 1
  • 17
  • 29
  • 4
    Assuming `doAsyncThingFromExternalLibrary` returns a promise. – Charlie Schliesser Jul 23 '18 at 15:50
  • Yea, assuming it does. If not then there's no real way. – Baruch Jul 23 '18 at 15:52
  • @Baruch You could return a created promise if the external method was not returning one itself, ie the wrapper becomes the promise – Pogrindis Jul 23 '18 at 15:54
  • You could, but that doesn't guarantee that the function will wait for the `doAsyncThingFromExternalLibrary` to finish before executing. – Baruch Jul 23 '18 at 15:55
  • It would indeed (a)wait until the promise resolves. – Charlie Schliesser Jul 23 '18 at 15:58
  • @CharlieSchliesser The wrapping promise, yes, but not the third party library promise. Or am I wrong? – Baruch Jul 23 '18 at 16:13
  • So, regardless of what the third party function returns – a value, null, a Promise, whatever – if you wrap it in your *own* Promise that doesn't resolve until you decide it resolves, then the `await` will work as expected. Would you mind if I edited your answer to include what I'm suggesting? It would just be a few lines and will help illustrate the point. – Charlie Schliesser Jul 23 '18 at 18:15
  • @CharlieSchliesser Don't mind at all. – Baruch Jul 23 '18 at 18:52
  • Take a look. Regardless of what happens in `doAsyncThingFromExternalLibrary`, execution will not proceed past the `await` until `resolve()` runs. – Charlie Schliesser Jul 23 '18 at 19:09
1

Is there a way to "wrap" that call so the code stops and waits for it? Should I wrap it in my own Promise and make it thenable?

Yes.

await will await a promise, so if you have a function that you want to await then you need it to return a promise.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • Does this mean, by extension, that all my steps need to be in their own functions? A function being the only thing that can be "awaited"? – Deane Jul 26 '18 at 18:37
  • @Deane - no. You can only await promises, not functions. – Quentin Jul 26 '18 at 19:25