71

I have a spec from a client for an implementation of a method in a module:

 // getGenres():
 //  Returns a promise. When it resolves, it returns an array.

If given an array of genres,

['comedy', 'drama', 'action']

Here is a skeleton method with a promise:

MovieLibrary.getGenres = function() {
  var promise = new Promise(function(resolve, reject) {
    /* missing implementation */
  });

  return promise;
};

Can the promise be made to return the data found in the genres? Is there a better way to achieve the spec description?

sealocal
  • 873
  • 11
    Promises don't "return" values, they pass them to a callback (which you supply with .then()). The spec simply sounds confused to me. It's probably trying to say that you're supposed to do resolve([genre1, genre2, ...]); inside the promise implementation. – Ixrec Apr 22 '15 at 23:07
  • A promise can also be rejected (instead of resolved) in case of an error/exception, and in such case the returned promise is 'caught' with the Promise.catch. In case of using async / await syntax one should use the try-catch block to enclose the awaited statement(s). Note there is also a Promise.finally. – prasad_ Nov 17 '21 at 11:25

3 Answers3

64

It sounds like you aren't understanding how promises are used. You return a promise. Then, later when your code resolves the promise, it resolves it with a result and that result is passed to the .then() handler attached to the promise:

MovieLibrary.getGenres = function() {
  var promise = new Promise(function(resolve, reject) {
    /* missing implementation */
    resolve(result);
  });

  return promise;
};

MovieLibrary.getGenres().then(function(result) {
    // you can access the result from the promise here
});
jfriend00
  • 3,572
  • Probably, you don't understand why this might be needed. In environment, like React Native with Redux with Realm, I have to put initial state to Redux createStore. It should be fetched from Realm, but it returns promise. I just want to block until it returns data as this should be very fast and simple. Instead I get problems how to combine Realm promises and usual JSON object for createStore() – likern Dec 26 '18 at 19:19
  • 3
    @likern Browser Javascript can't block... end of story. – user253751 Jul 17 '20 at 11:18
59

Updated answer for ES2017

The method will still return a promise, but you can use const value = await (the promise) to assign a value to a variable

await stops executing until the Promise has resolved (ie, has a value). Unlike using .then() you can just keep awaiting values as you run various functions that return promises, and execution continues onto the next line (this is called 'direct style). It's also much nicer to look at than .then() everywhere, since it's consistent with the rest of JavaScript.

// Example function that returns a Promise that will resolve after 2 seconds
var getGenres = function() {
  return new Promise(function(resolve) {
    setTimeout(function(){
      resolve(['comedy', 'drama', 'action'])
    }, 2000);
  });
}

// We start an 'async' function to use the 'await' keyword (async function(){ var result = await getGenres() console.log('Woo done!', result)

// But the best part is, we can just keep awaiting different stuff, without ugly .then()s var somethingElse = await getSomethingElse() var moreThings = await getMoreThings() })()

Await is supported in all current browsers and node

1

The way I like to do it is to create a let variable and assign the 'then' result to that variable. Const won't work because it will give you an error. It will be like let value_result = function_that_returns_promise.then(result => value_result = result)

For your example, I made the method into a function for simplicity:

const genres = ['comedy', 'drama', 'action'];

function getGenres() { var promise = new Promise( (resolve, reject) => { let result = genres[0] //just picking out the first one as example resolve(result) }) return promise }

const result_as_promise = getGenres() console.log(result_as_promise);

//output: //Promise { 'comedy' }

let result_as_value = getGenres().then(result => result_as_value = result)

setTimeout(function () { console.log(result_as_value); }, 0);

//output: //comedy

You have to use setTimeout if you are running through node (otherwise you'll get undefined error due to racing condition); if running it in browser console, then you prob don't need the setTimeout.

Jason
  • 111
  • I don't think you need to assign the promise to result_as_value in let result_as_value = getGenres().then(...). Just declaring the variable and the assignment in the then(result => result_as_value = result) suffice. The reason this works is because the callback to setTimeout(...) executes after all of the other user code has executed, which includes resolving the getGenres() promise. This is related to how the event loop works. See how Node.js does it, https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/ – Jose Quijada Nov 19 '21 at 14:06