I need to make a function call which does not return anything (void). Only way to get notified about function completion is sending a callback function.
Now I an using BLoC pattern along with ReDux, When an event is dispatched, I dispatch another action to store of redux, After action is completed it calls the callback function. And now inside callback function, I want to update the state of bloc. Below is my implementation,
if (event is Login) {
yield currentState.copyWith(formProcessing: true);
store.dispatch(authActions.login(
currentState.username,
currentState.password,
(error, data) {
print(error);
print(data);
// I want to yield here.
yield currentState.copyWith(formProcessing: false);
},
));
}
As shown in the above code snippet, Inside the callback function, I want to yield.
Solution
Create a function that returns the future and makes callback function to store dispatch, Heres sample.
if (event is Login) {
yield currentState.copyWith(formProcessing: true);
try {
dynamic result = await loginAction(store, currentState.username, currentState.password);
print(result);
yield currentState.copyWith(formProcessing: false);
} catch (e) {
print(e);
}
}
Future loginAction(store, username, password) {
var completer = new Completer();
store.dispatch(authActions.login(
username,
password,
(error, data) {
if (error != null) {
completer.completeError(error);
} else if (data != null) {
completer.complete(data);
}
},
));
return completer.future;
}
You need to create other event, and dispatch this event in your callback function, then you can do what you want in the function that filter your events.
I don't know what is the purpose of your BLoC, but the name of this event depends on the use case, it can be UpdateForm, UpdateState, LoggedIn,LoggedOut, etc. You will find the most descriptive name for your use case.
Remember that you can also create this event with parameters, for example UpdateForm (bool isLoggedIn), and yield different states according to your conditions.
For the example, the name of this event is OtherEvent.
if (event is Login) {
yield currentState.copyWith(formProcessing: true);
store.dispatch(authActions.login(
currentState.username,
currentState.password,
(error, data) {
print(error);
print(data);
dispatch(OtherEvent());
},
));
} else if (event is OtherEvent) {
// You can yield here what you want
yield currentState.copyWith(formProcessing: false);
}
Related
I am working on a GraphQL query where I am trying to find a unique model. However, nothing ever gets returned because the code kept carrying on before the query was finished, thus attempted to return a Promise when it expected a Model. The code looks as follows...
const findShift = async (date) => {
console.log("In mutation function")
const foundShift = await db.shift.findUnique({
where: {
date: date
}
})
return foundShift
}
const foundShift = findShift(date).then( resolved => {
console.log("printing resolved...")
console.log(resolved)
if (resolved.id != 'undefined'){
console.log({
id: resolved.id,
date: resolved.date,
allDevices: resolved.allDevices
})
return foundShift
}
else{
throw new Error("no shift of that date found!")
}
})
And the console.log statements make the console look as so...
In mutation function
Promise { <pending> }
prisma:info Starting a postgresql pool with 9 connections.
and ultimately the query just returns null. As you see, I tried using then and putting the mutation itself into an entirely different function just to circumvent these asynchronisity issues to no avail. Does anyone see a workaround?
First off, ALL async functions return a promise. The return value in the async function becomes the resolved value of that promise. So, the caller of an async function MUST use .then() or await to get the resolved value from the async function. There is no way to "circumvent" the asynchronicity like you are attempting. You can tame it to make it more usable, but you can't escape it. So, your async function returns a pending promise that will eventually resolve to whatever value you return inside your async function.
You can read more about how async functions work here in this other answer.
In trying to make a minimal, reproducible example of your code, I've reduced it to this where I've substituted an asynchronous simulation for the database call:
function delay(t, v) {
return new Promise(resolve => setTimeout(resolve, t, v));
}
// simulate asynchronous database operation
const db = {
shift: {
findUnique: function(data) {
return delay(100, { id: 123, date: Date.now(), allDevices: ["iPhone", "Galaxy", "Razr"] });
}
}
}
const findShift = async (date) => {
console.log("In mutation function")
const found = await db.shift.findUnique({
where: {
date: date
}
})
return found;
}
const date = Date.now();
const foundShift = findShift(date).then(resolved => {
console.log("printing resolved...")
console.log(resolved);
if (resolved.id != 'undefined') {
console.log({
id: resolved.id,
date: resolved.date,
allDevices: resolved.allDevices
})
return foundShift
} else {
throw new Error("no shift of that date found!")
}
});
When I run this in nodejs, I get this error:
[TypeError: Chaining cycle detected for promise #<Promise>]
And, the error is caused by this line of code:
return foundShift
You are attempting to return a promise that's already part of this promise chain from within the promise chain. That creates a circular dependency which is not allowed.
What you need to return there is whatever you want the resolved value of the parent promise to be. Since that looks like it's the object you construct right above it, I've modified the code to do that. This code can be run and foundShift is a promise that resolves to your object.
function delay(t, v) {
return new Promise(resolve => setTimeout(resolve, t, v));
}
// simulate asynchronous database operation
const db = {
shift: {
findUnique: function(data) {
return delay(100, { id: 123, date: Date.now(), allDevices: ["iPhone", "Galaxy", "Razr"] });
}
}
}
const findShift = async (date) => {
const found = await db.shift.findUnique({
where: {
date: date
}
})
return found;
}
const date = Date.now();
const foundShift = findShift(date).then(resolved => {
if (resolved.id != 'undefined') {
let result = {
id: resolved.id,
date: resolved.date,
allDevices: resolved.allDevices
};
return result;
} else {
throw new Error("no shift of that date found!")
}
});
// foundShift here is a promise
// to get it's value, you have to use .then() or await on it
foundShift.then(result => {
console.log("final result", result);
}).catch(e => {
console.log(e);
});
Here are a couple of rule about promises that might help:
All fn().then() or fn().catch() calls return a new promise that is chained to the one that fn() returned.
All async functions return a promise.
You cannot "circumvent" asynchronicity and somehow directly return an asynchronously retrieved value. You will have to use a callback, an event or return a promise (or some similar asynchronous mechanism) in order to communicate back to the caller an asynchronously retrieved value.
await can only be used inside an async function (or at the top level of an ESM module).
The first await in a function suspends execution of the async function and then immediately returns an unfulfilled promise to the caller. So, the await only affects the current function flow, not the caller's flow. The caller will still have to use .then() or await to get the value out of the promise that the async function returns.
Try as you might, there is no way around these rules (in Javascript as it currently runs in a browser or in nodejs).
I have a cloud function that I would like to run whenever a user performs a set of actions on the web app AND daily at a specified time. In the interest of not duplicating code and future features/bug fixes, I'd like to run both from one function/file.
Any suggestions/references on this flow would be greatly appreciated!
You can write your business logic in one function, that you call from the two Cloud Functions. Something along the following lines, with an asynchronous business logic and the use of async/await:
exports.myFunctionCalledFromTheApp = functions.https.onCall(async (data, context) => {
try {
const result = await asyncBusinessLogic();
return { result: result }
} catch (error) {
// ...
}
});
exports.myFunctionCalledByScheduler = functions.pubsub.schedule('every 24 hours').onRun(async (context) => {
try {
await asyncBusinessLogic();
return null;
} catch (error) {
// ...
return null;
}
});
async function asyncBusinessLogic() {
const result = await anAsynchronousJob();
return result;
}
I am very new to Flutter, and stuck at the following problem:
child: RaisedButton(
onPressed: () {
fetchData();
},
// ...
fetchData() async {
final res = await http.get("https://jsonplaceholder.typicode.com/posts");
if (res.statusCode == 200) {
// If the call to the server was successful, parse the JSON
print("it works");
return json.decode(res.body);
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
// ...
when i remove the http.get part it prints "it works" so i think the problem is in the http.get executing.
Make the onPressed async and add an await to fetchData().
Make your method of type Future, and let it return the data type dynamic like this. And then call it wherever you want.
Future<dynamic> fetchData() async {
final res = await http.get("https://jsonplaceholder.typicode.com/posts");
if (res.statusCode == 200) {
// If the call to the server was successful, parse the JSON
print("it works");
return json.decode(res.body);
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
And call it via this, since you are returning the data
child: RaisedButton(
onPressed: () {
// the value is the call back function data which you're returning
fetchData().then((value){
// since the data it return is a dynamic one
print(value.toString());
});
}
)
If you don't want to return any data, and just simply call the method. Then do this
fetchData() async {
final res = await http.get("https://jsonplaceholder.typicode.com/posts");
if (res.statusCode == 200) {
// If the call to the server was successful, parse the JSON
print("it works");
// do not return
print(json.decode(res.body).toString());
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
And simply call like your method call in your code
child: RaisedButton(
// no need of anything now
onPressed: () => fetchData()
)
Solved by running Flutter clean still don't know how
I have a cloud code from which I call an external function.
The cloud code response is null but the console displays the response
my cloud code ;
Parse.Cloud.define("testccadd", async request => {
try {
var ccaddrequest = {
conversationId: '123456789',
email: 'email#email.com',
};
externalFunction (ccaddrequest, function (err, result) {
console.log(result);
return result;
}) ;
} catch (e) {
console.log("Error");
}
});
console.log (result); shows the values from the external function, but the return result; returns null
how can I get the external function response as response of my cloud code function ?
The problem is that your externalFunction uses a callback to return its result. That is an asynchronous event, meaning that it happens after your cloud functions has been processed.
The cloud function will execute var ccaddrequest... and then call externalFunction but it won't "wait" for externalFunction to call the callback function if it contains asynchronous commands.
So you need to wrap the externalFunction in a Promise (see how to promisify callbacks) and then await the result of it.
Plus you need to return the result of the Promise, so in your code you need to add
Parse.Cloud.define("testccadd", async request => {
try {
var ccaddrequest = {
conversationId: '123456789',
email: 'email#email.com',
};
var result = await externalFunctionPromise(...);
return result;
} catch (e) {
console.log("Error");
}
});
I'm writing a small utility function that wrap a call to AngularJS http.get with the necessary authentication headers:
get(endpoint: string): Observable {
var headers = new Headers();
this._appendAuthentificationHeaders( headers, this.user.credentials);
return this.http.get(endpoint, { headers: headers })
.map(res => res.json());
}
The point here is that if this.user is null, the method will just crash.
So I have three options:
Return null and check that return value on every call...
Throw an exception
Find a way to also return an RxJS Observable object that will directly trigger the error handler.
I would like to implement the third method, as it would allow me unify this method's behavior: It always returns an observable no matter what happen.
Do you have an idea about how to do that?
Do I have to create a new Observable and kind of merge those two?
What can I do?
If the user is null, you can simply return a raw observable that triggers an error:
if (this.user == null) {
return Observable.create((observer) => {
observer.error('User is null');
});
}
(...)
or leverage the throw operator:
if (this.user == null) {
return Observable.throw('User is null');
}
(...)
This way the second method of the subscribe method will be called:
observable.subscribe(
(data) => {
(...)
},
(err) => {
// Will be called in this case
}
);
I think the cleanest way would be to wrap the whole function body to an observable, as it will turn any accidental error to an observable error. Something like this:
get(endpoint: string): Observable {
return Rx.Observable.defer(() => {
var headers = new Headers();
this._appendAuthentificationHeaders(headers, this.user.credentials);
return Rx.Observable.just(headers);
})
.flatMap(headers => this.http.get(endpoint, { headers: headers }))
.map(res => res.json());
}
However I still do not agree with http.get returning an observable instead of a promise. As these are single valued observables, your function could be a simple async function (sry, js instead of ts):
async get(endpoint) {
var headers = new Headers();
this._appendAuthentificationHeaders(headers, this.user.credentials);
const res = await this.http.get(endpoint, { headers })).toPromise();
return res.json();
}