How can i return status inside the promise?

I started to learning promise with loopback and jsforce, and couldn't handle this problem; I couldn't return status var inside promise to cb() function. Basically i want to connect salesforce and get data via JSforce and write it to db via loopback. Then want to return created/updated/error records to client after remote mothed called.

I'm developing with Loopback via using Node.JS & Express.js I'm using JSforce library to connect salesforce

How can I fix that?

Here is my code:

module.exports = function(Contact) {
  var jsforce = require('jsforce');
  var async = require("async");
  var lr = require('lr.js');

  Contact.ImportContacts = function(cb) {
    // Salesforce Projects List
    var sf_projects = [];
    //Salesforce Conn String
    var conn = lr.SalesforceConn();
    conn.apex.get("/Contact/", function(err, res) {
      var status = {
        "Created": [],
        "Updated": [],
        "Error": ""
      };
      if (err) console.log(err);

      sf_projects = res;
      // Clear result
      status.Created.length = 0;
      status.Updated.length = 0;
      status.Error = "";

      if (sf_projects != undefined) {
        async.eachSeries(sf_projects, function(contact, callback) {
          Contact.findOrCreate({
              where: {
                co_SalesforceID: contact.Id
              }
            }, {
              co_Name: contact.FirstName,
              co_Surname: contact.LastName,
              co_Salutation: contact.Salutation,
              co_Title: contact.Title,
              co_Department: contact.Department,
              co_Email: contact.Email,
              co_PhonePersonal: contact.HomePhone,
              co_PhoneWork: contact.Phone,
              co_PhoneCell: contact.MobilePhone,
              co_Description: contact.Description,
              co_SalesforceID: contact.Id
            },
            function(err, cntct, created) {
              if (err) console.log(err);
              if (created) {
                status.Created.push(cntct.id);
                console.log("Contact created. SalesForeID: " +
                  cntct.co_SalesforceID +
                  " ContactName: " +
                  lr.isDefined(cntct.co_Salutation) + " " +
                  lr.isDefined(cntct.co_Name) + " " +
                  lr.isDefined(cntct.co_Surname));
              } else {
                Contact.replaceById(cntct.id, {
                    co_Name: contact.FirstName,
                    co_Surname: contact.LastName,
                    co_Salutation: contact.Salutation,
                    co_Title: contact.Title,
                    co_Department: contact.Department,
                    co_Email: contact.Email,
                    co_PhonePersonal: contact.HomePhone,
                    co_PhoneWork: contact.Phone,
                    co_PhoneCell: contact.MobilePhone,
                    co_Description: contact.Description,
                    co_SalesforceID: contact.Id
                  },
                  false,
                  function(err, obj) {
                    if (err) console.log(err);
                    status.Updated.push(obj.id);
                    console.log("Contact updated. SalesForeID: " +
                      obj.co_SalesforceID + " ContactName: " +
                      lr.isDefined(obj.co_Salutation) + " " +
                      lr.isDefined(obj.co_Name) + " " +
                      lr.isDefined(obj.co_Surname));
                  });
              }
            });
          callback(err);
        }, function(err) {
          if (err) console.error(err);
        });
      } else {
        console.log("Salesforce Connection Error!");
        status.Error = "Salesforce Connection Error";
      }
      return Promise.resolve(status);
    }).then(function(end) {
      cb(null, end);

    }).catch(function(err) {
      if (err) console.log(err);
    });
  };
  Contact.remoteMethod(
    'ImportContacts', {
      returns: {
        arg: 'result',
        type: 'string'
      },
      http: {
        path: '/importContacts',
        verb: 'get'
      }
    }
  );
};

It's not entirely clear what are asking about, and you don't include your solve() function which may be important here, so I can only give you some general tips.

You have something like this:

}).then(function(end) {
  cb(null, end);
}).catch(function(err) {
  if (err) console.log(err);
});

The first part (then) suggests that the cb() callback takes the error as the first argument and a value as the second argument, following the usual convention of Node callbacks.

But then in the second part (catch) you don't call the callback with the error. Also, the if (err) is redundant since in the catch handler there will always be an error, unless the solve() function returns a rejected promise with false or null specified as the rejection reason - and even then, whatever the rejection reason is, the callback should always be called in the case of error:

}).then(function(end) {
  cb(null, end);
}).catch(function(err) {
  console.log(err);
  cb(err);
});

That way you will not get a situation where the callback is never called and waits forever. When you mix promises with traditional callbacks you have to keep few things in mind:

Any function that gets a callback as an argument should make sure that this callback is called and that it is called exactly once. This is your responsibility as the function author to ensure that. In the case of error you should run:

callback(error);

and in the case of success you should call:

callback(null, data);

That way, the callback can know when the operation is finished and whether it finished with a success or failure by testing its first argument:

function (err, data) {
  if (err) {
    console.log('Error:', err);
  } else {
    console.log('Success:', data);
  }
}

The entire invocation of a function taking callback is usually:

functionTakingCallback('some', 'arguments', function (err, data) {
  if (err) {
    console.log('Error:', err);
  } else {
    console.log('Success:', data);
  }
});

On the other hand if the function returns a promise, you use it like this:

functionReturningPromise('some', 'arguments')
.then(function (data) {
  console.log('Success:', data);
})
.catch(function (err) {
  console.log('Error:', err);
});

No need for testing err in this case.

Callbacks should always be called exactly once. Promises should always be either resolved or rejected eventually. The usage is different and the responsibility of both the caller and the callee is different. When you mix those two styles - functions that take traditional Node-style callbacks and functions that return promises - then you have to be careful with those differences.

You can sometimes convert functions that take callbacks to functions that return promises using libraries like Bluebird and its promisify() and promisifyAll() to have consistent API for all your async functions in your entire code base. See:

  • http://bluebirdjs.com/docs/api/promise.promisify.html
  • http://bluebirdjs.com/docs/api/promise.promisifyall.html

You can see some other answers where I explain the difference between callbacks and promises and how to use them together in more detail, which may be helpful to you in this case:

  • A detailed explanation on how to use callbacks and promises
  • Explanation on how to use promises in complex request handlers
  • An explanation of what a promise really is, on the example of AJAX requests