0

Trying to use Promises instead of caolan's async library, I'm struggling with completely different approach. A little clarification is highly appreciated.

Let's say I'm registering User with Sequezlie library. To do this I need:

  1. Create user oject
  2. Create dependent object Folder
  3. Return result to client

Sequezlie use promises for all CRUD-operations. And User.register is a promisifed shortcut to User.create(...).

user =
  email: req.body.email
  name : req.body.name

createFolder = (user)->
  new Promise (resolve, reject)->
    Folder
      .build(title: "untitled", UserId: user.id)
      .save()
      .then (folder)-> resolve folder
      .catch (err)-> reject err

# register
User
  .register(user, req.body.password)
  .then(createFolder(user))
  .then (folder)->
    console.log "User with Folder is created"
    res.send 201

At this time Folder is created, but createFolder() has no access to user object. What I'm doing wrong? Which is the better way to chain Promises and access it's results in each other?

FYI, I'm using then/promise implementation.

Thanks.

f1nn
  • 6,989
  • 24
  • 69
  • 92
  • Does Folder return a promise? Because it looks like it. – slebetman May 02 '15 at 13:37
  • @ slebetman ...yes, and If I've got your idea, function should look like this? `createFolder = (user)-> Folder.build(title: "untitled", UserId: user.id).save()` – f1nn May 02 '15 at 13:40
  • Is this coffeescript? I don't know coffee but can give you a plain js answer – slebetman May 02 '15 at 13:42
  • @slebetman you are right, plain js version would be ```var createFolder = function(user) { return Folder.build({ title: "untitled", UserId: user.id }).save(); }; ``` – f1nn May 02 '15 at 13:45
  • a) Don't use the [`Promise` constructor antipattern](http://stackoverflow.com/q/23803743/1048572) (in `createFolder`) b) I think you'll want to pass a callback to `then`?! – Bergi May 02 '15 at 18:31

2 Answers2

3

You've defined a function createUser that accepts a parameter user.

I suspect (though it's hard to tell from your code) that this is not meant to be the same object user declared in the outer scope. You pass that to register, and that returns a different object (wrapped in a promise) representing the user. Let's call that object registeredUser to avoid confusion:

User
  .register(user, req.body.password)
  .then((registeredUser) -> createFolder(registeredUser))
  .then (folder)-> ...

We can write that more succinctly:

User
  .register(user, req.body.password)
  .then(createFolder)
  .then (folder)-> ...

The difference is that we are not calling createFolder - we're just passing it to then so it acts directly as the handler function for the successful resolution of the promise returned by register, i.e. it will be called by then when the user is registered.

Update

You asked in the comments about accessing the results of multiple previous promises, further back in the chain. Just start another chain inside the previous handler:

User.register(user, req.body.password).then((registeredUser) ->
  createFolder(registeredUser).then (folder) ->
    # do more stuff here, or just return an object
    {
      folder: folder
      user: registeredUser
    }
).then (all) ->
  console.log all

By returning an object from the inner then, that becomes the result passed back to the outer chain, so all is the object with folder and user properties.

Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
  • Thank you, I've figured it out. Wha't about chaining more the 2 functions? For example if in 3th chain (promise) I need to get the resolve (result) of 1st? – f1nn May 02 '15 at 14:02
  • 1
    Notice there are [many ways to access results from previous promises](http://stackoverflow.com/q/28250680/1048572). The [nesting](http://stackoverflow.com/a/28250687/1048572) presented here is just one of them. – Bergi May 02 '15 at 18:34
1

Sequelize queries seems to return promises. In which case you don't need to create your own promise objects - just use the ones returned by sequelize:

function createFolder (user) {
    // assuming the following returns a promise
    return Folder.build(title: "untitled", UserId: user.id).save();
}

Then you can chain it like this:

User.register(user, req.body.password)
    .then(createFolder) // <------------ note!!
    .then function (folder) {
        console.log("User with Folder is created");
        res.send(201);
    }

note!!: We pass createFolder instead of calling it! That's because the .then() will call it when .register() completes.

slebetman
  • 109,858
  • 19
  • 140
  • 171
  • I think the problem is that `createFolder` expects a `user` object that has an `id` property. In the OP's code (and your version) it is being passed the wrong object. – Daniel Earwicker May 02 '15 at 13:53
  • Ah, yes. Assuming `User.register` returns the object `createFolder` needs, it should just be passed as-is rather than called. Good catch. – slebetman May 02 '15 at 13:55