I am using Ionic 3 with Angular.
I have multiple asyn functions:
async buildNewsItemsViaHttp(){
let result = await this.http.get()....
}
async buildNewsItemsViaLocalJSON(){
return await this.http.get()....
}
async getNewsItems(){
return await this.storage.get()...
}
I would like to run the async functions conditionally based on if they return a value or not. This means:
if getNewsItems() would return data, return it
if not, run buildNewsItemsViaHttp and see if it returns data
if not, run buildNewsItemsViaLocalJSON
Suggestion:
async getItems(){
let result = await.this.getNewsItems();
if (result) {
return result;
} else {
result = await.this.buildNewsItemsViaHttp();
if (result) {
return result;
} else {
result = await.this.buildNewsItemsViaLocalJSON();
return result;
}
}
}
Is this solution correct?
Thanks in advance
You should use sort of chaining to achieve what you want, this below is a bit naive but should work:
getNewsItems().then((result)=>{
if (result === "what you need") {
// do here what you want with the result data
} else {
buildNewsItemsViaHttp().then((result)=>{
if (result === "what you need") {
// do here what you want with the result data
} else {
buildNewsItemsViaLocalJSON.then((result)=>{
// do here what you need with result
})
}
})
}})
And of course you should also have logic to catch errors (.catch(err)=>{})
Related
I want my program to execute the code in the second else statement when reponse equals to 0.
However, it seems like it's not waiting for the function to complete and always executes what's in the true branch.
I'm not really familiar with async/await and would appreciate some guidance here.
async function fetchInvoice(currentValue, callback) {
let requestData = basePayload;
requestData.body = //requestbody;
let productData = await fetch(baseUrl, requestData);
let jsonData = await productData.json();
if (await jsonData.result.records.length !== 0) {
//code
} else {
return false;
}
};
if (fetchInvoice(myParameter)) {
//code
} else {
//code
}
There are (at least) two issues...
First, myFunction never returns true. For any logical condition where you want the function to return true, add a return statement:
return true;
Otherwise, if the function doesn't return anything, its result is undefined which is falsy.
Second, you forgot to await the async function:
if (await fetchInvoice(myParameter)) {
//code
} else {
//code
}
Or, if this code is executing in a context which can't use await (such as top-level code in a browser) then you can follow up the Promise with .then:
fetchInvoice(myParameter).then(x => {
if (x) {
//code
} else {
//code
}
});
As an aside, you don't need await here:
if (await jsonData.result.records.length !== 0) {
Don't just randomly sprinkle keywords around the code. await is used for a specific purpose, to await the result of a Promise. There's no Promise in this operation.
You need to actually call myFunction and await/.then. If you just call it, it will return a Promise (because it's async) which is truthy. awaiting it will give you the actual value
if (await myFunction()) {
// True
} else {
// False
}
Here is one example function.
async function getRandomBig() {
let result;
result = await randomModule.getRandom();
if (result > 0.9 ) {
return getRandomBig();
} else {
return result;
}
}
So Obviously, I would like that execution to randomModule.getRandom() happens asynchronously. Is this example the way to go?
Also, I would like to know how to make the first call to getOutput be asynchronous.
Thank you
It's a little hard to answer your question given the info you've provided, but maybe this will help:
The async function will return a promise to the outside caller. Than means that results obtained from await will happen asynchronously. For example consider this code:
// normal synchronous function
function changeRes() {
return "after"
}
// sync function that `awaits` it's result
async function getget() {
res = await changeRes()
}
let res = "before"
// p will be a pending promise
var p = getget()
// what is res at this point?
console.log(res) // still "before" because await doesn't return until next tick
// now res is 'after'
p.then(() => console.log(res))
But be careful because the call to changeRes is not asynchronous — it is called in the current loop. Compare this with the first code. We only change the behavior of changeRes():
function changeRes() {
res = "after"
return res
}
async function getget() {
res = await changeRes()
}
let res = "before"
// p is still a pending promise
var p = getget()
// but res was changed in changeRes synchronously
console.log(res)
p.then(() => console.log(res))
EDIT based on comment:
With the recursive function, everything important is happening inside the async function, so it all should work as expected. For example, you can substitute your randomModule.getRandom with the regular Math.random() which is synchronous, but the await will make it work in the context of an async function. So this function will return a promise that resolves to a random float less than 0.25:
async function getRandomBig() {
let result;
result = await Math.random();
if (result > 0.25) {
return getRandomBig();
} else {
return result;
}
}
getRandomBig().then(console.log)
So will this even though it's truly async:
function asyncRandom(){
return new Promise(resolve => {
setTimeout(() => resolve(Math.random()), 500)
})
}
async function getRandomBig() {
let result;
result = await asyncRandom();
if (result > 0.25 ) {
console.log("too big recurse")
return getRandomBig();
} else {
return result;
}
}
getRandomBig().then(console.log)
I have a conditional statement in which I need to perform one of two operations, then continue after whichever operation has resolved. So my code currently looks as follows:
if (shoud_do_thing_a) { //should_do_thing_a is just a variable that determines which function to call. it is not a promise
do_thing_a()
} else {
do_thing_b()
}
// more code
The issue is that both do_thing_a and do_thing_b return promises, and I can't move on until whichever gets executed has resolved. The best way I've come up with to solve this is like this:
var more_code = function () {
// more code
}
if (shoud_do_thing_a) {
do_thing_a().then(more_code)
} else {
do_thing_b().then(more_code)
}
I don't like this structure. It's difficult to follow because you need to jump around to find where more_code is defined (imagine I have this type of control flow in several locations), rather than simply being able to continue reading.
Is there a better way to deal with this type of thing in javascript?
If you can use async/await
async function someFunc() {
var more_code = function () {
// more code
}
if (shoud_do_thing_a) {
await do_thing_a()
} else {
await do_thing_b()
}
more_code()
}
Or if you can't, use then():
var more_code = function () {
// more code
}
var do_thing;
if (shoud_do_thing_a) {
do_thing = do_thing_a()
} else {
do_thing = do_thing_b()
}
do_thing.then(more_code)
If you're stuck with raw Promises and can't use async/await (You usually should have no trouble, what with babel/typescript etc), the following is a bit more elegant than storing the promise in a variable:
function something() {
return Promise.resolve()
.then(() => {
if (should_do_thing_a) {
return do_thing_a();
}
else if (should_do_thing_b) {
return do_thing_b();
}
})
.then(some_more_code);
}
Note that when you start working with Promises, your functions should always return a Promise that other functions can work with. Leaving an asynchronous action without any way to handle it means bad things, especially when it comes to error handling.
In a more general sense, it means that when you use Promises, more of your code is "uplifted" into being executed and returned as Promises.
How I want to improve on other answers:
keep it clean and simple
no unneeded variables
return promise asap
in js we use camelCase
put it in a function and name that function to keep it readable
let then execute moreCode so it's called after the thing is done.
function doTheThing () {
if (shouldDoA) return doThingA()
else return doThingB()
}
doTheThing().then(moreCode)
Simple working example:
The scope it's defined in must be async.
const createUser = async (data) => {
if (await isUsernameTaken(username)) { return 'username-taken' }
}
The isUsernameTaken func:
const isUsernameTaken = async (username) => {
const request = await API.SomeRequest
return !request.isEmpty
}
Save the promise and add the then after the if statement:
var promise;
if (shoud_do_thing_a) {
promise = do_thing_a();
} else {
promise = do_thing_b();
}
promise.then(more_code);
var promise = shoud_do_thing_a? do_thing_a: do_thing_b
promise().then(function () {
// more code
})
Similar to other answers here, but you can self execute the async and clean up the condition a bit.
(async () => {
const should_do_thing_a = true
const do_thing_a = function() {
return new Promise(function(resolve, reject) {
resolve('a')
})
}
const do_thing_b = function() {
return new Promise(function(resolve, reject) {
resolve('b')
})
}
const result = (should_do_thing_a) ? await do_thing_a() : await do_thing_b()
console.log(result)
})()
The way I would do it would be to put the if check into another function that returns a promise. The promise gets resolved with the resolve of the other function calls in the if-else statement.
Example:
function performCheck(condition) {
var defer = $q.defer();
if (condition) {
doThingA().then(function(response) {
defer.resolve(response);
});
} else {
doThingB().then(function(response) {
defer.resolve(response)
});
}
return defer.promise;
}
performCheck(condition).then(function(response) {
//Do more code.
});
In my opinion, I would prefer this method because this function can now be used in multiple places where you have a check on the condition, reducing code duplication, and it is easier to follow.
You could reduce this down further with
function performCheck(condition) {
var defer = $q.defer();
var doThisThing = condition ? doThingA : doThingB;
doThisThing().then(function (response) {
defer.resolve(response);
});
return defer.promise;
}
performCheck(condition).then(function(response) {
//Do more code.
});
You can use async/await
async function fn() {
let p, result;
if (shoud_do_thing_a) {
p = await do_thing_a()
} else {
p = await do_thing_b()
}
if (p) {
result = more_code();
}
return result
}
more_code = miFunc() => return new Promise((resolve, reject) => ... });
Solution 1
const waitFor = should_do_thing_a ? do_thing_a() : do_thing_b();
waitFor.then(...).catch(...)
Solution 2
let waitFor = Promise.resolve();
if (do_thing_a) {
waitFor = do_thing_a();
} else {
waitFor = do_thing_b();
}
waitFor.then(...).catch(...);
Title says it all, I am using the async-library eachSeries method
let
valueOne = null,
validItem = null;
const asyncEachSeries = require('async/eachSeries');
const getContent = function (item) {
contentResponse = fetchContent(item);
return contentResponse;
}
if (recommendationsArr.length > 0) {
asyncEachSeries(recommendationsArr, function (item, callback) {
getApiContent(item).then(function getValidResult(response) {
try {
valueOne = response.content.valueOne || null; //may or may not be present
recommendationsArr.splice(recommendationsArr.indexOf(item), 1);
validItem = item;
console.log("##### valueOne is: ", valueOne); // I get data here
return true;
} catch (err) {
valueOne = null;
return false;
}
});
//I am guessing I need a return statement here, though not sure what
});
console.log("##### Outside promise, valueOne is: ", valueOne); // no data here
// Access valueOne, validItem, recommendationsArr to be able to pass as props for the component
return {
component: <Layout><ComponentName
//pass other non-dependent props
validItem={validItem}
valueOne={valueOne}
recommendationsArr={recommendationsArr}
/></Layout>,
title: `Page Title`,
};
}
return null;
Scenario: recommendationsArr is an array of items (Its 99.9% not null, but since its an external api call I prefer to have a check). The purpose is to have values for valueOne, validItem, and an updated recommendationsArr.
The validity depends on the existence of valueOne, so if the recommendationsArr[0] has valid valueOne then I don't need to fetch api results for the rest of the array. I am using eachSeries as it runs only a single async operation at a time, hence if that async gets me the valid result I don't have to iterate over other items.
Sample recommendationsArr: recommendationsArr = ["blue", "white", "green", "red"]; //usually the array is anywhere between 12-50 elements
Now in order to be able to pass the updated values to the component I need to be able to access the values set in try block outside of the asyncEachSeries iteration loop. I understand I will have to return the processed/new values but am not sure at what point and how to return those values.
As is usual with any question concerning asynchronous code you return values by using the callback. But eachSeries does not collect return values. What you want is async.mapSeries:
async.mapSeries(recommendationsArr, function (item, callback) {
getApiContent(item).then(function (response) {
try {
valueOne = response.content.valueOne || null; //may or may not be present
recommendationsArr.splice(recommendationsArr.indexOf(item), 1);
validItem = item;
callback(null, valueOne); // This is how you return a value
// return true; // return does absolutely nothing
} catch (err) {
valueOne = null;
callback(err, valueOne);
// return false;
}
});
},function (err, result) {
// this is where you can process the result
// note that the result is an array
});
However, since you are using promises you can use async-q instead which wraps caolan's async library in promises:
var asyncQ = require('async-q');
asyncQ.mapSeries(recommendationsArr, function (item) {
// Note: the return in the line below is important
return getApiContent(item).then(function (response) {
try {
valueOne = response.content.valueOne || null; //may or may not be present
recommendationsArr.splice(recommendationsArr.indexOf(item), 1);
validItem = item;
return valueOne; // this is how you return a value in a promise
} catch (err) {
return null
}
});
})
.then(function(result) {
// you can process the result here
});
Personally I'd prefer to rewrite the code and process the results at the end because the code is cleaner:
asyncQ.mapSeries(recommendationsArr, getApiContent)
.then(function (result) {
for(var i=0;i<result.length;i++) {
var response = result[i];
// do what needs to be done with each response here
}
});
I a promise in such fashion,
function getMode(){
var deferred = Promise.defer();
checkIf('A')
.then(function(bool){
if(bool){
deferred.resolve('A');
}else{
return checkIf('B');
}
}).then(function(bool){
if(bool){
deferred.resolve('B');
}else{
return checkIf('C');
}
}).then(function(bool){
if(bool){
deferred.resolve('C');
}else{
deferred.reject();
}
});
return deferred.promise;
}
checkIf returns a promise, and yes checkIf cannot be modified.
How do I break out of the chain at the first match? (any way other than explicitly throwing error?)
Any way other than explicitly throwing error?
You may need to throw something, but it does not have to be an error.
Most promise implementations have method catch accepting the first argument as error type (but not all, and not ES6 promise), it would be helpful under this situation:
function BreakSignal() { }
getPromise()
.then(function () {
throw new BreakSignal();
})
.then(function () {
// Something to skip.
})
.catch(BreakSignal, function () { })
.then(function () {
// Continue with other works.
});
I add the ability to break in the recent implementation of my own promise library. And if you were using ThenFail (as you would probably not), you can write something like this:
getPromise()
.then(function () {
Promise.break;
})
.then(function () {
// Something to skip.
})
.enclose()
.then(function () {
// Continue with other works.
});
You can use
return { then: function() {} };
.then(function(bool){
if(bool){
deferred.resolve('A');
return { then: function() {} }; // end/break the chain
}else{
return checkIf('B');
}
})
The return statement returns a "then-able", only that the then method does nothing.
When returned from a function in then(), the then() will try to get the result from the thenable.
The then-able's "then" takes a callback but that will never be called in this case. So the "then()" returns, and the callback for the rest of the chain does not happen.
I think you don't want a chain here. In a synchronous fashion, you'd have written
function getMode(){
if (checkIf('A')) {
return 'A';
} else {
if (checkIf('B')) {
return 'B';
} else {
if (checkIf('C')) {
return 'C';
} else {
throw new Error();
}
}
}
}
and this is how it should be translated to promises:
function getMode(){
checkIf('A').then(function(bool) {
if (bool)
return 'A';
return checkIf('B').then(function(bool) {
if (bool)
return 'B';
return checkIf('C').then(function(bool) {
if (bool)
return 'C';
throw new Error();
});
});
});
}
There is no if else-flattening in promises.
I would just use coroutines/spawns, this leads to much simpler code:
function* getMode(){
if(yield checkIf('A'))
return 'A';
if(yield checkIf('B'))
return 'B';
if(yield checkIf('C'))
return 'C';
throw undefined; // don't actually throw or reject with non `Error`s in production
}
If you don't have generators then there's always traceur or 6to5.
You could create a firstSucceeding function that would either return the value of the first succeeded operation or throw a NonSucceedingError.
I've used ES6 promises, but you can adapt the algorithm to support the promise interface of your choice.
function checkIf(val) {
console.log('checkIf called with', val);
return new Promise(function (resolve, reject) {
setTimeout(resolve.bind(null, [val, val === 'B']), 0);
});
}
var firstSucceeding = (function () {
return function (alternatives, succeeded) {
var failedPromise = Promise.reject(NoneSucceededError());
return (alternatives || []).reduce(function (promise, alternative) {
return promise.then(function (result) {
if (succeeded(result)) return result;
else return alternative();
}, alternative);
}, failedPromise).then(function (result) {
if (!succeeded(result)) throw NoneSucceededError();
return result;
});
}
function NoneSucceededError() {
var error = new Error('None succeeded');
error.name = 'NoneSucceededError';
return error;
}
})();
function getMode() {
return firstSucceeding([
checkIf.bind(null, 'A'),
checkIf.bind(null, 'B'),
checkIf.bind(null, 'C')
], function (result) {
return result[1] === true;
});
}
getMode().then(function (result) {
console.log('res', result);
}, function (err) { console.log('err', err); });
i like a lot of the answers posted so far that mitigate what the q readme calls the "pyramid of doom". for the sake of discussion, i'll add the pattern that i plunked out before searching around to see what other people are doing. i wrote a function like
var null_wrap = function (fn) {
return function () {
var i;
for (i = 0; i < arguments.length; i += 1) {
if (arguments[i] === null) {
return null;
}
}
return fn.apply(null, arguments);
};
};
and i did something totally analogous to #vilicvane's answer, except rather than throw new BreakSignal(), i'd written return null, and wrapped all subsequent .then callbacks in null_wrap like
then(null_wrap(function (res) { /* do things */ }))
i think this is a good answer b/c it avoids lots of indentation and b/c the OP specifically asked for a solution that doesn't throw. that said, i may go back and use something more like what #vilicvane did b/c some library's promises might return null to indicate something other than "break the chain", and that could be confusing.
this is more a call for more comments/answers than a "this is definitely the way to do it" answer.
Probably coming late the party here, but I recently posted an answer using generators and the co library that would answer this question (see solution 2):
https://stackoverflow.com/a/43166487/1337392
The code would be something like:
const requestHandler = function*() {
const survey = yield Survey.findOne({
_id: "bananasId"
});
if (survey !== null) {
console.log("use HTTP PUT instead!");
return;
}
try {
//saving empty object for demonstration purposes
yield(new Survey({}).save());
console.log("Saved Successfully !");
return;
}
catch (error) {
console.log(`Failed to save with error: ${error}`);
return;
}
};
co(requestHandler)
.then(() => {
console.log("finished!");
})
.catch(console.log);
You would pretty much write synchronous code that would be in reality asynchronous !
Hope it helps!
Try to use libs like thisone:
https://www.npmjs.com/package/promise-chain-break
db.getData()
.then(pb((data) => {
if (!data.someCheck()) {
tellSomeone();
// All other '.then' calls will be skiped
return pb.BREAK;
}
}))
.then(pb(() => {
}))
.then(pb(() => {
}))
.catch((error) => {
console.error(error);
});