Working with office.js using promise pattern - javascript

Currently I've started developing some Apps for Office 2013. To develop these apps I'm using office.js which is designed to work with Excel worksheets for example.
Most of the APIs are like:
document.getSelectedDataAsync(p1, p2, function(asyncResult)
{
if (asyncResult.status == 'success')
// do something with asyncResult.value
else if (asyncResult.status == 'fail')
// show asyncResult.error as Error
});
I don't like this type of asynchronous programming. Rather I prefer to use promises and write something like:
document.getSelectedDataAsync(p1, p2)
.done(function(result)
{
// do something with result
})
.fail(function(error)
{
// show error message
})
Is there any way to use office.js API using promises like above?

Sure thing - this example is using the bluebird promise library. Basically, we're converting a callback API to promises:
function promisify(fn){ // take a function and return a promise version
return function(){
var args = [].slice.call(arguments);
return new Promise(function(resolve, reject){
args.push(function(asyncResult){
if(asyncResult.status === 'success') resolve(asyncResult.value);
else reject(asyncResult.error);
});
fn.apply(this, args); // call function
}.bind(this)); // fixate `this`
};
}
This would let you do something like:
document.getSelectedDataPromise = promisify(document.getSelectedDataAsync);
document.getSelectedDataPromise(p1, p2).then(function(result){
// do something with result
}).catch(function(err){
// handle error
});

The simplest form of working around this is by replacing the callback with a custom one which resolves a promise. Note that the below implementation uses the ES6 promises which are available in Chrome:
function toPromise () {
var args = Array.prototype.slice.call(arguments);
var self = this;
return new Promise(function (reject, resolve) {
var callback = function () {
if (arguments[0] instanceof Error) {
return reject.apply(null, arguments);
}
resolve.apply(arguments);
};
args.push(callback);
self.apply(self, args);
});
}
Function.prototype.toPromise = toPromise;
document.getSelectedDataAsync.toPromise(p1, p2).then(function () {
//success
}).catch(function () {
//error
});

Related

Trying to use named functions in promise of Mongoose [duplicate]

As a node programmer. I'm used to use "nodebacks" for handling errors in my code:
myFn(param, function(err, data) {
if (err){
//error handling logic
}
else {
// business logic
}
});
When writing that function, I can do something like:
var myFn = function(param, callback){
var calc = doSomeCalculation(param);
if(calc === null) { // or some other way to detect error
callback(new Error("error with calculation"), null);
}
...
someAsyncOp(calcN,function(err, finalResult){
if(err) return callback(err, null);
callback(null, finalResult); // the error is null to signal no error
});
};
How would I do this sort of error handling with promises?
Rule of Thumb
Whenever you have a doubt about how to do something with promises - think about the synchronous version.
try{
var result = myFn(param);
// business logic with result
} catch(e) {
//error handling logic
}
This, at least to me looks a lot cleaner than a callback with a first parameter that is sometimes null.
The promises way is almost always very similar to the synchronous version of the problem:
myFn(param).then(function(result){
// business logic with result
}).catch(function(e){
//error handling logic
});
Where myFn would look something like when working with callbacks:
var myFn = function(param){
return new Promise(function(resolve, reject){
var calc = doSomeCalculation(param);
if(calc === null) { // or some other way to detect error
reject(new Error("error with calculation"), null);
}
someAsyncOp(calcN,function(err, finalResult){
if(err) reject(err);
resolve(finalResult);
})
});
};
Working with callbacks/nodebacks
This is only something you should have to do when working with callbacks, when working with promises it is a lot simpler, and you can do:
var myFn = function(param){
var calc = doSomeCalculation(param);
...
return someAsyncOp(calcN); // returning a promise.
}
Moreover, when working inside promise chains, you get throw safety:
myFn(param).then(function(calcN){
// here, you throw to raise an error and return to resolve
// new Promise should be used only when starting a chain.
}).catch(function(err){
// handle error
}).then(function(){
// ready to go again, we're out of the catch
});
Note, some libraries like Bluebird , RSVP and Q offer syntactic sugar and automatic promisification of methods so you rarely have to use new Promise yourself.
Also, consider reading this and that to learn more about promise error handling.
If you're using the async/await syntax, you can just use the regular try-catch syntax for error handling.
// your promise function
const myFn = function(param){
return new Promise(function(resolve, reject){
if (someLogic()) {
resolve(someValue);
} else {
reject('failure reason');
}
});
}
// Define the parent function as an async function
async function outerFn(param) {
try {
// Wait for the promise to complete using await
const result = await myFn(param)
// business logic with result
} catch (e) {
//error handling logic
}
}

Returning a value from a function depending on whether a Promise was resolved or not

const dbConnection = require("../dbConnection");
var task = function () {
var response = "";
dbConnection.then(function () {
//do something here
response = "some value";
})
.catch(function () {
response = new Error("could not connect to DB");
});
//I can't return response here because the promise is not yet resolved/rejected.
}
I'm using a node module that someone else wrote. It returns a promise. I want to return either a string or a new Error() depending on whether the Promise object returned by the module resolved or not. How can I do this?
I can't return inside the finally() callback either because that return would apply to the callback function not my task function.
dbConnection.then().catch() will itself return a promise. With that in mind, we can simply write the code as return dbConnection.then() and have the code that uses the function treat the return value as a promise. For example,
var task = function () {
return dbConnection.then(function() {
return "Good thing!"
}).catch(function() {
return new Error("Bad thing.")
})
}
task().then(function(result){
// Operate on the result
}
const dbConnection = require("../dbConnection");
var task = function () {
var response = "";
return dbConnection.then(function () {
//do something here
response = "some value";
return response;
})
.catch(function () {
response = new Error("could not connect to DB");
return response;
});
}
This will return a promise which you can then chain.
The point of using promises is similar to using callbacks. You don't want the CPU to sit there waiting for a response.

Javascript BlueBird Promises: architecture for concurrent API requests

I am seeking for an architecture advice. Using Bluebird Promises in a MEAN environment (talking node.js server-side here), I intend to make many concurrent API calls, aggregate all results and respond to client. Example (pseudo-)code:
exports.getAllData = function(searchquery, cb) {
var results;
wrapper1.getResultsFromAPI1(searchquery, function(err,data){
results += data;
});
wrapper2.getResultsFromAPI2(searchquery, function(err,data){
results += data;
});
wrapper3.getResultsFromDataBase(searchquery, function(err,data){
results += data;
});
if (AllRequests done){
cb(null,results);
}
}
Now I don't know how I can make sure to:
Fire all requests concurrently (NOT sequentially to reduce
response time)
Respond to client once I got responses from ALL API requests
In case of one API request to fail for whatever reason, not having the entire promise chain to be rejected, thus "loosing" the other API response data.
I checked on Bluebird Promise website for appropriate collections, but none seems to fully meet the requirements listed above. Any suggestions?
One way of doing this would be using reflect calls.
var Promise= require('bluebird');
Promise.props({
"wrapper1": someasync(1).reflect(),
"wrapper2": someasync(0).reflect(),
"wrapper3": someasync(1).reflect()
})
.then(function(results) {
Object.keys(results).forEach(function(key) {
if (results[key].isRejected()) {
console.log(key + " failed.", results[key].reason());
} else {
console.log(key + " successed", results[key].value());
}
});
});
function someasync(t) {
if (t===0) return Promise.reject('some err');
else return Promise.resolve(true);
}
Which results in the following:
wrapper1 successed true
wrapper2 failed. some err
wrapper3 successed true
var Promise= require('bluebird');
var chain = require('lodash').chain;
function rejectedPromise(settledPromise) {
return settledPromise.isRejected();
}
function extractResponse(fulfilledPromise) {
return fulfilledPromise.value();
}
Promise.settle([
asyncCall(),
asyncCall(),
asyncCall()
])
.then(function retrieveSuccessfulResponses(settledPromises) {
return chain(settledPromises)
.reject(rejectedPromise)
.map(extractResponse)
});
If you want, you can manually promisify your API methods, e.g:
var p1 = new Promise(function(resolve, reject) {
wrapper1.getResultsFromAPI1(searchquery, function(err, data) {
if (err) reject(err);
else resove(data);
});
});
But since you're using the BlueBird library, then you can use Promise.promisify, so you can avoid the boilerplate code of wrapping the methods into promises. E.g:
var getResultsFromAPI = Promise.promisify(wrapper1.getResultsFromAPI1);
var p1 = getResultsFromAPI(searchquery);
And if you want to promisify all the methods of an API, you can use Promise.promisifyAll. E.g:
var wrapper1 = Promise.promisifyAll(require('my-api'));
// then, all the wrapper1 methods are already wrapped into a Promise
var p1 = wrapper1.getResultsFromAPI1(searchquery);
So, after turning all your methods into Promises, you can use the Promise.settle to achieve what you want: See what promises were fulfilled and what of them were rejected:
exports.getAllData = function(searchquery, cb) {
/* Don't forget to promisify all the API methods firstly */
var results;
var p1 = wrapper1.getResultsFromAPI1(searchquery);
var p2 = wrapper2.getResultsFromAPI2(searchquery);
var p3 = wrapper3.getResultsFromDataBase(searchquery);
Promise.settle([p1, p2, p3]).then(function(arr) {
arr.forEach(function(res, index) {
if (res.isFulfilled()) { // check if the Promise was fulfilled
results += res.value(); // the Promise's return value
}
else if (res.isRejected()) { // check if the Promise was rejected
console.log(res.reason()); // do something with the error
}
});
cb(null, results);
});
}

JavaScript Exception Is Lost In Promise Chain

I am new to JavaScript promises and I am trying to implement them into some PhoneGap code on an Android device. I want to log exceptions and it looks like exceptions are swallowed somewhere. See the sample code below. The exception thrown due to the call to the non-existent function "thiswillfail" does not show anywhere. I commented out the irrelevant code and added code to force the AddRecord promise to be called. The code checks if a record exists and, if not, return the AddRecord promise which is the error is. I am not using any 3rd party libraries. What am I doing wrong?
Edit: If I add another promise in the chain "DoSomethingWithRecord", this promise is called when the expectation is to skip to the catch.
function TestPromiseExceptionHandling() {
var record = null;
var CheckForRecord = function () {
return new Promise(function (resolve, reject) {
//getData(
// function (data) {
var data = "";
if (data != "") {
//record = new Record(data);
record = "existed";
resolve();
}
else return AddRecord();
// },
// function (err) {
// reject(new Error("An error occurred retrieving data, msg=" + err.message));
// });
});
};
var AddRecord = function () {
return new Promise(function (resolve, reject) {
thiswillfail();
//add record
var success = true;
record = "new";
if (success) resolve();
else reject(new Error("add record failed"));
});
};
var DoSomthingWithRecord = function () {
return new Promise(function (resolve, reject) {
alert(record);
resolve();
});
};
try {
CheckForRecord()
.then(DoSomthingWithRecord())
.catch(function (err) { alert(err.message);})
.then(function () { alert("done"); });
} catch (err) {
alert(err.message);
}}
You can't return from the promise constructor, when you do:
else return AddRecord();
Nothing will wait for AddRecord, instead, you want to resolve with AddRecord which will wait for it before resolving the promise:
else resolve(AddRecord());
However, if this is your code you can just return AddRecord() instead of using the promise constructor anyway. The promise constructor (new Promise) is mostly useful for converting non-promise APIs to promises and aren't supposed to be used with already promisified APIs. Use thens instead.

Creating promises

I am having trouble with creating / understanding promises. I understand the advantages and understand how to use them. Creating own promise-functionality is the difficult part. Simply, how do I convert this function to work with promises:
ret.getDataByGame = function (gameID, playerID) {
var cb = new callbackHelper();
models.gameData.find( { }, function (err, found) {
if (err) {
console.log("error in getting gamedata for gameID: "+gameID);
cb.setData(void 0);
} else {
cb.setData(found);
}
});
return cb;
};
function callbackHelper() {
var self = this;
this.data = false;
this.setData = function (data) {
self.data = data;
};
It should not matter what framework or vanilla js you use to show the example to me.
ret.getGameDataByGame = lib.promisify(models.gameData.find);
might suffice. Or use a dedicated node-style callback helper function:
ret.getGameDataByGame = function(gameID, playerID) {
return lib.ninvoke(models.gameData, "find", {…});
};
For the Q library, check the Adapting Node section of its docs.
For creating a promise with the pattern you've used for your callbackHelper thing, your promise library typically offers Deferreds. You would use them like this:
ret.getDataByGame = function (gameID, playerID) {
var def = new lib.Deferred();
models.gameData.find({…}, function (err, found) {
if (err) {
def.reject("error in getting gamedata for gameID: "+gameID);
} else {
def.fulfill(found);
}
});
return def.promise;
};
See also the The Beginning section in the Q docs.
Just to give a second input, I quickly looked at the promise implementation of Q docs, but this is the implementation that I use, which is supported by default, in browsers (except IE). With respect to your posted algorithm:
//define promise structure for callback function of interest
ret.getDataByGame = function(gameID, playerID){
return new Promise(function(resolve,reject)
{
try
{
//do any callback function etc. which you want to do
models.gameData.find({},function(err, found){
if(err)
{
console.log("error in getting gamedata for gameID: "+gameID);
reject(err); //if there is error, save as reject
}
else
resolve(found); //if have solution, save as resolve
}
}
catch(exc)
{reject('Error exc gameData.find: '+exc.message);}
}); //end of Promise
}
And then where you call your class functions etc.:
//where you physically call the function you defined as a promise function
ret.getDataByGame('input1','input2').then(function(output){
alert("woohoo, you are awesome!, output = "+output);
},function(error){
alert("Output error:\r\n"+error);
});
Here is the definition and implementation of promises which I consider as the "standard" thus far, with browser support versions: Promise doc + tutorial. An the cool thing if you do it for massive amounts of data, and they are async, you really optimize your execution time!! such as:
//repeat promise function
function repeatPromise(inputDataArray)
{
for(var i = 0; i < inputDataArray.length; i++)
{
//where you physically call the function you defined as a promise function
ret.getDataByGame(inputDataArray[i].input1,inputDataArray[i].input2).then(function(resolve){
alert("Output is in async, output = "+resolve);
},function(error){
alert("Output error:\r\n"+error);
});
} //end of for loop
} //end of function
Hope this helps :)

Categories

Resources