Recent chats
ASKGEEZ Q&A GUIDETechnology & devices

How do I return a value from a promise chain so the next step can use it?

Quick answer

Return the value you want the next .then() handler to receive. Returning an ordinary value passes it along; returning a promise makes the chain wait for that promise’s outcome. If a handler returns nothing, the next handler receives undefined. [S1] For asynchronous work inside a block-bodied handler, write return before the promise when later steps depend on its result.

LET’S WORK THROUGH IT

The question, the details, the next step.

Question

I have several .then() calls, but the second one gets undefined instead of the result from the first step. How does a value actually get passed along?

Geez

Think of each .then() as producing a new promise. What its fulfillment handler returns determines the value that the next fulfillment handler receives: an ordinary returned value is passed along, while a returned promise makes the chain wait for that promise’s outcome. Returning nothing gives the next handler undefined. [S1]

For example:

Promise.resolve(4)
  .then((number) => {
    return number * 3;
  })
  .then((result) => {
    console.log(result); // 12
    return result + 1;
  })
  .then((nextResult) => {
    console.log(nextResult); // 13
  });

Value flow: the first handler receives 4, returns 12, and the second receives 12. It returns 13, which the third receives. Each handler must return the value that should continue down the chain.

Follow-up question

My handler starts another promise inside curly braces. Why doesn’t the next step wait for that promise?

Geez

With a block-bodied arrow function, the braces require an explicit return if you want the handler to pass something back. Without it, the handler returns nothing, so the next step sees undefined; starting asynchronous work inside the handler does not by itself make the chain wait for that work. [S1]

Missing return:

getRecord()
  .then((record) => {
    saveRecord(record);
  })
  .then((savedRecord) => {
    console.log(savedRecord); // undefined
  });

If saveRecord(record) returns a promise and the next step depends on its outcome, return it:

getRecord()
  .then((record) => {
    return saveRecord(record);
  })
  .then((savedRecord) => {
    console.log(savedRecord); // outcome returned by saveRecord
  });

Here the second handler waits for the returned promise to settle, then receives its fulfillment value. This assumes saveRecord returns a promise that fulfills with a useful value; if it fulfills without a value, the next handler receives undefined.

Follow-up question

Can you show the same pattern when I need to extract a value from one asynchronous result and use it in the next step?

Geez

Return the extracted value from the first handler. If getting that value itself involves a promise, return that promise too. For example, assuming loadSettings() returns a promise for an object with a theme property, and loadThemeDetails(theme) returns a promise:

loadSettings()
  .then((settings) => {
    return settings.theme;
  })
  .then((theme) => {
    console.log(theme); // the theme value
    return loadThemeDetails(theme);
  })
  .then((details) => {
    console.log(details); // fulfillment value from loadThemeDetails
  });

Value flow: loadSettings() fulfills with settings; the first handler returns settings.theme; the next receives that theme and returns a promise; the final handler receives that promise’s fulfillment value. Returning the promise is what connects the asynchronous result to the chain. [S1]

For a concise handler that returns one expression, you can omit braces: .then(settings => settings.theme). An expression-bodied arrow returns its expression. If you use braces, include return explicitly.

Follow-up question

What’s a quick way to check my own chain and catch this mistake?

Geez

Read the chain from top to bottom and label each handler with two things: what it receives and what it returns. Then check every block-bodied handler. If a later step needs a value or asynchronous outcome from that handler, make sure the handler returns it.

A reusable annotation might look like this:

startTask()                         // fulfills with: input
  .then((input) => {                // receives: input
    return transform(input);        // returns: transformed value
  })
  .then((transformed) => {          // receives: transformed value
    return finishAsync(transformed); // returns: a promise
  })
  .then((finished) => {              // receives: finishAsync's fulfillment value
    showResult(finished);
  });

The names here are placeholders; replace them with your functions and values. The comments describe the intended flow, not guaranteed return values. Check what your actual functions return. In particular, returning a promise only helps if that promise fulfills with the value you expect; the return and waiting behavior does not establish what any application-specific function returns. [S1]

A practical checklist

  • For each .then() handler, note the value it receives.

  • Identify the exact value the next handler needs.

  • Return that value from the current handler.

  • If the next step depends on asynchronous work, return the promise for that work.

  • Check block-bodied handlers for a missing return and verify the called function's actual fulfillment value.

A few more useful answers

Why does an expression-bodied handler work without the word `return`?

An arrow function written as .then(value => transform(value)) implicitly returns its expression. If you add braces, write return explicitly: .then(value => { return transform(value); }).

What if I want the next step to receive the original value as well as a new result?

Return a combined value, such as an object: .then(original => calculate(original).then(result => ({ original, result }))). The next handler receives that object after the returned promise fulfills.

Does returning a promise guarantee the next step gets the value I expect?

No. The next handler receives the returned promise’s fulfillment value, but whether that is the value you expect depends on what the operation returns. [S1]

Sources

References used for this guide.

  1. [S1] Promise.prototype.then() — JavaScript | MDN
    developer.mozilla.org · Checked

YOUR QUESTION. YOUR DETAILS.

Let’s work through your situation.

Get a tailored answer or help with your next task. Starts at $1, with a quote before you pay.

Ask your own question
← Back to Geez at Work