How to break a promise chain? - javascript

I'm aware that there are similar questions, but I haven't seen any that address this chaining pattern.
I have the following:
var runTests = function (chain, resolutionTest) {
return chain.then(function (result) {
if (result)
return result; // Early return if the previous tests were successful. This is where I want to prevent other attempts.
const attempt = tryOpenStream(resolutionTest).then(streamToDom);
return attempt;
});
}
// from someplace else
numTests = resolutionTests.length;
return resolutionTests.reduce(runTests, Promise.resolve()); // start reduce with an empty promise
The problem that I'm encountering is that I'm calling tryOpenStream many times even after I've captured a result.
Options I'm considering:
Raise some global flag that just prevents further execution from within the chain. Yuck, because the chain still proceeds, it's just emptied.
throw new Error(result) instead of return result. This would break the chain (I think...) but it's misusing Error and would be easily misunderstood by another developer.
How can I break this chain at return result;?
UPDATE 1
I'm trying the following:
var makeTest = function (runMoreTests, resolutionTest) {
return function runTest() {
return tryOpenStream(resolutionTest).then(streamToDom).then(function (result) {
if (result)
return result;
else
return runMoreTests();
});
};
}
return resolutionTestBuilder.buildTests().then(function (resolutionTests) {
numTests = resolutionTests.length;
return resolutionTests.reduceRight(makeTest, function () { Promise.reject("No resolutions succeeded.") })();
});
However no calls to runTest are invoked. This is a bit of new syntax for me so I'll research some and update with any findings.
UPDATE 2
I was missing the () to invoke the reduceRight. Though now I'm seeing that reject called even with success... though when I step through, that rejection isn't invoked. It's as if by the time I get a result back, all links in the chain have been invoked.

Both flags and exceptions can be used, but as you noticed they're not the proper tool.
Instead, use recursion, like in #IsiahMeadows' answer, or a right fold:
var makeTest = function (runMoreTests, resolutionTest) {
return function runTest(result) {
if (result)
return result;
return tryOpenStream(resolutionTest).then(streamToDom).then(runMoreTests);
};
}
return Promise.resolve(resolutionTests.reduceRight(makeTest, x => x)(undefined));
or better written as
var makeTest = function (runMoreTests, resolutionTest) {
return function runTest() {
return tryOpenStream(resolutionTest).then(streamToDom).then(result => {
if (result)
return result;
else
return runMoreTests();
});
};
}
return resolutionTests.reduceRight(makeTest, () => Promise.reject("nothing succeeded"))();

Try using this:
function runTests(resolutionTest) {
return tryOpenStream(resolutionTest).then(streamToDom)
}
// from someplace else
function loop(tests, i) {
if (i === tests.length) return undefined
return runTests(tests[i]).then(function (result) {
if (result) return result
return loop(tests, i + 1)
})
}
return loop(resolutionTests, 0)
Although I do wonder why you can't use an exception to denote your tryOpenStream failed. That would actually simplify your code some.

Related

JavaScript: Recursive function with Promise, resolve returns "undefined"

I have a recursive function which is returning promise, there is some conditions which on fulfillment the resolve will fire. But the resolve always returns undefined.
Here is the code:
var children = [];
var temp = [];
function getRelationsOfRep(repId) {
var index;
return new Promise(function (resolve, reject) {
return RepRelation.findOne({_id: repId}).then(function (repData) {
if (<condition>) {
temp = temp.concat(repData.children);
temp.forEach(function (childId) {
return getRelationsOfRep(childId);
});
} else {
// else
}
if (<another condition>) {
return resolve(children);
}
}).catch(function (error) {
return reject(error);
})
})
}
function updateRepData(id){
children = [];
temp = [];
getRelationsOfRep(id).then(function (branchesData) {
console.log('branchesData:: ', branchesData); // it prints undefined
})
}
I'm trying to get all children of a Representative and the children of its children (if any) and return them all.
What is it that I'm doing wrong?
Any help would be much appreciated.
Thanks in advance.
Use Promise.all() to capture all the promises in Loop.
Instead of:
temp.forEach(function (childId) {
return getRelationsOfRep(childId);
});
Try:
var promisesArray = [];
temp.forEach(function (childId) {
promisesArray.push(getRelationsOfRep(childId));
});
return Promise.all(promisesArray);
Using something like this, you will be able to get nested response for the recursive calls.
P.S. Not tested, modify according to the logic you want.
You are making things slightly complicated. I think what you want to achieve is to use promise to return a result from a recursive function.
Here's a cleaner version
A cleaner solution -
getRelationsOfRep(repId) {
return new Promise( function(resolve,reject) {
recursiveFunctionCall(repId);
function recursiveFunctionCall(repId) {
//your recursive function logic
recursiveFunctionCall(childId);
if(...) //edge case condition
resolve('your-answer');
else
reject('your-error')
}
});
This way, you'll just be using one promise and returning when your recursive function resolves.

Having if-else condition inside promises chains

I have a promises chain and inside some points I have if-else condition like the following:
.then(function() {
if(isTrue) {
// do something returning a promise
} else {
// do nothing - just return
return;
}
})
.then(function() {
...
})
Honestly I don't like this pattern. I feel it wrong. I mean using just a plain return without anything. Do you have some idea to make look different this code?
That else { return; } part can simply be completely omitted without changing the code's meaning:
.then(function() {
if (isTrue) {
// do something returning a promise
}
})
Functions do return undefined anyway by default.
I guess you have tested the code. And recognized that this is not working like you expected. Let me explain you:
function getPromise() {
callSomeFunctionWhichReturnsPromise().then(function(result) {
return result; // You hope, that this will be logged on the console? nope, you have to do it here instead.
console.log('logged in the promise', result); // This will work
});
}
var result = getPromise();
console.log(result); // undefined!!!
you could instead do this:
function getPromise() {
return callSomeFunctionWhichReturnsPromise();
}
var result = getPromise();
result.then(console.log); // will call console.log(arguments)

how can to execute multiple actions in one line?

In my case I have one repository like this from temphire (breeze)
define(['durandal/system'], function (system) {
var Repository = (function () {
var repository = function (entityManagerProvider, entityTypeName, resourceName, fetchStrategy) {
.........
this.find = function (predicate) {
var query = breeze.EntityQuery
.from(resourceName)
.where(predicate);
return executeQuery(query);
};
function executeQuery(query) {
return entityManagerProvider.manager()
.executeQuery(query.using(fetchStrategy || breeze.FetchStrategy.FromServer))
.then(function (data) { return data.results; });
}
................
};
return repository;
})();
return {
create: create,
getCtor: Repository
};
function create(entityManagerProvider, entityTypeName, resourceName, fetchStrategy) {
return new Repository(entityManagerProvider, entityTypeName, resourceName, fetchStrategy);
}
});
NOW
HOW CAN DO LIKE SOME THIS
repository.query(predicate).execute();
function query(predicate) {
return query = breeze.EntityQuery
.from(resourceName)
.where(predicate);
};
function executeQuery(query) {
return entityManagerProvider.manager().executeQuery(query.using(fetchStrategy || breeze.FetchStrategy.FromServer)).then(function(data) {
return data.results;
});
}
function execute() -- >
return executeQuery
the first action return query and after to execute
many thanks
I think the problem with what you are trying is that return terminates execution. If you want to do something as well as return in that function, then you need to do it before you return.
If, on the other hand, you really need to return the value and then execute something, then you should have the method that calls the function expecting the return, call the function to get the return value, and then have that calling function execute the thing you want executed. If that execution needs some data from the function that returns the value, then return that information with the value returned, and pass it into the function that does the execution.
Use
executeQueryLocally // This is syn
instead of
executeQuery // This is async
executeQuery sync

Javascript scope issue on return function

I've created a function into backbone view that return true or false under some condition.
The issue is that the return value is evere undefined. I think it is a scope problem.
This issue is different from Ajax return because my return is inside an iteration and not in an ajx call. The previous Ajax call in my code is sync and not async.The console.log inside my iteration is correctly printed, only return statement seems doesn't work.
isAlreadyRegistered: function(){
this.checkUser = new Utenti();
this.checkUser.fetch({async:false});
_.each(this.checkUser.models, function (user) {
if(user.get("idTwitter") === this.utente.get('idTwitter')){
console.log("gia reg");
return true;
} else {
console.log("non reg");
return false;
}
}, this);
}
console.log(isAlreadyRegistered());//ever undefined
You don't want to use each, but every or some. They will return booleans depending on what your callback invocations did return.
isAlreadyRegistered: function(){
this.checkUser = new Utenti();
this.checkUser.fetch({async:false});
var id = this.utente.get('idTwitter');
return _.some(this.checkUser.models, function (user) {
return user.get("idTwitter") === id;
});
}

Why prototype function returns nothing in javascript?

My javascript prototype function like below;
ServerDataEngine.prototype.ExecuteCommand = function(command)
{
try
{
var result;
$.get(command, function (data) {
result = jQuery.parseJSON(atob(data));
console.log(result);
});
return result;
}
catch (Exception)
{
throw (new Exception("Can not connect to server"));
}
}
And I call this function like this;
ServerDataEngine.prototype.ExecuteQuery = function (query)
{
console.log(this.ExecuteCommand(query));
}
In ExecuteCommand, everything is ok, but in ExecuteQuery, console.log(this.ExecuteCommand(command)) line produces undefined.
What is the problem?
You are treating an Asynchronous Request as a synchronous one. You are returning the value before the Ajax call ever returns a value. It is not going to be able to work that way.

Categories

Resources