Can someone explain how to correctly implement promise in Angular2 and Firebase.
I've read some articles such as this https://www.firebase.com/blog/2016-01-21-keeping-our-promises.html
in my app.component.ts file i have this
export class AppComponent{
players: Player[];
constructor(private _playerService: PlayerService){}
getPlayers(){
this._playerService.getPlayers().then(res => this.players = res);
}
ngOnInit(){
this.getPlayers();
}
}
inside the player.service.ts file I have this
getPlayers() {
this.playersRef.once('value', function (snap){
return snap.val();
});
}
I always get TypeError: this._playerService.getPlayers(...) is undefined
I also tried this as the article on top suggests
getPlayers() {
var data;
this.playersRef.once('value').then( function (snap){
data = snap.val();
});
return data;
}
But then i get this: Error: Query.once failed: Was called with 1 argument. Expects at least 2. in [null]
I'm not sure how the article is working at all with .once('value').then()
Problem occurs because you are trying to using .then over a method which isn't using promise. Basically missed to return promise from getPlayers method, you should return promise from there to perform promise chaining using .then method over it.
Also don't use callback to return value from it(because callback are not capable of returning anything from it), use .then function over .once so that you can extend promise chain & will be able to return out data correctly.
Code
getPlayers() {
//returned promise here
return this.playersRef.once('value').then((snap) => {
return snap.val();
});
}
Related
I’m using meteor with check() and audit-check-arguments package.
When I use a meteor method using async/await and pass a parameter, even though I use check() to validate the function parametrs, the audit package still throws an exception indicating that not all input parameters have been checked. If I remove the async/await implementation, the package does not crib. What am I missing?
Example:
Meteor.methods({
test: async function(param1){
check(param1, String);
...
await ....
}
});
Throws an exception:
=> Client modified -- refreshing
I20200513-10:43:27.978(5.5)? Exception while invoking method 'test' Error: Did not check() all arguments during call to 'test'
I20200513-10:43:27.979(5.5)? at ArgumentChecker.throwUnlessAllArgumentsHaveBeenChecked (packages/check/match.js:515:13)
Whereas this traditional meteor method does not throw any exceptions
Meteor.methods({
test: function(param1){
check(param1, String);
...
}
});
I know for sure that I am passing exactly one parameter.
It looks like audit-argument-checks only works for synchronous functions.
I don't have this issue because we use mdg:validated-method, which uses requires you to specify an argument validator for each method.
It shuts up the argument checker by wrapping the method function with this:
// Silence audit-argument-checks since arguments are always checked when using this package
check(args, Match.Any);
The simplest solution I can think of, is to separate the check from the async function. You could use a wrapper function to do this:
function checkAndRun(check, run) {
return function(...args) {
check.apply(this, args);
return run.apply(this, args);
}
}
Meteor.methods({
'example': checkAndRun(
function(exampleID){
check(exampleID, String);
},
async function(exampleID) {
const result = await doSomethingAsync(exampleID);
SomeDB.update({ _id: exampleID }, { $set: { someKey: result.value } });
return result.status;
}
}
});
or you could even do it inline with an async IIFE:
Meteor.methods({
example(exampleID) {
check(exampleID, String);
return (async () => {
const result = await doSomethingAsync(exampleID);
SomeDB.update({ _id: exampleID }, { $set: { someKey: result.value } });
return result.status;
})()
}
});
Which, come to think of it, is much simpler than the simplest solution I could think of at first 🙃
You just want to separate the sync check from the async method body somehow
In case you're curious, let diving through the source to see where it's called. When the method is called (in ddp-server/livedata-server), we end up here, a sync method call for the first reference of audit-argument-checks:
https://github.com/meteor/meteor/blob/master/packages/ddp-server/livedata_server.js#L1767-L1770
Which takes us into check/Match for another sync call here: https://github.com/meteor/meteor/blob/71f67d9dbac34aba34ceef733b2b51c2bd44b665/packages/check/match.js#L114-L123
Which uses the strange Meteor.EnvironmentVariable construct, which under the hood has another sync call: https://github.com/meteor/meteor/blob/master/packages/meteor/dynamics_nodejs.js#L57
I want to get a single value from Firebase Database within Firebase function. However, the Promise never returns and the chained method never executes. Here is the method that fetches a value from the database
function getFcmToken(username){
return admin.database().ref('tokens/{username}/fcmToken').once('value').then(snap => {
if(snap.exists()){
const token = Object.keys(snap.val());
console.log("FCM Token", token);
return token;
}
return [];
});
}
The above method was supposed to return a token, but I am not sure it is, so the method below does not get executed.
function sendNotification(keyword, username){
return getFcmToken(username).then(token => {
if(token.length > 0){
//Notification Detail
let payload = {
data:{
keyword: keyword
}
};
return admin.messaging().sendToDevice(token, payload);
}
});
}
In the console log, all I see is Promise pending.
How can I update the code above to return a single value, it appears it is returning an array?
Thanks
Your database ref path is wrong. You might wanted to replace username in path, but single quoted won't do that.
Firebase is listening on tokens/{username}/fcmToken, which doesn't exists. Hence on value event will not be triggered and so downline callback will not be executed.
You can use Template literals for building dynamic strings.
Try ref path as
`tokens/${username}/fcmToken`
Code:
function getFcmToken(username){
return admin.database().ref(`tokens/${username}/fcmToken`).once(...)
}
It tells me "promise is not a function". My problem is that with isomorphic fetch you have to put twice then to get your parsed result. What should I do to manage that properly with redux-saga generators ?
import { put, call, takeEvery, takeLatest } from 'redux-saga/effects'
import fetch from 'isomorphic-fetch'
import errorMessages from './../conf/errorMessages'
function *fetchBalances(address) {
try {
var request = fetch('/api/getBalances/rJnZ4YHCUsHvQu7R6mZohevKJDHFzVD6Zr').then(function(response) {
return response.json();
}). then(function(result) {
// finally my parsed result !
return result;
});
const balances = yield call(request)
yield put({ type: 'GET_BALANCES_SUCCEED', balances: balances})
}
catch(error) {
yield put({ type: 'GET_BALANCES_ERROR', error: error })
}
}
export function* watchGetBalances() {
yield takeEvery('GET_BALANCES', fetchBalances);
}
I could put that in a closure but is that the best idea ? =/
var request = function() {
return fetch('/api/getBalances/rJnZ4YHCUsHvQu7R6mZohevKJDHFzVD6Zr').then(function(response) {
return response.json();
}). then(function(result) {
return result;
});
}
The confusion here comes from the fact that the request variable that you're passing to the call effect holds a Promise. What you want, is the call effect to execute that Promise for you.
In other words, you have already manually called the fetch function, instead of passing call a function that will call and return it.
So, yes, wrapping your fetch calls in a, eg. callRequest function could be useful but if you do so, you must be careful to write const response = yield call(callRequest), and not const response = yield call(callRequest()), which would be equivalent to what you wrote.
A few precisions, in case it helps. From the docs, call receives as its first argument:
A Generator function, or normal function which either returns a Promise as result, or any other value.
Let's see how that works.
First a generator function.
const response = yield call(myFunction);
function* myFunction() {
yield delay(1000);
return true;
}
// response === true
Then a function returning some value (not the most useful, but…)
const response = yield call(myFunction);
function myFunction() {
return true;
}
// response === true;
Finally a function returning a Promise
const response = yield call(callRequest);
function callRequest() {
return fetch(…).then( r => r.json() )
}
// response holds your data
// if the Promise were to be rejected, you could catch
// the error in a try-catch block
I am saving some data into my Firebase database inside my Polymer element. All works fine. But as I person who's new to Promises I need help to understand what Promise.resolved() means here at then end of the method. Isn't the promise going through before that when .then is used? So what exactly this is doing? I looked around but can't find an example of resolved() with no value.
And how can I change this to have more familiar structure as below:
.then(function(snapshot) {
// The Promise was "fulfilled" (it succeeded).
}, function(error) {
// The Promise was rejected.
});
Here's the block and the Promise.resolved() taken from the documentation:
saveData : function() {
this.$.document.data = this.$.message.value;
this.$.document.save("/parent", "child").then(function() {
console.log('sent the event!!!!!!');
this.$.document.reset();
}.bind(this));
return Promise.resolve();
},
First you need to understand the basics of Promises.
Lets start from very basics -
A newly created es6 promise is in one of the following states:
resolved
rejected
pending --> waiting to either resolved or rejected
Lets create a sample Promise
var promise = new Promise(function(fulfill, reject) {
// Do some stuff and either fullfill or reject the promise
});
So above promise receives a callback function also called executor function with signature function(fullfill, reject).
A newly created promise also has a very important property function called then used for chaining and controlling the logic flows.
then takes two optional callback parameters onFulfilled and onRejected.
Inside this executor function two things happens to indicate the outcome of promise -
fullfill method gets called with or without a value:
means operation completed successfully. If you call fulfill with a value then onFulfilled callback in then will receive that value, if you decided not to provide a value in fulfill call then onFulfilled will be called with a parameter undefined.
var promise = new Promise(function(fulfill, reject) {
// lets assume operation completed successfully
fulfill('Success');
});
promise.then(onFulfilled, onRejected);
function onFulfilled(result) {
console.log(result);
// Success will be printed
}
reject method gets called with or without a value:
Some problem occurred while performing the operation. You can decided whether pass some error message reject callback to indicate the error occurred to end user.
var promise = new Promise(function(fulfill, reject) {
// lets assume operation did not complete successfully
reject(new Error('Error'));
});
promise.then(onFulfilled, onRejected);
function onRejected(error) {
console.log(error.message);
// Error message will be printed
}
Now lets talk about Promise.resolve.
At the top you learned how to create promise through the constructor.
var promise = new Promise(function (fulfill, reject) {
fulfill('Success value');
});
// Now: Promise.resolve
// Exactly does the same thing as above code
var promise = Promise.resolve('Success value');
Similarly comes Promise.reject -
var promise = new Promise(function (fulfill, reject) {
reject(new Error('Error VALUE'));
});
var promise = Promise.reject(new Error('Error VALUE'));
In your case save seems to be returning a promise already and internally that promise may be calling either fulfill or reject method so you don't need to call Promise.resolve(). You just need to get the values returned by that promise either fulfilled value or rejected value in the then method.
saveData : function() {
this.$.document.data = this.$.message.value;
// return this promise
return this.$.document.save("/parent", "child");
}
saveData()
.then(function() {
console.log('sent the event!!!!!!');
this.$.document.reset();
}.bind(this));
I hope it makes things about promises somewhat clearer.
If you're trying to be able to do obj.saveData().then(...), then you can return the inner promise like this:
saveData : function() {
this.$.document.data = this.$.message.value;
// return this promise
return this.$.document.save("/parent", "child").then(function() {
console.log('sent the event!!!!!!');
this.$.document.reset();
}.bind(this));
}
I'm new to using redux, and I'm trying to set up redux-promise as middleware. I have this case I can't seem to get to work (things work for me when I'm just trying to do one async call without chaining)
Say I have two API calls:
1) getItem(someId) -> {attr1: something, attr2: something, tagIds: [...]}
2) getTags() -> [{someTagObject1}, {someTagObject2}]
I need to call the first one, and get an item, then get all the tags, and then return an object that contains both the item and the tags relating to that item.
Right now, my action creator is like this:
export function fetchTagsForItem(id = null, params = new Map()) {
return {
type: FETCH_ITEM_INFO,
payload: getItem(...) // some axios call
.then(item => getTags() // gets all tags
.then(tags => toItemDetails(tags.data, item.data)))
}
}
I have a console.log in toItemDetails, and I can see that when the calls are completed, we eventually get into toItemDetails and result in the right information. However, it looks like we're getting to the reducer before the calls are completed, and I'm just getting an undefined payload from the reducer (and it doesn't try again). The reducer is just trying to return action.payload for this case.
I know the chained calls aren't great, but I'd at least like to see it working. Is this something that can be done with just redux-promise? If not, any examples of how to get this functioning would be greatly appreciated!
I filled in your missing code with placeholder functions and it worked for me - my payload ended up containing a promise which resolved to the return value of toItemDetails. So maybe it's something in the code you haven't included here.
function getItem(id) {
return Promise.resolve({
attr1: 'hello',
data: 'data inside item',
tagIds: [1, 3, 5]
});
}
function getTags(tagIds) {
return Promise.resolve({ data: 'abc' });
}
function toItemDetails(tagData, itemData) {
return { itemDetails: { tagData, itemData } };
}
function fetchTagsForItem(id = null) {
let itemFromAxios;
return {
type: 'FETCH_ITEM_INFO',
payload: getItem(id)
.then(item => {
itemFromAxios = item;
return getTags(item.tagIds);
})
.then(tags => toItemDetails(tags.data, itemFromAxios.data))
};
}
const action = fetchTagsForItem(1);
action.payload.then(result => {
console.log(`result: ${JSON.stringify(result)}`);
});
Output:
result: {"itemDetails":{"tagData":"abc","itemData":"data inside item"}}
In order to access item in the second step, you'll need to store it in a variable that is declared in the function scope of fetchTagsForItem, because the two .thens are essentially siblings: both can access the enclosing scope, but the second call to .then won't have access to vars declared in the first one.
Separation of concerns
The code that creates the action you send to Redux is also making multiple Axios calls and massaging the returned data. This makes it more complicated to read and understand, and will make it harder to do things like handle errors in your Axios calls. I suggest splitting things up. One option:
Put any code that calls Axios in its own function
Set payload to the return value of that function.
Move that function, and all other funcs that call Axios, into a separate file (or set of files). That file becomes your API client.
This would look something like:
// apiclient.js
const BASE_URL = 'https://yourapiserver.com/';
const makeUrl = (relativeUrl) => BASE_URL + relativeUrl;
function getItemById(id) {
return axios.get(makeUrl(GET_ITEM_URL) + id);
}
function fetchTagsForItemWithId(id) {
...
}
// Other client calls and helper funcs here
export default {
fetchTagsForItemWithId
};
Your actions file:
// items-actions.js
import ApiClient from './api-client';
function fetchItemTags(id) {
const itemInfoPromise = ApiClient.fetchTagsForItemWithId(id);
return {
type: 'FETCH_ITEM_INFO',
payload: itemInfoPromise
};
}