30

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.

5
  • 6
    "I want to know if there is any way at all to use the data from a resolved promise in 'normal' synchronous code " Nope. Commented Jul 25, 2018 at 12:52
  • 1
    Not possible. Doesn't matter whether you access the result through a promise or not, when it becomes available asynchronously then that means it is not - and cannot be - available now (synchronously). Commented Jul 25, 2018 at 12:54
  • Writing Library.functionReturningAPromise().then(...etc will solve your problem. You can add as many "then" (or any of the other valid) callbacks to the promise as you like. Commented Jul 25, 2018 at 12:57
  • 2
    As far as I understand the question, it's not about getting data from an asynchronous function, but processing the data after the asynchronous bit. The asynchronous way of .then-chaining and general coding style can get confusing if you're not used to it. And even if you are, once in a while everyone might get lost in one .then too many :) Commented Jul 25, 2018 at 13:09
  • No, but you don't technically have to wait for the promise to resolve immediately, you can call then or catch (or async try/catch) on the promise whenever you want, even multiple times and manipulate it in different ways. Commented Jul 25, 2018 at 17:14

3 Answers 3

20

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);
});
Sign up to request clarification or add additional context in comments.

17 Comments

Is there really no way to execute synchronous / blocking code without utilizing callbacks?
@NathanL - I'm not sure what you're asking because you may have the terminology confused. In an environment like node.js, there are asynchronous APIs and synchronous APIs. Some APIs like the file system API let you choose which version you want to use blocking/synchronous or non-blocking/asynchronous. If you choose the asynchronous API, there is NO way to use it without some sort of callback or promise. You can use await with promises to write more synchronous looking code, though it is still asynchronous. With other APIs like networking, there are only non-blocking/asynchronous APIs.
@NathanL - So you cannot write purely synchronous code with asynchronous APIs in Javascript. Can't do it. It is a different way of programming. If you want blocking/synchronous networking APIs, then you need to pick a different programming environment.
Sorry, to clarify: you can't run an otherwise asynchronous method in a blocking manner, such that the script doesn't continue to execute until the method is complete?
@smaudet - We will just have to disagree here. The opportunity for race conditions with async/await is far, far, far less than with actual threads because control NEVER flips to another thread of execution except when you explicitly use await. If you're hoping the OS saves you from two deadlocked threads, good luck. That's just buggy code that's crazy hard to test to find those threading race conditions. The fact that you are referring to an "async/await module" makes it sounds like don't even know what it is. It's not a module. They are first class features of the language.
|
1

If you want to "leave" the asynchronous context you could just wait for the Promise to fulfill, and then() call one of your "regular" functions as a callback.

I can understand that the asynchronous coding style can be very confusing, but effectively this is just what then(function () {}) does.

If this coding style is easier for you to understand or work with, it is functionally equivalent.

function first() {
    console.log("sync!");
    doAsync();
}

function doAsync() {
    console.log("now we're async!");
    Library.functionReturningAPromise().then(second);
}

function second() {
    console.log("sync again!");
    // ...
}

3 Comments

"Just waiting for the Promise to fulfill" does not leave the asynchronous context.
@Bergi please note the quotes around leave. Also please take my comment on the question into account. Hope that makes it clear :) I don't mean to say that you can actually ever leave an asynchronous context, there is simply no going back. But you can "escape" the async and it's associated style (then, await, async) with this approach.
I would argue that this coding style is not functionally equivalent, one of the main purposes of using Promises to manage asynchronous code is the error handling pipeline. The proposed approach turns Promises into glorified callbacks. While it may be easier to understand when you're used to writing synchronous code it is worth taking the time to understand how Promises work so you can leverage their fundamental features.
1

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());

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.