Nested Promises Stuck - javascript

The following code gets stuck:
var Promise = require('promise');
var testPromise = function(){
return new Promise(function(fulfill, reject){
element.all(by.repeater('item in menu.items')).first().then(function(el){
console.log('test f');
fulfill(el);
console.log('test fe');
});
});
};
... called by the following:
testPromise().then(function(el){
console.log('test successful '+el);
});
The console prints
test f
test fe
And get stuck no more code is executed. It never reaches the then although fulfill has been called.
if using nested promises is an anti pattern then how do I do the following without a nested promise:
var getMenuItemEl = function(itemName){
return new Promise(function(fulfill, reject){
var elFound;
element.all(by.repeater('item in menu.items')).then(function(els){
async.each(els, function(el, callback){
el.getText().then(function(text){
console.log('getMenuItemEl:'+text);
if(text === itemName){
elFound = el;
}
callback();
});
}, function(err){
console.log('complete '+elFound);
if(elFound){
console.log('fulfill');
fulfill(elFound);
console.log('after fulfill');
}else{
reject('no item found');
}
});
});
});
};
This also gets stuck after the fulfill has been called

if using nested promises is an anti pattern then how do I do the following without a nested promise
You would not use the async library. Since all your methods (element.all(), el.getText() etc) do already return promises, you don't need to go back to node-style error callbacks. From my "rules for promises",
every async function (even if it is a callback) should return a promise, and use your libraries' methods to compose them. You really don't need to call the Promise constructor on your own. That iteration you are doing there can be easily done by a map over the els, then collecting all the single promises together with Promise.all.
function getMenuItemEl(itemName) {
return element.all(by.repeater('item in menu.items'))
.then(function(els){
return Promise.all(els.map(function(el) {
return el.getText()
.then(function(text){
console.log('getMenuItemEl:'+text);
return (text === itemName) ? el : null;
});
}));
})
.then(function(foundEls) {
for (var i=0; i<foundEls.length; i++)
if (foundEls[i] != null) {
console.log('fulfill');
return foundEls[i];
}
throw new Error('no item found');
});
}

The following code solves my problem, mainly due to my lack of knowledge of the protractor api
var doClickMenuItemEl = function(itemName){
return element.all(by.repeater('item in menu.items')).filter(function(elem, index) {
return elem.getText().then(function(text) {
return text === itemName;
});
}).then(function(els){
els[0].click();
});
};
It also says on https://github.com/angular/protractor/issues/379 that
Yes, this is inherited from webdriver promises, which are not quite promise A+ compliant, unfortunately.

Related

Don't understand the result of Promise API [duplicate]

I just implemented my first function that returns a promise based on another promise in AngularJS, and it worked. But before I decided to just do it, I spent 2 hours reading and trying to understand the concepts behind promises. I thought if I could write a simple piece of code that simulated how promises worked, I would then be able to conceptually understand it instead of being able to use it without really knowing how it works. I couldn't write that code.
So, could someone please illustrate in vanilla JavaScript how promises work?
A promise is basically an object with two methods. One method is for defining what to do, and one is for telling when to do it. It has to be possible to call the two methods in any order, so the object needs to keep track of which one has been called:
var promise = {
isDone: false,
doneHandler: null,
done: function(f) {
if (this.isDone) {
f();
} else {
this.doneHandler = f;
}
},
callDone: function() {
if (this.doneHandler != null) {
this.doneHandler();
} else {
this.isDone = true;
}
}
};
You can define the action first, then trigger it:
promise.done(function(){ alert('done'); });
promise.callDone();
You can trigger the action first, then define it:
promise.callDone();
promise.done(function(){ alert('done'); });
Demo: http://jsfiddle.net/EvN9P/
When you use a promise in an asynchronous function, the function creates the empty promise, keeps a reference to it, and also returns the reference. The code that handles the asynchronous response will trigger the action in the promise, and the code calling the asynchronous function will define the action.
As either of those can happen in any order, the code calling the asynchronous function can hang on to the promise and define the action any time it wants.
For the simplicity to understand about the promises in Javascript.
You can refer below example. Just copy paste in a new php/html file and run.
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript">
function test(n){
alert('input:'+n);
var promise = new Promise(function(fulfill, reject) {
/*put your condition here */
if(n) {
fulfill("Inside If! match found");
}
else {
reject(Error("It broke"));
}
});
promise.then(function(result) {
alert(result); // "Inside If! match found"
}, function(err) {
alert(err); // Error: "It broke"
});
}
</script>
</head>
<body>
<input type="button" onclick="test(1);" value="Test"/>
</body>
</html>
Click on Test button,
It will create new promise,
if condition will be true it fulfill the response,
after that promise.then called and based on the fulfill it will print the result.
In case of reject promise.then returns the error message.
Probably the simplest example of promises usage looks like that:
var method1 = (addings = '') => {
return new Promise(resolve => {
console.log('method1' + addings)
resolve(addings + '_adding1');
});
}
var method2 = (addings = '') => {
return new Promise(resolve => {
console.log('method2' + addings)
resolve(addings + '_adding2');
});
}
method1().then(method2).then(method1).then(method2);
// result:
// method1
// method2_adding1
// method1_adding1_adding2
// method2_adding1_adding2_adding1
That's basic of basics. Having it, you can experiment with rejects:
var method1 = (addings = '*') => {
return new Promise((resolve, reject) => {
console.log('method1' + addings)
resolve(addings + '_adding1');
});
}
var method2 = (addings = '*') => {
return new Promise((resolve, reject) => {
console.log('method2' + addings)
reject();
});
}
var errorMethod = () => {
console.log('errorMethod')
}
method1()
.then(method2, errorMethod)
.then(method1, errorMethod)
.then(method2, errorMethod)
.then(method1, errorMethod)
.then(method2, errorMethod);
// result:
// method1*
// method2*_adding1
// errorMethod
// method2*
// errorMethod
// method2*
As we can see, in case of failure error function is fired (which is always the second argument of then) and then next function in chain is fired with no given argument.
For advanced knowledge I invite you here.
please check this simple promise code. this will help you to better understand of promise functionality.
A promise is an object that may produce a single value some time in the future: either a resolved value, or a reason that it’s not resolved. A promise may be in one of 3 possible states: fulfilled, rejected, or pending. Promise users can attach callbacks to handle the fulfilled value or the reason for rejection.
let myPromise = new Promise((resolve, reject)=>{
if(2==2){
resolve("resolved")
}else{
reject("rejected")
}
});
myPromise.then((message)=>{
document.write(`the promise is ${message}`)
}).catch((message)=>{
document.write(`the promise is ${message}`)
})
check this out

Node return BluebirdJS Promise

I have a small problem, this script works perfectly, with one problem, the "runTenant" method is not returning a promise (that needs resolving from "all()".
This code:
Promise.resolve(runTenant(latest)).then(function() {
end();
});
Calls this code:
function runTenant(cb) {
return new Promise(function() {
//global var
if (!Tenant) {
loadCoreModels();
Tenant = bookshelf.core.bs.model('Tenant');
}
new Tenant().fetchAll()
.then(function(tenants) {
if (tenants.models.length == 0) {
return;
} else {
async.eachSeries(tenants.models, function(tenant, next) {
var account = tenant.attributes;
Promise.resolve(db_tenant.config(account)).then(function(knex_tenant_config) {
if (knex_tenant_config) {
db_tenant.invalidateRequireCacheForFile('knex');
var knex_tenant = require('knex')(knex_tenant_config);
var knex_pending = cb(knex_tenant);
Promise.resolve(knex_pending).then(function() {
next(null, null);
});
} else {
next(null, null);
}
});
});
};
});
});
}
The code from runTenant is working correctly however it stalls and does not proceed to "end()" because the promise from "runTenant(latest)" isn't being resolved.
As if it weren't apparent, I am horrible at promises. Still working on getting my head around them.
Many thanks for any help/direction!
You should not use the Promise constructor at all here (and basically, not anywhere else either), even if you made it work it would be an antipattern. You've never resolved that promise - notice that the resolve argument to the Promise constructor callback is a very different function than Promise.resolve.
And you should not use the async library if you have a powerful promise library like Bluebird at hand.
As if it weren't apparent, I am horrible at promises.
Maybe you'll want to have a look at my rules of thumb for writing promise functions.
Here's what your function should look like:
function runTenant(cb) {
//global var
if (!Tenant) {
loadCoreModels();
Tenant = bookshelf.core.bs.model('Tenant');
}
return new Tenant().fetchAll().then(function(tenants) {
// if (tenants.models.length == 0) {
// return;
// } else
// In case there are no models, the loop iterates zero times, which makes no difference
return Promise.each(tenants.models, function(tenant) {
var account = tenant.attributes;
return db_tenant.config(account).then(function(knex_tenant_config) {
if (knex_tenant_config) {
db_tenant.invalidateRequireCacheForFile('knex');
var knex_tenant = require('knex')(knex_tenant_config);
return cb(knex_tenant); // can return a promise
}
});
});
});
}
Your promise in runTenant function is never resolved. You must call resolve or reject function to resolve promise:
function runTenant() {
return new Promise(function(resolve, reject) {
// somewhere in your code
if (err) {
reject(err);
} else {
resolve();
}
});
});
And you shouldn't pass cb in runTenant function, use promises chain:
runTenant()
.then(latest)
.then(end)
.catch(function(err) {
console.log(err);
});
You need to return all the nested promises. I can't run this code, so this isn't a drop it fix. But hopefully, it helps you understand what is missing.
function runTenant(cb) {
//global var
if (!Tenant) {
loadCoreModels();
Tenant = bookshelf.core.bs.model('Tenant');
}
return new Tenant().fetchAll() //added return
.then(function (tenants) {
if (tenants.models.length == 0) {
return;
} else {
var promises = []; //got to collect the promises
tenants.models.each(function (tenant, next) {
var account = tenant.attributes;
var promise = Promise.resolve(db_tenant.config(account)).then(function (knex_tenant_config) {
if (knex_tenant_config) {
db_tenant.invalidateRequireCacheForFile('knex');
var knex_tenant = require('knex')(knex_tenant_config);
var knex_pending = cb(knex_tenant);
return knex_pending; //return value that you want the whole chain to resolve to
}
});
promises.push(promise); //add promise to collection
});
return Promise.all(promises); //make promise from all promises
}
});
}

Why isn't .then() waiting for the promise?

I am currently working on a angular project, and I am kind of new to it.
I do not understand, why is .then() function not waiting for the promises?
I think it have to do something with that I only have one $q.defer() inside my getAllStats() function? When I try to console.log("testing: ", data); (on the bottom) it only logs out an empty array. Could someone help me please?
This is my code:
function getAllStats(dataArray, nameOfFile) {
var defer = $q.defer();
var promises = [];
for (index in dataArray) {
if (dataArray[index].indexOf('test') > -1 ) {
getStats(nameOfFile).then(function (data) {
promises.push();
});
}
}
function last() {
defer.resolve(promises);
}
$q.all(promises).then(last);
return defer.promise;
};
function getStats(nameOfFile) {
var defer = $q.defer();
$http.get(nameOfFile).success(function (data) {
defer.resolve(data);
});
return defer.promise;
};
getAllStats('test.txt').then(function(data) {
console.log("testing: ", data);
});
See the comments in this code:
function getAllStats(dataArray, nameOfFile) {
var promises = [];
// using `for (index in dataArray) {` is a bad idea unless
// dataArray is a non-array object
for (var index = 0; index < dataArray.length; index++) {
if (dataArray[index].indexOf('test') > -1 ) {
// Here is the trick, store the promise itself,
// don't try to subscribe to it here
promises.push(getStats(nameOfFile));
}
}
return $q.all(promises);
};
function getStats(nameOfFile) {
// http.get already returns a promise, see explicit promise creation antipattern
return $http.get(nameOfFile).then(function(r) { return r.data; });
};
getAllStats('test.txt').then(function(data) {
console.log("testing: ", data);
});
References:
Explicit promise creation antipattern
Why is for..in bad
Deprecation Notice
The $http legacy promise methods success and error
have been deprecated. Use the standard then method instead. If
$httpProvider.useLegacyPromiseExtensions is set to false then these
methods will throw $http/legacy error.
see: $http
In your example this block:
for (index in dataArray) {
if (dataArray[index].indexOf('test') > -1 ) {
getStats(nameOfFile).then(function (data) {
promises.push();
});
}
}
Takes dataArray, which is a string and runs through it char by char. Also, you are not setting nameOfFile. Change the call to:
getAllStats(['test.txt']).then(function(data) {
console.log("testing: ", data);
});
And then to make the push to promises be correct do like this:
promises.push(getStats(dataArray[index]));
Multiple issues wrong here:
You're passing an empty promises array to $q.all(). It has to be an array of promises at the time you pass it to $q.all().
You're creating promises when you can just return the ones you have
getAllStats() expects an array, but you're passing a string.
I'd suggest this overall cleanup of the code that fixes the above issues:
function getAllStats(dataArray) {
var promises = dataArray.filter(function(item) {
return item.indexOf('test') !== -1;
}).map(function(item) {
return $http.get(item);
});
return $q.all(promises);
};
getAllStats(['test.txt']).then(function(data) {
console.log("testing: ", data);
});
I'd also suggest you read about promise anti-patterns to teach yourself how to use the promises you already have and avoid creating new ones when new ones are not necessary.
P.S. I'm not sure what was the point of the nameOfFile argument since you don't want to be getStats() on the same file over and over again.

Q Promise chain and NodeJS callbacks

I think this is a really stupid question but I'm having a hard time wrapping my head around promises.
I'm using Q (for nodejs) to sync up a couple of async functions.
This works like a charm.
var first = function () {
var d = Q.defer();
fs.readdir(path,function(err,files){
if(err) console.log(err);
d.resolve(files);
});
return d.promise;
};
var second = function (files) {
var list = new Array;
files.forEach(function(value, index){
var d = Q.defer();
console.log('looking for item in db', value);
db.query(
'SELECT * FROM test WHERE local_name =? ', [value],{
local_name : String,
},
function(rows) {
if (typeof rows !== 'undefined' && rows.length > 0){
console.log('found item!', rows[0].local_name);
d.resolve(rows[0]);
} else {
var itemRequest = value;
getItemData(itemRequest);
}
}
);
list.push(d.promise);
});
return Q.all(list);
};
first()
.then(second)
.done(function(list){
res.send(list);
});
The problem I have is with this little function:
getItemData(itemRequest)
This function is filled with several of callbacks. The promise chain runs through the function just fine but ignores all the callbacks I use ( eg several XHR calls I make in the function).
A simplified version of the function looks like this (just to give you an idea):
function getItemData(itemRequest){
helper.xhrCall("call", function(response) {
var requestResponse = JSON.parse(response)
, requestInitialDetails = requestResponse.results[0];
downloadCache(requestInitialDetails,function(image) {
image = localImageDir+requestInitialDetails.image;
helper.xhrCall("call2", function(response) {
writeData(item,image,type, function(){
loadData(item);
});
});
} else {
writeData(item,image,type, function(){
loadData(item);
});
}
});
});
The xhr function I use looks like this:
xhrCall: function (url,callback) {
var request = require("request")
, colors = require('colors');
request({
url: url,
headers: {"Accept": "application/json"},
method: "GET"
}, function (error, response, body) {
if(!error){
callback(body);
}else{
console.log('Helper: XHR Error',error .red);
}
});
}
So my questions:
Can I leave the function unaltered and use the callbacks that are in place ánd the promise chain?
Or do I have to rewrite the function to use promises for the XHR?
And if so, How can I best write my promise chain? Should I reject the initial promise in the forEach?
Again, sorry if this is a really stupid question but I don't know what the right course of action is here.
Thanks!
[EDIT] Q.nfcall, I don't get it
So I've been looking into Q.nfcall which allows me to use node callbacks. Bu I just don't understand exacly how this works.
Could someone give a simple example how I would go about using it for a function with several async xhr calls?
I tried this but as you can see I don't really understand what I'm doing:
var second = Q.nfcall(second);
function second (files) {
[EDIT 2]
This is the final funcction in my getitemdata function callback chain. This function basically does the same as the function 'second' but I push the result directly and then return the promise. This works as stated, but without all the additional callback data, because it does not wait for the callbacks to return with any data.
function loadData(item) {
var d = Q.defer();
db.query(
'SELECT * FROM test WHERE local_name =? ', [item],{
local_name : String,
},
function(rows) {
if (typeof rows !== 'undefined' && rows.length > 0){
list.push(d.promise);
}
}
);
});
return Q.all(list);
};
Your answer is not really clear after your second edit.
First, on your orignal question, your getItemData has no influence on the promise chain.
You could change you the function's call signature and pass your deferred promise like so.
getItemData(itemRequest, d)
and pass this deferred promises all the way to your xhrCall and resolve there.
I would re-write your whole implementation and make sure all your functions return promises instead.
Many consider deferred promises as an anti-pattern. So I use use the Promise API defined in harmony (the next javascript)
After said that, I would re-implement your original code like so (I've not tested)
var Promise = Promise || require('es6-promise').Promise // a polyfill
;
function errHandler (err){
throw err
}
function makeQuery () {
var queryStr = 'SELECT * FROM test WHERE local_name =? '
, queryOpt = {local_name: String}
;
console.log('looking for item in db', value)
return new Promise(function(resolve, reject){
db.query(queryStr, [value], queryOpt, function(rows) {
if (typeof rows !== 'undefined' && rows.length > 0){
console.log('found item!', rows[0].local_name);
resolve(rows[0]);
} else {
// note that it returns a promise now.
getItemData(value).then(resolve).catch(errHandler)
}
})
})
}
function first () {
return new Promise(function(resolve, reject){
fs.readdir(path, function(err, files){
if (err) return reject(err)
resolve(files)
})
})
}
function second (files) {
return Promise.all(files.map(function(value){
return makeQuery(value)
});
}
first()
.then(second)
.then(res.send)
.catch(errHandler)
Note that there is no done method on the Promise API.
One down side of the new Promise API is error handling. Take a look at bluebird.
It is a robust promise library which is compatible with the new promise API and has many of the Q helper functions.
As far as I can tell, you need to return a promise from getItemData. Use Q.defer() as you do in second(), and resolve it when the callbacks complete with the data. You can then push that into list.
To save code, you can use Q.nfcall to immediately call a node-style-callback function, and return a promise instead. See the example in the API docs: https://github.com/kriskowal/q/wiki/API-Reference#qnfcallfunc-args

Resolving a deferred using Angular's $q.when() with a reason

I want to use $q.when() to wrap some non-promise callbacks. But, I can't figure out how to resolve the promise from within the callback. What do I do inside the anonymous function to force $q.when() to resolve with my reason?
promises = $q.when(
notAPromise(
// this resolves the promise, but does not pass the return value vvv
function success(res) { return "Special reason"; },
function failure(res) { return $q.reject('failure'); }
)
);
promises.then(
// I want success == "Special reason" from ^^^
function(success){ console.log("Success: " + success); },
function(failure){ console.log("I can reject easily enough"); }
);
The functionality I want to duplicate is this:
promises = function(){
var deferred = $q.defer();
notAPromise(
function success(res) { deferred.resolve("Special reason"); },
function failure(res) { deferred.reject('failure'); }
);
return deferred.promise;
};
promises.then(
// success == "Special reason"
function(success){ console.log("Success: " + success); },
function(failure){ console.log("I can reject easily enough"); }
);
This is good, but when() looks so nice. I just can't pass the resolve message to then().
UPDATE
There are better, more robust ways to do this. $q throws exceptions synchronously, and as #Benjamin points out, the major promise libs are moving toward using full Promises in place of Deferreds.
That said, this question is looking for a way to do this using $q's when() function. Objectively superior techniques are of course welcome but don't answer this specific question.
The core problem
You're basically trying to convert an existing callback API to promises. In Angular $q.when is used for promise aggregation, and for thenable assimilation (that is, working with another promise library). Fear not, as what you want is perfectly doable without the cruft of a manual deferred each time.
Deferred objects, and the promise constructor
Sadly, with Angular 1.x you're stuck with the outdated deferred interface, that not only like you said is ugly, it's also unsafe (it's risky and throws synchronously).
What you'd like is called the promise constructor, it's what all implementations (Bluebird, Q, When, RSVP, native promises, etc) are switching to since it's nicer and safer.
Here is how your method would look with native promises:
var promise = new Promise(function(resolve,reject){
notAPromise(
function success(res) { resolve("Special reason") },
function failure(res) { reject(new Error('failure')); } // Always reject
) // with errors!
);
You can replicate this functionality in $q of course:
function resolver(handler){
try {
var d = $q.defer();
handler(function(v){ d.resolve(v); }, function(r){ d.reject(r); });
return d.promise;
} catch (e) {
return $q.reject(e);
// $exceptionHandler call might be useful here, since it's a throw
}
}
Which would let you do:
var promise = resolver(function(resolve,reject){
notAPromise(function success(res){ resolve("Special reason"),
function failure(res){ reject(new Error("failure")); })
});
promise.then(function(){
});
An automatic promisification helper
Of course, it's equally easy to write an automatic promisification method for your specific case. If you work with a lot of APIs with the callback convention fn(onSuccess, onError) you can do:
function promisify(fn){
return function promisified(){
var args = Array(arguments.length + 2);
for(var i = 0; i < arguments.length; i++){
args.push(arguments[i]);
}
var d = $q.defer();
args.push(function(r){ d.resolve(r); });
args.push(function(r){ d.reject(r); });
try{
fn.call(this, args); // call with the arguments
} catch (e){ // promise returning functions must NEVER sync throw
return $q.reject(e);
// $exceptionHandler call might be useful here, since it's a throw
}
return d.promise; // return a promise on the API.
};
}
This would let you do:
var aPromise = promisify(notAPromise);
var promise = aPromise.then(function(val){
// access res here
return "special reason";
}).catch(function(e){
// access rejection value here
return $q.reject(new Error("failure"));
});
Which is even neater
Ok here's my interpretation of what I think you want.
I am assuming you want to integrate non-promise callbacks with a deferred/promise?
The following example uses the wrapCallback function to wrap two non-promise callbacks, successCallback and errCallback. The non-promise callbacks each return a value, and this value will be used to either resolve or reject the deferred.
I use a random number to determine if the deferred should be resolved or rejected, and it is resolved or rejected with the return value from the non-promise callbacks.
Non angular code:
function printArgs() {
console.log.apply(console, arguments);
}
var printSuccess = printArgs.bind(null, "success");
var printFail = printArgs.bind(null, "fail");
function successCallback() {
console.log("success", this);
return "success-result";
}
function errCallback() {
console.log("err", this);
return "err-result";
}
function wrapCallback(dfd, type, callback, ctx) {
return function () {
var result = callback.apply(ctx || this, arguments);
dfd[type](result);
};
}
Angular code:
var myApp = angular.module('myApp', []);
function MyCtrl($scope, $q) {
var dfd = $q.defer();
var wrappedSuccess = wrapCallback(dfd, "resolve", successCallback);
var wrappedErr = wrapCallback(dfd, "reject", errCallback);
var rnd = Math.random();
var success = (rnd > 0.5);
success ? wrappedSuccess() : wrappedErr();
console.log(rnd, "calling " + (success ? "success" : "err") + " callback");
dfd.promise.then(printSuccess, printFail);
}
Example output where the random number is less than 0.5, and so the deferred was rejected.
err Window /fiddlegrimbo/m2sgu/18/show/
0.11447505658499701 calling err callback
fail err-result

Categories