void sample_stream_1() {
final myGenerator = NumberCreator();
final myStream = myGenerator.stream;
myStream
.listen(
(data) => print('Data: $data'),
onError: (err) => print('Error!'),
cancelOnError: false,
onDone: () {
myGenerator.finish();
print('Done!');
},
);
}
class NumberCreator {
final _controller = StreamController<int>();
var _count = 1;
NumberCreator() {
Timer.periodic(Duration(seconds: 1), (t) {
this._controller.sink.add(this._count);
this._count++;
if (_count > 10) t.cancel();
});
}
Stream<int> get stream => this._controller.stream;
void finish() => this._controller.close();
}
main(List<String> args) {
sample_stream_1();
print('waiting...');
}
The output is:
[Running] dart "./future_stream_demo.dart"
waiting...
Data: 1
Data: 2
Data: 3
Data: 4
Data: 5
Data: 6
Data: 7
Data: 8
Data: 9
Data: 10
[Done] exited with code=0 in 11.164 seconds
onDone event is not triggered, if I change sample_stream_1() procedure to this:
void sample_stream_1() {
final myGenerator = NumberCreator();
final myStream = myGenerator.stream;
myStream
.take(5)
.listen(
(data) => print('Data: $data'),
onError: (err) => print('Error!'),
cancelOnError: false,
onDone: () {
myGenerator.finish();
print('Done!');
},
);
}
The output is:
[Running] dart "./future_stream_demo.dart"
waiting...
Data: 1
Data: 2
Data: 3
Data: 4
Data: 5
Done!
Unhandled exception:
Bad state: Cannot add event after closing
#0 _StreamController.add (dart:async/stream_controller.dart:623:24)
#1 _StreamSinkWrapper.add (dart:async/stream_controller.dart:900:13)
#2 new NumberCreator.<anonymous closure> (file:./future_stream_demo.dart:275:29)
#3 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:397:19)
#4 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:428:5)
#5 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)
[Done] exited with code=255 in 6.298 seconds
onDone event is triggered but it raises an exception also.
So:
Why does onDone event work for the prior one and not for the latter one?
Why do I get an exception in the latter one?
What's the correct way to implement onDone event?
The onDone callback is called when the Stream is closed. You're never closing the Stream in the first example you provided, so the callback is never called. If you call the closing function at the same time as the timer cancel, it works the way you expected.
Example:
Timer.periodic(Duration(seconds: 1), (t) {
this._controller.sink.add(this._count);
this._count++;
if (_count > 10) {
t.cancel();
finish();
}
});
You get an exception with the second example because you're trying
to add an event to a Stream that you closed already. After the 5
taken elements finishes, your onDone method is called, closing
the Stream from the NumberCreator, but the Timer is still ticking, and tries to add an event to the closed Stream. The confusion may have come from the fact that the take method returns a Stream that closes after it emits the provided number of events, in this case 5.
It's already being done correctly for the most part, you just never close the Stream so it's never expected to be called when you think it should.
Related
I have a timer function on firebase functions.
My code below
exports.timecontroller = functions.region('europe-west1').firestore.document("DigitalTargets/{digitalTargetID}").onCreate((snap, context) => {
const id = snap.id
const date = new Date(snap.data().endDate.toDate())
var countDownDate = date.getTime();
var myfunc = setInterval(async function () {
var now = new Date().getTime();
var timeleft = countDownDate - now;
db.collection('DigitalTargets').doc(snap.id).get().then(a => {
if (!a.data().isClaimed) {
console.log(timeleft)
if (timeleft < 0) {
db.collection("DigitalTargets").doc(id).update({ isActive: false })
clearInterval(myfunc);
}
}
else {
clearInterval(myfunc);
}
})
}, 1000);
return true;
})
My problem is, when i create a doc it starts the count. I can see on log screen. But after 10 min it stops working. no more logs shown. After expire time its not deactivating doc
What i need:
I placing targets on map and they have end time. I just want to set active false after timer finished.
is there any limitation on firebase functions? if theres ill check every 5 minutes with scheduled function.
From the docs:
The maximum value for timeoutSeconds is 540, or 9 minutes.
https://firebase.google.com/docs/functions/manage-functions
As mentioned, you could use a scheduled function:
exports.myCrontabFunction = functions.pubsub.schedule("every 5 minutes")
.timeZone("Europe/Berlin")
.onRun(async (context) => { ... } );
Sounds like you are using an Event Driven Function that starts when you create a doc.
As per this public documentation you can see that Even Driven Functions times out after 10 mins.
The alternative could be to have that event driven function to make an http request to another function with a higer time out of 60 minutes.
I got 2 servers, one for the main app and another one for huge tasks.
User -> Server 1 -> Server 2
Server 1: Main app & Easy tasks
Server 2: Huge Tasks
When I call a server 2's function which takes a long time to answer, server 1 receive undefined when server 2 answer a good result. However, if server2's function takes less than 1 minute to answer, server 1 got the result sent by server 2 and then send it back to the client.
Why it doesn't work only for functions which take more than 1 minute to compute ?
Client :
Meteor.call('reporting.default', params.subReport, params, function(error, result) {
if (result) self.setState({data: result});
else self.setState({data: error.message});
});
Server 1:
Meteor.methods({
'reporting.default'(subReport, params) {
this.unblock();
return Meteor.callWorker('reporting.' + subReport, Meteor.callId(), params).then((result, error) => { if (error) return error; else return result; }).await();
},
});
Meteor.worker = DDP.connect('localhost:' + Meteor.settings.heavyTasksServer.port);
Meteor.callWorker = (method, ...myParameters) => new Promise((resolve, reject) => {
console.log(method + ": REQUEST");
Meteor.worker.call(method, ...myParameters, (err, res) => {
if (err) {
console.log(method + ": ERROR");
reject(err);
}
else {
console.log(method + ": ANSWER");
resolve(res);
}
});
});
Meteor.callId = function () {
const d =new Date();
return d.getUTCFullYear() +""+ (d.getUTCMonth()+1) +""+ d.getUTCDate() +""+ d.getUTCHours() +""+ d.getUTCMinutes() +""+ d.getUTCSeconds() +""+ d.getUTCMilliseconds() + "-" + Meteor.userId();
};
Server 2:
Meteor.methods({
'reporting.clientsAssets'(callId, params) {
this.unblock();
const funcName = "reporting.clientsAssets";
if (canRunQuery(1, callId, arguments, funcName)) {
console.log(funcName + ": START");
const data = reportingClientsAssets(params);
console.log(funcName + ": END");
terminateQuery(callId);
return data;
}
}
});
You could consider an asynchronous model instead of a synchronous one (which is probably timing out).
Let's think about a queuing mechanism... create a collection call jobs (or whatever you prefer), and server 1 creates a record in the job collection with a status of 'ready'.
A timed task (you can use node-cron for this) runs on server 2 say every minute, and looks for jobs with a status of 'ready'. It takes the first one, sets the status to 'running' and then calls the function to do the work.
When that function completes, it sets the status of the task to 'complete'.
You make use of Meteor's reactivity, so that the user can see the status of the job, once it is started, it moves to 'running', and then to 'complete' once it is done. At that point a link may appear so they have access to the data, report or whatever is produced.
No timeout issues with this mechanism, and it's nicely decoupled.
Already spent quite a few hours trying to figure out the following, hopefully someone could point me.
In short what's happening: I have a firebase function that basically updates a database value in a transaction. But if I use transaction, the function always fail with this error:
Unhandled error RangeError: Maximum call stack size exceeded
at Function.isNull
Even though the transaction correctly updates the value in the database.
I was trying to debug it and remove anything I can. So whenever I remove transaction and use update() for example, the function finishes as expected with code 200.
As soon as I put transaction back the function fails with the following stacktrace:
Unhandled error RangeError: Maximum call stack size exceeded
at Function.isNull (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:11950:20)
at encode (/user_code/node_modules/firebase-functions/lib/providers/https.js:217:11)
at /user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:13402:38
at /user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:4911:15
at baseForOwn (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:2996:24)
at Function.mapValues (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:13401:7)
at encode (/user_code/node_modules/firebase-functions/lib/providers/https.js:242:18)
at /user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:13402:38
at /user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:4911:15
at baseForOwn (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:2996:24)
at Function.mapValues (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:13401:7)
Another similar stacktrace I've got:
Unhandled error RangeError: Maximum call stack size exceeded
at /user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:13402:38
at /user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:4911:15
at baseForOwn (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:2996:24)
at Function.mapValues (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:13401:7)
at encode (/user_code/node_modules/firebase-functions/lib/providers/https.js:242:18)
at /user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:13402:38
at /user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:4911:15
at baseForOwn (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:2996:24)
at Function.mapValues (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:13401:7)
at encode (/user_code/node_modules/firebase-functions/lib/providers/https.js:242:18)
Here is the function:
index.js:
const statsModule = require("./stats")
exports.myMethod9 = functions.https.onCall((data, context) => {
console.log("updateStatsMessagesChangedFeedbackHttp: data = ", data)
return statsModule.updateStatsMessagesChangedFeedbackHttp(data, context, admin);
});
stats.js
exports.updateStatsMessagesChangedFeedbackHttp = function (data, context, admin) {
var userId = "sdfsdf";
var feedback = data.feedback;
console.log("updateStatsMessagesChangedFeedbackHttp: userId = ", userId)
return exports.updateStatsMessagesChangedFeedback(userId, feedback, admin);
}
exports.updateStatsMessagesChangedFeedback = function (userId, feedback, admin) {
console.log("updateStatsMessagesChangedFeedback: feedback = ", feedback, ", userId = ", userId);
var root = admin.database().ref().root;
var path = constModule.statsUpdatedMessagesFeedbackPath + "/" + feedback;
var statsRef = root.child(path);
return statsRef.transaction(function (stats) {
if(!stats) {
stats = {votedUsers:{}, count: 0};
}
stats.votedUsers[userId] = true;
console.log("updateStatsMessagesChangedFeedback: stats = ", stats)
return stats;
}, function (error, committed, snapshot) {
//nothing wrong here - error is always null, committed - true
var snapVal = snapshot ? snapshot.val() : null;
console.log("error = ", error, ", committed = ", committed, ", data = ", snapVal);
if (error) {
// The fetch succeeded, but the update failed.
console.error("updateStatsMessagesChangedFeedback: The fetch succeeded, but the update failed: ", error);
} else {
console.log("updateStatsMessagesChangedFeedback: all ok: data = ", snapVal);
}
})
.catch(function (error) {
//this is never called - all good here as well
console.error("updateStatsMessagesChangedFeedback: error = ", error)
});
}
I call it from client web sdk like this:
var call = firebase.functions().httpsCallable('myMethod9');dFeedbackHttp
call({feedback: "some data"}).then(function (result) {...})
Neither catch, nor the transaction callback shows any errors. Though the function still fails only if I use transaction.
(click the image to enlarge)
Any ides on what's going on?
After discussion with support (very helpful guys by the way!) here is the solution:
one need to return a value from function in case of transaction:
exports.myMethod9 = functions.https.onCall((data, context) => {
console.log("updateStatsMessagesChangedFeedbackHttp: data = ", data)
return statsModule.updateStatsMessagesChangedFeedbackHttp(data, context, admin);
}).then(function(){
return {}
});
I don't have any clue on why returning a value makes any difference here as for example update() returns void which still makes the function complete with code 200.
Waiting on further comments form the support and meanwhile if anyone has any info on this feel free to share.
So I've been working on experimenting with DART (whereby my core languages are C++, and embedded C derivates). Hence my code is probably not pretty as I'm more of a procedural programmer, but I'm getting by and learning... I've been struggling around Futures pertaining to await sync, and basically, I simply can't get DART to WAIT. The following code establishes a socket connection to a small embedded device and extracts info. That all works, but notice the order of operations SHOULD be main() gets some info from the console, then should call the method cardStatus to run off and get the info from the embedded device via the socket connection. This is where the await should occur. When the Future is returned, it should go off to the printstuff() method. I've added print statements that should go in order and read:
This should print 1st
This should print 2nd
This should print 3rd
Instead since the wait is not occurring on the cardstatus call (which is time consuming), I get:
This should print 1st
This should print 3rd
This should print 2nd
I've followed another thread on the use of async, and seem to be at least following one solid way of using this Other thread (I tried a .then with a completer with a similar result, so there is something core I feel I'm missing).. but I've been stuck on this for a week.
Code below, along with the console output.
import 'dart:io';
import 'dart:async' show Future;
const String STATUS = "#111111;";
String defaultIP = "10.1.14.202";
int defaultConfigPort = 5111;
int defaultControlPort = 6722;
var card = new Map();
getInput(String defaults) {
String takenin = stdin.readLineSync();
if (takenin == '') takenin = defaults;
return takenin;
}
Future main() async {
stdout.write('What is the IP address of the card ($defaultIP): ');
String ipaddress = getInput(defaultIP);
defaultIP = ipaddress;
print ("This should print 1st");
stdout.writeln("Connecting to $defaultIP");
await cardStatus(defaultIP, defaultConfigPort, STATUS, card);
printstuff();
}
printstuff() {
stdout.writeln(card['subnet']);
print ("This should print 3rd");
}
Future cardStatus(String ip, int port, String message, Map card) {
return new Future.delayed(Duration.ZERO, () {
Socket.connect(ip, port).then((socket) {
print('Connected to: '
'${socket.remoteAddress.address}:${socket.remotePort}');
socket.listen((data) {
print(new String.fromCharCodes(data).trim());
List str1 = (new String.fromCharCodes(data).trim().split(','));
print(str1);
print ("This should print 2nd");
//var card = new Map();
card['ip'] = str1[0];
card['subnet'] = str1[1];
card['gateway'] = str1[2];
card['unknown'] = str1[3];
card['persist'] = str1[4] == 'true';
card['build'] = str1[5];
card['serial'] = str1[6].substring(0, 14);
card['cloudpassword'] = str1[6].substring(14, 20);
card['DNS'] = str1[7];
card['cloudhost'] = str1[8];
card['cloudenabled'] = str1[9] == 'true';
print(card['ip']);
},
onDone: () {
print("Done");
socket.destroy();
});
//Send the request
socket.write(message);
});
});
}
and this is the current console output. notice the null shouldn't be a null if the cardStatus would have completed it would be printed str1.
What is the IP address of the card (10.1.14.202):
This should print 1st
Connecting to 10.1.14.202
null
This should print 3rd
Connected to: 10.1.14.202:5111
>10.1.14.202,255.255.255.0,10.1.14.1,,0,435,F44900A60040F8000000,192.168.1.1,connect.tutuuu.com,0;
[>10.1.14.202, 255.255.255.0, 10.1.14.1, , 0, 435, F44900A60040F8000000, 192.168.1.1, connect.tutuuu.com, 0;]
This should print 2nd
10.1.14.202
Done
Process finished with exit code 0
Thanks for all the help!
You are missing return before Socket.connect. As it stands now your code just starts connecting but never awaits it through future. I would highly recommend using as much as possible the new await / async syntax.
Here is a running example that does get google homepage:
import 'dart:io';
import 'dart:async' show Future;
Future main() async {
print("This should print 1st");
await cardStatus('www.google.com', 80, 'GET /\nHTTP 1.1\n\n');
printstuff();
}
printstuff() {
print("This should print 3rd");
}
Future cardStatus(String ip, int port, String message) {
return new Future.delayed(Duration.ZERO, () {
return Socket.connect(ip, port).then((socket) {
print('Connected to: '
'${socket.remoteAddress.address}:${socket.remotePort}');
socket.listen((data) {
List str1 = (new String.fromCharCodes(data).trim().split(','));
print(str1.first);
print("This should print 2nd");
}, onDone: () {
print("Done");
socket.destroy();
}, onError: (e) {
print("Error while listening: $e");
});
socket.write(message);
});
});
}
Below slightly redacted version that uses awaits, and try / catch to handle errors:
import 'dart:io';
import 'dart:async' show Future;
Future main() async {
print("This should print 1st");
await cardStatus('www.google.com', 80, 'GET /\nHTTP 1.1\n\n');
print("This should print 3rd");
}
Future<String> cardStatus(String ip, int port, String message) async {
var socket = await Socket.connect(ip, port);
print('Connected to: '
'${socket.remoteAddress.address}:${socket.remotePort}');
socket.write(message);
print("Sent request");
try {
var response = await socket.fold(
'',
(String acc, List<int> data) =>
acc + new String.fromCharCodes(data).trim());
print("Received response: ${response.substring(0, 10)}");
return response;
} finally {
socket.close();
}
}
I know it was answered but the question is great and I struggled myself with the concept so here is another element that got me to understand. in the dartpad (https://dartpad.dartlang.org/) try this (comments are in the code):
import 'dart:async';
//Just creating a duration to use later
Duration duration = new Duration(milliseconds: 500);
void main() {
//This is what tricked me, printStuff is async so running in parallel processing by default
//There is no need to call the function in a certain way (like a go xxx for a goroutine)
//there is an await in the function so it will wait inside the function only
//i.e. printStuff('a') starts then printStuff('b') starts straight away...already in prallel processing
//Run it and check the output
printStuff('a');
printStuff('b');
//Basically the await is in the function so printStuff is still returning a Future
//i.e. printStuff('a') starts but doesn't wait to complete to start printStuff('b')
}
Future<void> printStuff(String id) async {
for(int i = 0; i <= 5; ++i) {
//this await is waiting for the command to complete to move to the next iteration...
//the i iterations are done one after the other
await new Future.delayed(duration, () {
print(id + i.toString());
});
}
}
Then try this:
import 'dart:async';
Duration duration = new Duration(milliseconds: 500);
//becuase I use await in main now, I must make it return a future and be async
Future main() async {
//to make it happen one after the other, you need await at a call function level
await printStuff('a');
await printStuff('b');
//Basically this says complete printStuff('a'), then start printStuff('b')...
//and yes technically one doesn't need the second await becuase there is nothing after
}
Future<void> printStuff(String id) async {
for(int i = 0; i <= 5; ++i) {
await new Future.delayed(duration, () {
print(id + i.toString());
});
}
}
So my personal misunderstanding was that an async function is called in parallel straight away and an await in a function waits for real but in the function itself, with no impact on other parallel processing happening.
I am fairly new to Angular 2, TypeScript and RxJS and I am creating a simple application that leverages the Salesforce Ajax Toolkit connections library.
I am trying to write a handler to catch when a token has expired any time a method from the connections library is called. I have created a service that essentially wraps the connections library to use observables. For example if we look at the insert function I have created my own wrapper function:
public insert(object: sforce.SObject): Observable<any> {
return new Observable(observer => {
// successfully inserted the record
let insertSuccess = (result) => {
observer.next(result);
observer.complete();
}
// An error occured inserting the record
let insertError = (result) => {
// This does not work yet
if (result.faultcode.indexOf('INVALID_SESSION_ID') != -1) {
this.refreshToken();
}
else {
observer.error(result);
}
}
let callback = { onSuccess: insertSuccess, onFailure: insertError };
sforce.connection.create([object], callback);
});
}
I have another function that refreshes the access token:
public refreshToken(): void {
this.loginService.login().subscribe(
response => {
Globals.SESSION_TOKEN = response.access_token;
//initialize the salesforce connection
this.init(Globals.SESSION_TOKEN, this.loginService.AuthParams.SOAP_URL);
},
error => {
}
);
}
I essentially want the original insert function to wait for refreshToken to complete. If it is successful I want to retry the same insert again, otherwise I want the original insert observable to call observer.error.
I've looked into retry and retryWhen, however I haven't been able to figure out how to implement it to wait for the refreshToken() function to complete. Any guidance or advice on this matter would be greatly appreciated. Thank you in advance.
The catch operator accepts a function which processes an error and the source Observable. This means that if you catch an error you can determine whether you want to resubscribe to the original source in the catch block:
public insert(object: sforce.SObject): Observable<any> {
return new Observable(observer => {
// successfully inserted the record
let insertSuccess = (result) => {
observer.next(result);
observer.complete();
}
// An error occured inserting the record
let insertError = (result) => observer.error(result);
let callback = { onSuccess: insertSuccess, onFailure: insertError };
sforce.connection.create([object], callback);
}).catch((err, source) => {
if (err.faultcode.indexOf('INVALID_SESSION_ID') != -1) {
//This waits for the refresh to complete and then resubscribes
//to the source
//If the refresh errors then it will skip the resubscribe
return this.refreshToken().flatMapTo(source);
}
//Non-authentication error
return Observable.throw(err);
});
}
Then make your refreshToken function into something like so:
public refreshToken(): Observable<any> {
return this.loginService.login()
.tap(response => {
Globals.SESSION_TOKEN = response.access_token;
//initialize the salesforce connection
this.init(Globals.SESSION_TOKEN, this.loginService.AuthParams.SOAP_URL);
});
}