Bluebird Promise Scope - javascript

I have just started using promises in attempt to cleanup some 'callback hell'.
I've decided on trying bluebird and I am running it in the browser but immediately ran into scoping problems.
Is there a way of setting the thisArg in a new Promise? The below example shows that the 'this' value inside the promise resolver is set to the browser window, but I'd like it set to the surrounding scope so I can easily access member variables.
I noticed there is a .bind() method but it only scopes the 'then()' method, not the promise. I also realize I can have 'var me = this' just before the promise and use closure, but I wanted to avoid it if possible.
function MyObject() {
this.value = 7;
}
MyObject.prototype.getValue = function () {
return new Promise(function (resolve) {
// some request/processing that takes a long time
var result = Ajax.request(...);
resolve({
value: this.value,
result: result
});
// 'this' is set to window instead of the instance,
// resulting in this.value as undefined
});
}
var obj = new MyObject();
obj.getValue().then(function (value) {
console.log(value); // preferably outputs 7
})

No, there is not. You can of course use the default approaches, but you shouldn't need to.
When doing heavy processing and getting back the value asynchronously, you want to get a promise for the value. You don't need to set the result value as a property of the original instance.
MyObject.prototype.getValue = function () {
return new Promise(function(resolve) {
// lots of processing to make a `value`
resolve(value); // no `this` at all!
});
};
In case you want to synchronously get the .value that you had set on the instance, you don't need the Promise constructor. Just use Promise.resolve to make a promise for an existing value:
MyObject.prototype.getValue = function () {
// TODO: lots of processing
return Promise.resolve(this.value);
};
Or, in your case, even Promise.method:
// TODO: lots of processing
MyObject.prototype.getValue = Promise.method(function () {
return this.value;
});

This is more a comment then an answer, as it is primary opinion based.
In the rar situations where I need this it would look like this in my code:
Ajax.requestAsync in this case would be a promisifyed version of Ajax.request.
I know this might just move your problem to another place.
MyObject.prototype.getValue = function () {
return Ajax.requestAsync(...)
.bind(this)
.then(function(result) {
return {
value: this.value,
result: result
}
});
}
Or something like this:
MyObject.prototype.getValue = function () {
var returnValue = {
value: this.value
};
return Ajax.requestAsync(...)
.then(function(result) {
returnValue.result = result;
return returnValue;
});
}
A rarely use such constructs:
MyObject.prototype.getValue = function () {
return Promise.all([this, Ajax.requestAsync(...)])
.spread(function(object, result) {
return {
value: object.value,
result: result
};
});
}

Related

Pass additional parameter to promise chain [duplicate]

A promise, just for example:
var P = new Promise(function (resolve, reject) {
var a = 5;
if (a) {
setTimeout(function(){
resolve(a);
}, 3000);
} else {
reject(a);
}
});
After we call the .then() method on the promise:
P.then(doWork('text'));
Then doWork function looks like this:
function doWork(data) {
return function(text) {
// sample function to console log
consoleToLog(data);
consoleToLog(b);
}
}
How can I avoid returning an inner function in doWork, to get access to data from the promise and text parameters? Are there any tricks to avoiding the inner function?
Perhaps the most straightforward answer is:
P.then(function(data) { return doWork('text', data); });
Or, since this is tagged ecmascript-6, using arrow functions:
P.then(data => doWork('text', data));
I find this most readable, and not too much to write.
You can use Function.prototype.bind to create a new function with a value passed to its first argument, like this
P.then(doWork.bind(null, 'text'))
and you can change doWork to,
function doWork(text, data) {
consoleToLog(data);
}
Now, text will be actually 'text' in doWork and data will be the value resolved by the Promise.
Note: Please make sure that you attach a rejection handler to your promise chain.
Working program: Live copy on Babel's REPL
function doWork(text, data) {
console.log(text + data + text);
}
new Promise(function (resolve, reject) {
var a = 5;
if (a) {
setTimeout(function () {
resolve(a);
}, 3000);
} else {
reject(a);
}
})
.then(doWork.bind(null, 'text'))
.catch(console.error);
Use currying.
var P = new Promise(function (resolve, reject) {
var a = 5;
if (a) {
setTimeout(function(){
resolve(a);
}, 3000);
} else {
reject(a);
}
});
var curriedDoWork = function(text) {
return function(data) {
console.log(data + text);
}
};
P.then(curriedDoWork('text'))
.catch(
//some error handling
);
The new answer to this question is to use arrow functions (which automatically bind the this and are much more readable). Google for links such as:
https://2ality.com/2016/02/arrow-functions-vs-bind.html
You can set the text like:
this.text = 'text';
P.then(data => doWork(data));
Note: this.text inside doWork will evaluate to 'text'.
This is suggested by jib above and that (or this!) should be the accepted answer now.
Lodash offers a nice alternative for this exact thing.
P.then(_.bind(doWork, 'myArgString', _));
//Say the promise was fulfilled with the string 'promiseResults'
function doWork(text, data) {
console.log(text + " foo " + data);
//myArgString foo promiseResults
}
Or, if you'd like your success function to have only one parameter (the fulfilled promise results), you can utilize it this way:
P.then(_.bind(doWork, {text: 'myArgString'}));
function doWork(data) {
console.log(data + " foo " + this.text);
//promiseResults foo myArgString
}
This will attach text: 'myArgString' to the this context within the function.
use this so you can access global variable inside the promise body
var ref=this;
Example
p.then((data)=>{
var ref=this;
});

Function doesn't return anything (async?)

I'm trying to add XPATH evaluation into my form. When user fills XPATH, it's evaluated on a server using AJAX and then return true or false.
The problem is that this function seems to return undefined allways. I suppose it's because of asynchronious behaviour of JS so I used $.when but it didn't helped.
function evalXpath(xpath) {
var test = $.post('/api/test-xpath/', {'xpath': xpath});
test.done(function (data) {
console.log('BEFORE RETURN '+Boolean(data['success']));
return Boolean(data['success']);
})
}
$(document).ready(function () {
$('#id_xpath').on('change', function () {
var xpath = $("#id_xpath").val();
$.when(evalXpath(xpath)).done(function (evaluated) {
console.log('RETURNED '+evaluated);
$('#xpath-valid').text(evaluated ? 'VALID' : 'INVALID');
});
});
});
The console output (as you can see, it's still asynchronious):
Do you have any ideas?
You're really close. A couple of things:
You forgot to return the promise out of evalXpath.
To get proper promise value chaining (e.g., in order for your value from the callback inside evalXpath to then become the resolution value of the promise it returns), use then, not done.
Then when using evalXpath, there's no need for $.when.
So:
function evalXpath(xpath) {
var test = $.post('/api/test-xpath/', {'xpath': xpath});
return test.then(function (data) {
// ^^^^^^ ^^^^
console.log('BEFORE RETURN '+Boolean(data['success']));
return Boolean(data['success']);
})
}
// ...
evalXpath(xpath).then(function (evaluated) {
// ^^^^ (this one could be `done`, but let's be consistent)

chaining async method calls - javascript

You have a prototype object Foo with two async method calls, bar and baz.
var bob = new Foo()
Foo.prototype.bar = function land(callback) {
setTimeout(function() {
callback()
console.log('bar');
}, 3000);
};
Foo.prototype.baz = function land(callback) {
setTimeout(function() {
callback()
console.log('baz');
}, 3000);
};
We want to do bob.bar().baz() and have it log "bar" and "baz" sequentially.
If you cannot modify the method calls (including passing in your callback function), how can you pass a default callback into these method calls?
Some ideas:
Wrap "bob" with decorator (still fuzzy on how to implement, could use a small example)
Modify constructor to assign default callback if none assigned (have not considered if this is possible or not)
Use a generator wrapper that will continue to call next method until none are left?
The more recommended way instead is to use promises as this is the community-wide practice to do async work.
We want to do bob.bar().baz() and have it log "bar" and "baz"
sequentially.
Why would you want to do that just to achieve this bob.bar().baz() "syntax"? You could do it pretty neatly using the Promise API w/o additional efforts to make that syntax work that indeed increases code complexity reducing the actual readability.
So, you might want to consider using the promise-based approach like this. It offers much flexibility than what you would have achieved with your approach:
Foo.prototype.bar = function () {
return new Promise(function (resolve) {
setTimeout(function () {
resolve()
console.log('bar');
}, 3000);
};
};
Foo.prototype.baz = function () {
return new Promise(function (resolve) {
setTimeout(function () {
resolve()
console.log('baz');
}, 3000);
};
};
Now you'd do this to run them sequentially one after another:
var bob = new Foo();
bob.bar().then(function() {
return bob.baz();
});
// If you're using ES2015+ you could even do:
bob.bar().then(() => bob.baz());
If you need to chain more functions you could simply do it:
bob.bar()
.then(() => bob.baz())
.then(() => bob.anotherBaz())
.then(() => bob.somethingElse());
Anyway, if you're not used to using promises you might want to read this
Warning this isn't quite right yet. Ideally we'd subclass Promise and have proper then/catch functionality but there are some caveats with subclassing bluebird Promise. The idea is to store an internal array of promise generating functions, then when a Promise is waited on (then/await) serially wait on those promises.
const Promise = require('bluebird');
class Foo {
constructor() {
this.queue = [];
}
// promise generating function simply returns called pGen
pFunc(i,pGen) {
return pGen();
}
bar() {
const _bar = () => {
return new Promise( (resolve,reject) => {
setTimeout( () => {
console.log('bar',Date.now());
resolve();
},Math.random()*1000);
})
}
this.queue.push(_bar);
return this;
}
baz() {
const _baz = () => {
return new Promise( (resolve,reject) => {
setTimeout( () => {
console.log('baz',Date.now());
resolve();
},Math.random()*1000);
})
}
this.queue.push(_baz);
return this;
}
then(func) {
return Promise.reduce(this.queue, this.pFunc, 0).then(func);
}
}
const foo = new Foo();
foo.bar().baz().then( () => {
console.log('done')
})
result:
messel#messels-MBP:~/Desktop/Dropbox/code/js/async-chain$ node index.js
bar 1492082650917
baz 1492082651511
done
If you want to avoid callback hell and keep your sanity ES6 promises are the most appropriate approach for the sake of functional programming. You just chain up your sequential asynchronous tasks in the asynchronous timeline just like working in a synchronous timeline.
In this particular case you just need to promisify your asynchronous functions. Assume that your asynch functions takes a data and a callback like asynch(data,myCallback). Let us assume that the callback is error first type.
Such as;
var myCallback = (error,result) => error ? doErrorAction(error)
: doNormalAction(result)
When your asynch function is promisified, you will actually be returned a function which takes your data and returns a promise. You are expected to apply myCallback at the then stage. The return value of myCallback will then be passed to the next stage at where you can invoke another asynch function supplied with the return value of myCallback and this goes on and on as long as you need. So let's see how we shall implement this abstract to your workflow.
function Foo(){}
function promisify(fun){
return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}
function myCallback(val) {
console.log("hey..! i've got this:",val);
return val;
}
var bob = new Foo();
Foo.prototype.bar = function land(value, callback) {
setTimeout(function() {
callback(false,value*2); // no error returned but value doubled and supplied to callback
console.log('bar');
}, 1000);
};
Foo.prototype.baz = function land(value, callback) {
setTimeout(function() {
callback(false,value*2); // no error returned but value doubled and supplied to callback
console.log('baz');
}, 1000);
};
Foo.prototype.bar = promisify(Foo.prototype.bar);
Foo.prototype.baz = promisify(Foo.prototype.baz);
bob.bar(1)
.then(myCallback)
.then(bob.baz)
.then(myCallback)

get element attribute value in Protractor

I'm writing a Protractor test that has to wait for an element attribute to have a non-empty value and then I want to return that value to the caller function. This has proven to be more difficult to write than I expected!
I am able to correctly schedule a browser.wait() command to wait for the element attribute to have a non-empty value and I have verified that this value is in fact what I am expecting to get inside the callback function, but for some reason, I am not able to return that value outside of the callback function and onto the rest of the test code.
Here is how my code looks like:
function test() {
var item = getItem();
console.log(item);
}
function getItem() {
var item;
browser.wait(function() {
return element(by.id('element-id')).getAttribute('attribute-name').then(function(value) {
item = value;
// console.log(item);
return value !== '';
});
});
return item;
}
I can tell that the order of execution is not as I expect it to be, because when I uncomment the console.log() call inside the callback function, I see the expected value printed out. However, the same call in the test() function prints 'undefined'.
What is going on here? What am I missing? How can I get the attribute value out of the callback function properly?
I appreciate your help.
I would not combine the wait and the getting attribute parts - logically these are two separate things, keep them separate:
browser.wait(function() {
return element(by.id('element-id')).getAttribute("attribute").then(function(value) {
item = value;
// console.log(item);
return value !== '';
});
});
element(by.id('element-id')).getAttribute("attribute").then(function (value) {
console.log(value);
});
Note that, you may simplify the wait condition this way:
var EC = protractor.ExpectedConditions;
var elm = $('#element-id[attribute="expected value"]');
browser.wait(EC.presenceOf(elm), 5000);
elm.getAttribute("attribute").then(function (value) {
console.log(value);
});
Just FYI, you may have solved your current problem with the deferred:
function test() {
getItem().then(function (value) {
console.log(value);
});
}
function getItem() {
var item = protractor.promise.defer();
browser.wait(function() {
return element(by.id('element-id')).getAttribute('attribute').then(function(value) {
var result = value !== '';
if (result) {
item.fulfill(value);
}
return result;
});
});
return item.promise;
}
After doing some more reading about how protractor works with promises and schedules/registers them with the control flow, I found an easier work-around close to the first solution #alecxe provided. Here it goes:
function test() {
var item = getItem().then(function(item) {
console.log(item);
});
}
function getItem() {
return browser.wait(function() {
return element(by.id('element-id')).getAttribute('attribute-name').then(function(value) {
return value;
});
});
}
Since browser.wait() returns a promise itself, it can be chained with another then() inside the caller and this way the right order of execution is guaranteed.

Promises, pass additional parameters to then chain

A promise, just for example:
var P = new Promise(function (resolve, reject) {
var a = 5;
if (a) {
setTimeout(function(){
resolve(a);
}, 3000);
} else {
reject(a);
}
});
After we call the .then() method on the promise:
P.then(doWork('text'));
Then doWork function looks like this:
function doWork(data) {
return function(text) {
// sample function to console log
consoleToLog(data);
consoleToLog(b);
}
}
How can I avoid returning an inner function in doWork, to get access to data from the promise and text parameters? Are there any tricks to avoiding the inner function?
Perhaps the most straightforward answer is:
P.then(function(data) { return doWork('text', data); });
Or, since this is tagged ecmascript-6, using arrow functions:
P.then(data => doWork('text', data));
I find this most readable, and not too much to write.
You can use Function.prototype.bind to create a new function with a value passed to its first argument, like this
P.then(doWork.bind(null, 'text'))
and you can change doWork to,
function doWork(text, data) {
consoleToLog(data);
}
Now, text will be actually 'text' in doWork and data will be the value resolved by the Promise.
Note: Please make sure that you attach a rejection handler to your promise chain.
Working program: Live copy on Babel's REPL
function doWork(text, data) {
console.log(text + data + text);
}
new Promise(function (resolve, reject) {
var a = 5;
if (a) {
setTimeout(function () {
resolve(a);
}, 3000);
} else {
reject(a);
}
})
.then(doWork.bind(null, 'text'))
.catch(console.error);
Use currying.
var P = new Promise(function (resolve, reject) {
var a = 5;
if (a) {
setTimeout(function(){
resolve(a);
}, 3000);
} else {
reject(a);
}
});
var curriedDoWork = function(text) {
return function(data) {
console.log(data + text);
}
};
P.then(curriedDoWork('text'))
.catch(
//some error handling
);
The new answer to this question is to use arrow functions (which automatically bind the this and are much more readable). Google for links such as:
https://2ality.com/2016/02/arrow-functions-vs-bind.html
You can set the text like:
this.text = 'text';
P.then(data => doWork(data));
Note: this.text inside doWork will evaluate to 'text'.
This is suggested by jib above and that (or this!) should be the accepted answer now.
Lodash offers a nice alternative for this exact thing.
P.then(_.bind(doWork, 'myArgString', _));
//Say the promise was fulfilled with the string 'promiseResults'
function doWork(text, data) {
console.log(text + " foo " + data);
//myArgString foo promiseResults
}
Or, if you'd like your success function to have only one parameter (the fulfilled promise results), you can utilize it this way:
P.then(_.bind(doWork, {text: 'myArgString'}));
function doWork(data) {
console.log(data + " foo " + this.text);
//promiseResults foo myArgString
}
This will attach text: 'myArgString' to the this context within the function.
use this so you can access global variable inside the promise body
var ref=this;
Example
p.then((data)=>{
var ref=this;
});

Categories