How to execute promise of super class in ES6 - ecmascript-6

I've a promise in Parent class, whenever I call the promise from child class, it is returning the undefined, instead of executing the promise and returning the resul.
import {newsApiKey as APIKEY, newUrl as APIURL} from "./secretToken";
class News{
constructor(){
this.token = APIKEY;
this.url = APIURL;
this.source = 'bbc-news&';
}
topNews(){
const bbcNews = fetch(`${this.url}?source=${this.source}&sortBy=top&apiKey=${this.token}`);
bbcNews.then(response => {
if (!response.ok) {
throw Error(response.statusText);
}
return response.json()
})
.then(json => {
console.log(json.articles);
return json.articles;
})
.catch((err) => {
return err.message;
});
}
}
export { News as default};
CHILD CLASS
import News from "./news";
class StickyNote extends News{
displayNews(){
let bbcNews = super.topNews(); // It is returning only undefined
if (typeof bbcNews != 'undefined') {
console.log(bbcNews); //
}
}
}

topNews never returns anything, so the result of calling it is undefined.
You probably wanted a return here:
topNews() {
const bbcNews = fetch(`${this.url}?source=${this.source}&sortBy=top&apiKey=${this.token}`);
return bbcNews.then(response => {
// ^^^^^^
if (!response.ok) {
throw Error(response.statusText);
}
return response.json()
})
.then(json => {
console.log(json.articles);
return json.articles;
})
.catch((err) => {
return err.message;
});
}
Also note that displayNews will need to use the promise it receives:
displayNews(){
super.topNews().then(articles => {
// ...use articles
});
}
(Normally you'd also have a catch there at the endpoint of consumption, but as you've converted rejections into resolutions...)
Note: That code has a bit of an anti-pattern in it: It converts a rejection into a resolution with an error message. Anything using the promise will never see a rejection, only resolutions with varying return types (whatever json.articles is or a string). In general, it's better to allow rejections to propagate, and handle them at the ultimate point of consumption of the entire chain (displayNews, I believe, in your example). You might transform their content, but not convert them from a rejection into a resolution.
FWIW, I'd probably rewrite that like so:
topNews() {
return fetch(`${this.url}?source=${this.source}&sortBy=top&apiKey=${this.token}`)
.catch(_ => {
throw new Error("network error");
})
.then(response => {
if (!response.ok) {
throw new Error(response.statusText);
}
return response.json();
})
.then(data => { // "data", not "json" -- it's not JSON anymore
return data.articles;
});
}
...which ensures that the caller either gets a resolution with the articles, or a rejection with an Error, so:
displayNews(){
super.topNews()
.then(articles => {
// ...use articles
})
.catch(err => {
// ...show error
});
}

Related

cloud function Not all code paths return a value still I am returning all values possible?

any idea why my cloud function is telling my that (Not all code paths return a value) I put a return on the promise at the end also on the if statement yet it is not working, I read about similar issues tried to solve according to them but I still missing part of the puzzle, the error started showing when I wrapped the whole thing with try-catch.
export const createClient = functions.https.onCall(async(data, context) => {
try {
const snapshotCheckBelong = await db.collection('data- main').doc(data.sharedId).get();
const getClient = snapshotCheckBelong.data() ? .clients.filter((el: any) => el.clientId === data.clientId);
const snapshotCheckExist = await db.collection('data-registered-clients').doc(data.sharedId).get();
const checkClient = snapshotCheckExist.data() ? .clients.filter((el: any) => el.clientId === data.clientId);
if (checkClient[0]) {
return {
result: 'Registered client already exisits.'
};
}
if (getClient[0] ? .clientId === data.clientId && checkClient.length === 0) {
const payload = {
user: 'client',
verifiedEmail: false,
createdAt: admin.firestore.Timestamp.now(),
};
let auth = admin
.auth()
.getUserByEmail(context.auth ? .token ? .email!)
.then((user) =>
admin.auth().setCustomUserClaims(user.uid, {
userType: 'client',
premiumUnitll: admin.firestore.Timestamp.fromDate(snapshotCheckBelong.data() ? .premiumUntill)
})
)
.catch((err: any) => {
console.log(err);
});
let setClientData = db
.collection('data-clients')
.doc(context.auth ? .uid!)
.set(payload)
.catch((err: any) => {
console.log(err);
});
let updateRegisteredClients = db
.collection('data-registered-clients')
.doc(data.sharedId)
.update({
clients: admin.firestore.FieldValue.arrayUnion({
clientAccount: context.auth ? .uid,
clientId: data.clientId,
trainerId: data.sharedId
})
})
.catch((err: any) => {
console.log(err);
});
return Promise.all([auth, setClientData, updateRegisteredClients]);
} else {
return null;
}
} catch {
(err: any) => {
console.log(err);
};
}
});
Your catch blocks are not returning anything. If you handle an error with catch, you have to determine what value gets returned to the caller.
I typically prefer sending the error back to the caller, as shown in the documentation on handling errors in callable cloud functions and on the calling client.

setValue in formArray angular 8 and show ui

I want show initial data in my formArray
i can set value and show value in console log but dont show this data in the ui form
ngOnInit() {
this.getCertificate(this.id);
this.assessmentForm = this.fb.group({
certificateArray: this.fb.array([ this.createItem() ]),
});
}
createItem(): FormGroup {
return this.fb.group({
confirm: '',
score: '',
description: ''
});
}
getCertificate(id) {
this.certificateList = [];
this.UsersRegisterService.getCertificate(id).subscribe((res: any[]) => {
this.certificateList = res;
var index=0;
this.certificateList.forEach(element => {
this.AssessmentService.getCertificateAssessment(element.id.value).subscribe((res: any[]) => {
if(res!=null){
this.certificateArray.at(index).setValue(
{ confirm: res['confirm'], score: res['score']['value'],description:res['description']});
console.log( this.assessmentForm['controls'].certificateArray['controls'][index]['controls'].score.value);
}
});
index++;
});
});
}
i set value this method
this.certificateArray.at(index).setValue(
{ confirm: res['confirm'], score: res['score']})
please help me how can i show this value in the Ui form
Use patchValue
this.certificateArray.at(index).patchValue(res);
Note that you would never subscribe inside a subscription, and even less often (never never) subscribe in a forEach. pipe the data instead.
You can refactor your code a little bit, right now you have a shadowed name res. I would log responses from each call to getCertificateAssessment to make sure you're getting what you expect:
getCertificate(id) {
this.certificateList = []; // this should be set at the top of your component
this.UsersRegisterService.getCertificate(id).pipe(
catchError(err => {
console.log('get cert error', err);
return [];
})
).subscribe((list) => {
this.certificateList = list;
this.certificateList.forEach((element, i) => {
this.AssessmentService.getCertificateAssessment(element.id.value).pipe(
catchError(err => {
console.log('get assessment error', err);
return null;
})
).subscribe((res) => {
if (res) {
console.log('res', i, res); // be sure of response
this.certificateArray.at(i).setValue({
confirm: res.confirm,
score: res.score.value,
description: res.description
});
} else {
console.log('no res!');
}
});
});
});
}
Chris makes a good point about piping but I'm guessing these service calls are http requests so they complete just like a promise.
I also added catchError to catch errors with the service calls.
Plus you are having to make a call for each assessment which makes for a lot of calls. Maybe refactor your backend to make it 1 call?
If you refactored the endpoint for getCertficateAssessment to accept an array of values and return an array of responses, you could do this:
this.UsersRegisterService.getCertificate(id).pipe(
switchMap(list => this.AssessmentService.getCertificateAssessment(list.map(l => l.id.value)))
);
You would need to create an id for each assessment so you could assign them.

ES6 JS Promises - how to avoid conditional nesting

I am trying to write a piece of code using promises, avoiding nesting them but I am stuck in testing the returned results to handle the promises flow ..
Is this pattern workable ??
// set of promise tasks returning values
function doTask1() => {
return apiPromise1()
.then((result1) => {
return result1;
})
}
function doTask2(result1, paramOne) => {
return apiPromise2(result1, paramOne)
.then((result2) => {
return result2;
})
}
function doTask3(result1) => {
return apiPromise3()
.then((result3) => {
return result3;
})
}
function doTask4(result1, paramOne) => {
return apiPromise4()
.then((result4) => {
return result4;
})
}
// main promise to handle the flow of promises according to promises returned results
function getCurrentProcess(paramOne) {
const promises = [];
// how to get the returned result1 to be used by other promises ?
promises.push(doTask1);
if (result1 === 'OK') {
promises.push(doTask2(result1, paramOne));
if (result2 === 'OK') {
promises.push(doTask3(result1));
if (result3 === 'OK') {
promises.push(doTask4(result1, paramOne));
}
}
}
return Promisz.all(promises)
.then(() => {
return 'well done'
});
}
// initial calling function
exports.newJob = functions.https.onRequest((req, res) => {
const paramOne = { ... }
getCurrentProcess(paramOne).then((res) => {
return { status: 200, infos: res };
}, error => {
return {status: error.status, infos: error.message};
}).then(response => {
return res.send(response);
}).catch(console.error);
});
If you want to write promises in more procedural way you need use async/await (ES6). If you need backward compatibility with ES5 you need to use babel or typescript which translate await/async to ES5.
async function getCurrentProcess(paramOne) {
const result1 = await doTask1();
if (result1 === 'OK') {
const result2 = await doTask2(result1, paramOne);
if (result2 === 'OK') {
const result3 = await doTask3(result1);
if (result3 === 'OK') {
await doTask4(result1, paramOne);
}
}
}
return 'well done'
}
Without async/await you need to use promise chain:
doTask1().then((result1)=>{
if (result1 === 'OK') {
...
}
...
})
However it will not produce readable code.
You could write a wrapper function which takes an array of doTaskN as deferred functions:
const conditional = (...fns) => {
if(fns.length === 0) return Promise.resolve();
const [next] = fns;
return next()
.then(() => conditional(...fns.slice(1)));
};
The idea would be to pass in the reference to the doTask functions so that the conditional function executes them. This can be used like:
conditional(doTask1, doTask2, doTask3, doTask4)
.then(() => {
console.log("all done");
})
.catch(() => {
console.log("failed");
});
Here's a full example of how to use it:
const conditional = (...fns) => {
if(fns.length === 0) return Promise.resolve();
const [next] = fns;
return next()
.then(result => {
console.log("task:", result);
if(result === "OK") {
return conditional(...fns.slice(1))
}
});
};
const task1 = (param1, param2) => Promise.resolve("OK");
const task2 = (param1) => Promise.resolve("OK");
const task3 = () => Promise.resolve("failed");
const task4 = () => Promise.resolve("OK");
conditional(() => task1("one", 2), () => task2(1), task3, task4)
.then(() => {
console.log("all done");
})
.catch(() => {
console.log("failed");
});
If you want that your promise return result is used by other promises, you shouldn't use Promise.all() method because it doesn't run methods in the order you want, it just waits for all of the promise methods to complete and returns all results.
Maybe something like promise-array-runner would help?
Maybe you could check if result === 'OK' inside your task method? Or create a Factory which takes care of that.
.then((result1) => {
return result1;
})
is a no-op and should be omitted, but I suppose that real code doesn't have this problem.
This is a use case for async function because they can seamlessly handle this sort of control flow, as another answer suggests. But since async is syntactic sugar for raw promises, it can be written in ES6. Since tasks depend on results of each other, they cannot be processed with Promise.all.
This is same case as this one that uses async.
You can bail out from promise chain by throwing an exception and avoid nested conditions with:
// should be additionally handled if the code is transpiled to ES5
class NoResultError extends Error {}
function getCurrentProcess(paramOne) {
doTask1()
.then(result1 => {
if (result1 !== 'OK') throw new NoResultError(1);
return result1;
})
.then(result1 => ({ result1, result2: doTask2(result1, paramOne) }))
.then(({ result1, result2 }) => {
if (result2 !== 'OK') throw new NoResultError(2);
return result1;
})
// etc
.then(() => {
return 'well done';
})
.catch(err => {
if (err instanceof NoResultError) return 'no result';
throw err;
})
}
Since result1 is used in multiple then callbacks, it could be saved to a variable instead of being passed through promise chain.
Promise chain could become simpler if NoResultErrors were thrown in task functions.
Thanks to all feedbacks !!
All answers are rights... however I voted for CodingIntrigue wtapprer function solution in my case...
1 - As i am using Firebase functions , it's still ES5 , I cannot use sync/await. Using babel or typescript only for Firebase functions will result in much more setup work...
2 - I tested various use cases and this pattern is quite easy to understand with JS level... I am sur that it can be improved later..
so finally I got this running ...
let auth = null;
let myList = null;
const conditional = (...fns) => {
if(fns.length === 0) return Promise.resolve();
const [next] = fns;
return next()
.then(result => {
if(result) {
return conditional(...fns.slice(1));
}
return result;
});
};
const task1 = (param1) => Promise.resolve()
.then(() => {
console.log('TASK1 executed with params: ', param1)
auth = "authObject"
return true;
});
const task2 = (param1, param2) => Promise.resolve()
.then(() => {
console.log('TASK2 executed with params: ', param1, param2)
return true;
});
const task3 = (param1, param2) => Promise.resolve()
.then(() => {
console.log('TASK3 executed with params: ', param1, param2)
myList = "myListObject"
console.log('search for param2 in myList...')
console.log('param2 is NOT in myList task4 will not be executed')
return false;
});
const task4 = (param1) => Promise.resolve()
.then(() => {
console.log('TASK4 executed with params: ', param1)
return true;
});
// FIREBASE HTTP FUNCTIONS ==================
exports.newContactMessage = functions.https.onRequest((req, res) => {
conditional(() => task1("senderObject"), () => task2(auth, "senderObject"), () => task3(auth, "senderObject"), () => task4("senderObject"))
.then((res) => {
return { status: 200, infos: res };
}, error => {
return {status: error.status, infos: error.message};
}).then(response => {
return res.send(response);
}).catch(console.error);
});

TextDecoder failing in ES6 Promise recursion

I'm attempting to query an API which responds with a ReadableStream of XML.
The code below uses a recursive Promise. Recursive because it sometimes doesn't decode the stream in a singular iteration and this is whats causing my headache.
While I'm successfully fetching the data, for some reason the decoding stage doesn't complete sometimes, which leads me to believe it's when the stream is too large for a single iteration.
componentDidMount() {
fetch("http://thecatapi.com/api/images/get?format=xml&size=med&results_per_page=9")
.then((response) => {
console.log('fetch complete');
this.untangleCats(response);
})
.catch(error => {
this.state.somethingWrong = true;
console.error(error);
});
}
untangleCats({body}) {
let reader = body.getReader(),
string = "",
read;
reader.read().then(read = (result) => {
if(result.done) {
console.log('untangling complete'); // Sometimes not reaching here
this.herdingCats(string);
return;
}
string += new TextDecoder("utf-8").decode(result.value);
}).then(reader.read().then(read));
}
I think that the next iteration was sometimes being called before the current iteration had completed, leading to incorrectly concatenation of the decoded XML.
I converted the function from sync to async and as a regular recursive method of the component rather than a recursive promise with a method.
constructor({mode}) {
super();
this.state = {
mode,
string: "",
cats: [],
somethingWrong: false
};
}
componentDidMount() {
fetch("http://thecatapi.com/api/images/get?format=xml&size=med&results_per_page=9")
.then( response => this.untangleCats( response.body.getReader() ) )
.catch(error => {
this.setState({somethingWrong: true});
console.error(error);
});
}
async untangleCats(reader) {
const {value, done} = await reader.read();
if (done) {
this.herdingCats();
return;
}
this.setState({
string: this.state.string += new TextDecoder("utf-8").decode(value)
});
return this.untangleCats(reader);
}

Combining api request

I am new to Angular2 and just started to work with Http request and observables.
Previously I worked with .NET and MySql, and I am now trying to learn the best practice working with data from API’s.
I am used to join tables, and I would like to find the best way to combine json data.
In this example I want the user to fill a form and type his phone number.
The prefix of the phone number is a dropdownlist with country code and prefix f.ex. Germany +49
Therefor I need an object: { “Germany”:”49”, Cambodia:"855" ….}
I make 2 http request to country.io:
http://country.io/phone.json // e.g. DE: “Germany”
http://country.io/names.json // e.g. DE: “49”
From these 2 request I used the code below to make my new json object : myPhonePrefixObject
I think the code is too long, and that it must be possible to do it in a better way.
country-service.ts:
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/Rx';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class CountryService {
constructor( private _http:Http) { }
getCountryCode(): Observable<any> {
return this._http.get('http://crossorigin.me/http://country.io/phone.json')
.map(countryCodes => countryCodes.json());
}
getPhonePrefix(): Observable<any> {
return this._http.get('http://crossorigin.me/http://country.io/names.json')
.map(phonePrefix => phonePrefix.json());
}
}
Code inside userform.component in where I import the CountryService
myPhonePrefixObject;
this.countryPhonePrefix()
.then((pp) => {
myPhonePrefixObject = pp;
})
.catch((err) => {
console.log(err);
});
private getCountryCode() {
return new Promise((resolve) => {
this._countryService.getCountryCode()
.subscribe(
res => resolve(res)
);
});
}
private getPhonePrefix() {
return new Promise((resolve, reject) => {
return this._countryService.getPhonePrefix()
.subscribe(
res => resolve(res),
error => reject(error)
);
});
}
private countryPhonePrefix() {
return new Promise((resolve, reject) => {
let cc: Object;
this.getCountryCode()
.then((cCode) => {
cc = cCode;
return this.getPhonePrefix()
})
.then((pPrefix) => {
let pp: Object = {};
Object.keys(cc).forEach((key, index) => {
pp[cc[key]] = pPrefix[key];
});
resolve(pp);
})
.catch((err) => {
reject(err);
});
});
}
.NET developer here too!
To deal with multiple streams, you will need aggregation methods. In this case, you want to yield the object based on the result of 2 streams (HTTP requests), the aggregation method you are looking for is combineLatest. It combines the 2 streams and let you define the output data based on 2 sources:
getCombinedData(): Observable<Data> {
return this.getCountryPhones().combineLatest(this.getCountryNames(),
(phoneData, nameData) => {
var resultData = {};
Object.keys(nameData).forEach((key) => {
resultData[nameData[key]] = phoneData[key];
});
return resultData;
});
}
Plunker: http://plnkr.co/edit/agUPNujG3NnbKI6J3ZVJ?p=preview