Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using resolved promise data synchronously

I'm learning about promises and I absolutely want to make sure I am understanding their use before I continue. I am using a library for an online service which has functions which return a promise.

Nearly all examples I read either use the resolved data in chained then() functions

const result = Library.functionReturningAPromise()
result.then(function(res) {
    const obj = new Example(res)
    return obj
}).then(function(ob) {
    // do the rest of the logic within these then() functions
})

or use the resolved data within an async function

async function test() {
    const result = await Library.functionReturningAPromise()
    const obj = new Example(result)

    // do the rest of the logic
}

I want to know if there is any way at all to use the data from a resolved promise in 'normal' synchronous code

 const result = Library.functionReturningAPromise()

 // do something to resolve the promise

 const obj = new Example(result)

or if you need to always 'wrap' all your logic that uses the data from a resolved promise in an async function.

like image 645
myol Avatar asked Jul 25 '18 12:07

myol


2 Answers

I want to know if there is any way at all to use the data from a resolved promise in 'normal' synchronous code

There isn't a way to write completely synchronous code when handling asynchronous responses. Once any operation is asynchronous, you have to deal with the response using asynchronous techniques and cannot program with it synchronously. You must learn to program asynchronously.

The two options you show (.then() or async/await) are your two choices for handling the returned promise.

or if you need to always 'wrap' all your logic that uses the data from a resolved promise in an async function.

If you want to use await so that you can write synchronous-looking code for dealing with promises, then all that code has to be inside an async function. And, as soon as you leave the scope of that function (like want to return a value), you're returning a promise from the async function and again have to deal with the promise.

There is no way around this. It's just something one must learn in Javascript. It becomes a second nature after awhile.


As you appear to know, you can use async and await to get some synchronous-looking logic flow, but there are some things to make sure you understand when doing this. From your example:

async function test() {
    const result = await Library.functionReturningAPromise()
    const obj = new Example(result);

    // do the rest of the logic
}
  1. All functions declared with async return a promise. That's the only kind of return value you get from them. If the caller is looking for a return value or wants to know when the asynchronous operations are done or is looking for errors, they MUST use that returned promise with .then(), .catch() or again with await inside an async function.
  2. If a promise that you are awaiting rejects, it will essentially throw and abort the rest of the execution of the function and then return reject the returned promise.
  3. If you want the execution flow to abort on a rejected promise and the caller is going to handle the rejection, then that's all fine.
  4. But, if the caller isn't handling the rejection, the somebody needs to. If you're using await and you need to handle rejections locally, then you need to wrap them in try/catch (very similar syntax to synchronous exceptions).

Here's an example of using try/catch with await to handle errors locally:

async function test() {
    try {
        const result = await Library.functionReturningAPromise();
        const obj = new Example(result);

        // do the rest of the logic
    } catch(e) {
        // handle promise rejection here
    }
}

Here's an example of handling errors by the caller:

async function test() {
    const result = await Library.functionReturningAPromise();
    const obj = new Example(result);

    // do the rest of the logic
}

test().then(() => {
    console.log("all done");
}).catch(err => {
    console.log(err);
});
like image 162
jfriend00 Avatar answered Oct 03 '22 03:10

jfriend00


It's possible to use a combination of async/await and then() to program synchronously if you wrap their use in a function that returns a default value.

Here's an example that makes use of memoization to return a result or query it if needed:

const scope = {
  // someResult is wrapped in an object so that it can be set even if it's a primitive
  // (e.g. number / string).
  someResult: null,
};

function getResult() {
  if (scope.someResult !== null) {
    // If we already have a result, return it. No need to query Library again.
    return scope.someResult;
  }

  // Note: This example doesn't implement debouncing (avoiding repeated Library queries while
  // Library is still being queried).
  queryLibrary().then(result => {
    if (scope.someResult === null) {
      // If the Library call succeeded and we haven't already stored its result, store it.
      scope.someResult = result;
      console.log('We have a result now!', getResult());
    }
  });

  return scope.someResult;
}

async function queryLibrary() {
  const result = await functionReturningAPromise();
  // Do some stuff with result in a nice async / await way.
  result.push(9000, 1336);
  return result;
}

/** This is some complicated async function from Library. */
async function functionReturningAPromise() {
  return [419, 70];
}

// Calling getResult() will return either null or a list of numbers.
console.log('Do we have a result yet?', getResult());
like image 25
Acorn1010 Avatar answered Oct 03 '22 04:10

Acorn1010