javascriptpromisecloudkit-js

Chaining Promises and Passing Parameters between Them


I'm new to Node/Express and am trying to use Promises to executive successive API calls to Apple's CloudKit JS API.

I'm unclear on how to put the functions in sequence and pass their respective return values from one function to the next.

Here's what I have so far:

var CloudKit = require('./setup')

//----
var fetchUserRecord = function(emailConfirmationCode){
  var query = { ... }

  // Execute the query
  CloudKit.publicDB.performQuery(query).then(function (response) {
    if(response.hasErrors) {
      return Promise.reject(response.errors[0])
    }else if(response.records.length == 0){
      return Promise.reject('Email activation code not found.')
    }else{
      return Promise.resolve(response.records[0])
    }
  })
}

//-----
var saveRecord = function(record){
  // Update the record (recordChangeTag required to update)
  var updatedRecord = { ... }

  CloudKit.publicDB.saveRecords(updatedRecord).then(function(response) {
    if(response.hasErrors) {
      Promise.reject(response.errors[0])
    }else{
      Promise.resolve()
    }
  })
}

//----- Start the Promise Chain Here -----
exports.startActivation = function(emailConfirmationCode){

  CloudKit.container.setUpAuth() //<-- This returns a promise
  .then(fetchUserRecord) //<-- This is the 1st function above
  .then(saveRecord(record)) //<-- This is the 2nd function above
    Promise.resolve('Success!')
  .catch(function(error){
    Promise.reject(error)
  })

}

I get an error near the end: .then(saveRecord(record)) and it says record isn't defined. I thought it would somehow get returned from the prior promise.

It seems like this should be simpler than I'm making it, but I'm rather confused. How do I get multiple Promises to chain together like this when each has different resolve/reject outcomes?


Solution

  • There are few issues in the code.

    First: you have to pass function to .then() but you actually passes result of function invocation:

    .then(saveRecord(record))
    

    Besides saveRecord(record) technically may return a function so it's possible to have such a statement valid it does not seem your case. So you need just

    .then(saveRecord)
    

    Another issue is returning nothing from inside saveRecord and fetchUserRecord function as well.

    And finally you don't need to return wrappers Promise.resolve from inside .then: you may return just transformed data and it will be passed forward through chaining.

    var CloudKit = require('./setup')
    
    //----
    var fetchUserRecord = function(emailConfirmationCode){
      var query = { ... }
    
      // Execute the query
      return CloudKit.publicDB.performQuery(query).then(function (response) {
        if(response.hasErrors) {
          return Promise.reject(response.errors[0]);
        }else if(response.records.length == 0){
          return Promise.reject('Email activation code not found.');
        }else{
          return response.records[0];
        }
      })
    }
    
    //-----
    var saveRecord = function(record){
      // Update the record (recordChangeTag required to update)
      var updatedRecord = { ... }
    
      return CloudKit.publicDB.saveRecords(updatedRecord).then(function(response) {
        if(response.hasErrors) {
          return Promise.reject(response.errors[0]);
        }else{
          return Promise.resolve();
        }
      })
    }
    
    //----- Start the Promise Chain Here -----
    exports.startActivation = function(emailConfirmationCode){
    
      return CloudKit.container.setUpAuth() //<-- This returns a promise
        .then(fetchUserRecord) //<-- This is the 1st function above
        .then(saveRecord) //<-- This is the 2nd function above
        .catch(function(error){});
    }
    

    Don't forget returning transformed data or new promise. Otherwise undefined will be returned to next chained functions.