I have a function defined by:
static Future<http.Response> checkToken(token) async {
return await http.post("my-url", headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}, body: json.encode({
"token": token
})).timeout(const Duration (seconds:5), onTimeout : () => null);
}
I wonder if this function is identical to this function:
static Future<http.Response> checkToken(token) {
return http.post("my-url", headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}, body: json.encode({
"token": token
})).timeout(const Duration (seconds:5), onTimeout : () => null);
}
In the second definition I removed async / await part, cause on the web I found this statement:
Tip: If a function returns a Future, it’s considered asyncrounous; you do not need to mark the body of this function with async keyword. The async keyword is necessary only if you have used the await keyword in the body of your function.
Are these two functions identical?
It's true that the async keyword is not what makes a function asynchronous. The two versions of your function therefore are both asynchronous, and they're mostly equivalent but are subtly not identical.
The first version (which uses await) waits for a Future to complete and returns a new Future. The second version just returns the original Future directly.
Let's simplify the example:
final someFuture = Future.value(42);
Future<int> returnsFuture() => someFuture;
Future<int> awaitsFuture() async => await someFuture;
void main() {
final same = identical(returnsFuture(), awaitsFuture());
print('Identical Futures: $same'); // Prints: Identical Futures: false
}
In practice this usually shouldn't matter. Two cases where it might are:
Your code is somehow sensitive to specific Future instances.
await always yields, so the extra await could very slightly delay execution.
Since your asynchronously method does not contain the keyword await, then the two methods is indeed identical.
Related
I'm using the Dart Shelf package and I need to log the response it sends.
I've managed to log the request but the response technique is less clear:
final handler = const shelf.Pipeline()
.addMiddleware(corsHeaders())
.addMiddleware(shelf.logRequests(
logger: (message, isError) =>
_logRequest(message, isError: isError)))
.addHandler((req) async {
final res = await Router().call(req);
return res;
});
There two parts to the question.
how do I log the headers.
is it possible to log the body.
I know there is an issue in that the response body can only be read once.
As some of the responses are likely to be large I need to filter the requests for which the body is logged.
The answer is a bit of Dart-fu. You have an anonymous function returning an anonymous function.
var handler = const Pipeline()
.addMiddleware(
(handler) => (request) async {
final response = await handler(request);
print(response.headers);
// you could read the body here, but you'd also need to
// save the content and pipe it into a new response instance
return response;
},
)
.addHandler(syncHandler);
I have small app that execute requests to external service.
final app = Alfred();
app.post('/ctr', (req, res) async { // complex tender request
var data = await req.body;
await complexTendexAPIRequest(data as Map<String, dynamic>);
print('Hello World');
await res.json({'data': 'ok'});
});
Handler code:
complexTendexAPIRequest(Map<String, dynamic> data) async {
print('Request: $data');
try {
final response = await http.post(
Uri.parse(COMPLEX_URL),
headers: {'Content-Type': 'application/json', 'Authorization': 'bearer $ACCESS_TOKEN'},
body: json.encode(data)
);
if(response.statusCode == 200) {
var res = json.decode(response.body);
int latestId = res['id'];
String url = 'https://api.ru/v2/complex/status?id=$latestId';
stdout.write('Waiting for "complete" status from API: ');
Timer.periodic(Duration(seconds: 1), (timer) async {
final response = await http.get(
Uri.parse(url),
headers: {'Content-Type': 'application/json', 'Authorization': 'bearer $ACCESS_TOKEN'}
);
var data = json.decode(response.body);
if(data['status'] == 'completed') {
timer.cancel();
stdout.write('[DONE]');
stdout.write('\nFetching result: ');
String url = "https://api.ru/v2/complex/results?id=$latestId";
final response = await http.get(
Uri.parse(url),
headers: {'Content-Type': 'application/json', 'Authorization': 'bearer $ACCESS_TOKEN'}
);
stdout.write('[DONE]');
var data = prettyJson(json.decode(response.body));
await File('result.json').writeAsString(data.toString());
print("\nCreating dump of result: [DONE]");
}
});
}
else {
print('[ERROR] Wrong status code for complex request. StatusCode: ${response.statusCode}');
}
}
on SocketException catch(e) {
print('No Internet connection: $e');
} on TimeoutException catch(e) {
print('TenderAPI Timeout: $e');
} on Exception catch(e) {
print('Some unknown Exception: $e');
}
}
But output is very strange it's look like it's do not waiting complexTendexAPIRequest completion and go forward:
Waiting for "complete" status from API: Hello World
[DONE]
Fetching result: [DONE]
Creating dump of result: [DONE]
But should be:
Waiting for "complete" status from API: [DONE]
Fetching result: [DONE]
Creating dump of result: [DONE]
Hello World
I suppose that reason can be in Timer.periodic but how to fix it to get expected order and execution of:
print('Hello World');
await res.json({'data': 'ok'});
only after complexTendexAPIRequest completed.
upd: I rewrote code to while loop:
https://gist.github.com/bubnenkoff/fd6b4f0d7aeae7007680e7902fbdc1e9
it's seems that it's ok.
Alfred https://github.com/rknell/alfred
The problem is the Timer.periodic, as others have pointed out.
You do:
Timer.periodic(Duration(seconds: 1), (timer) async {
// do something ...
});
That sets up a timer, then immediately continues execution.
The timer triggers every second, calls the async callback (which returns a future that no-one ever waits for) and which does something which just might take longer than a second.
You can convert this to a normal loop, basically:
while (true) {
// do something ...
if (data['status'] == 'completed') {
// ...
break;
} else {
// You can choose your own delay here, doesn't have
// to be the same one every time.
await Future.delayed(const Duration(seconds: 1));
}
}
If you still want it to be timer driven, with fixed ticks, consider rewriting this as:
await for (var _ in Stream.periodic(const Duration(seconds: 1))) {
// do something ...
// Change `timer.cancel();` to a `break;` at the end of the block.
}
Here you create a stream which fires an event every second. Then you use an await for loop to wait for each steam event. If the thing you do inside the loop is asynchronous (does an await) then you are even ensured that the next stream event is delayed until the loop body is done, so you won't have two fetches running at the same time.
And if the code throws, the error will be caught by the surrounding try/catch, which I assume was intended, rather than being an uncaught error ending up in the Future that no-one listens to.
If you want to retain the Timer.periodic code, you can, but you need to do something extra to synchronize it with the async/await code around it (which only really understands futures and streams, not timers).
For example:
var timerDone = Completer();
Timer.periodic(const Duration(seconds: 1), (timer) async {
try {
// do something ...
// Add `timerDone.complete();` next to `timer.cancel()`.
} catch (e, s) {
timer.cancel();
timerDone.completeError(e, s);
}
});
await timerDone.future;
This code uses a Completer to complete a future and effectively bridge the gap between timers and futures that can be awaited (one of the listed uses of Completer in the documentation).
You may still risk the timer running concurrently if one step takes longer than a second.
Also, you can possibly use the retry package, if it's the same thing you want to retry until it works.
I made a function to post notification to a topic. It works great in normally, then I put it in compute function and hope it can posts notification in the background. But it not works.
Here is my code:
void onSendMessageInBackGround(String message) {
Future.delayed(Duration(milliseconds: 3000)).then((_) async{
Client client = Client();
final requestHeader = {'Authorization': 'key=my_server_key', 'Content-Type': 'application/json'};
var data = json.encode({
'notification': {
'body': 'tester',
'title': '$message',
},
'priority': 'high',
'data': {
'click_action': 'FLUTTER_NOTIFICATION_CLICK',
'dataMessage': 'test',
'time': "${DateTime.now()}",
},
'to': '/topics/uat'
});
await client.post('https://fcm.googleapis.com/fcm/send', headers: requestHeader, body: data);
});
}
call compute:
compute(onSendMessageInBackGround, 'abc');
Note: I has put the onSendMessageInBackGround function at the top level of my app as the library said
Is it missing something? or we can't do that?
You might need to add a return or await
void onSendMessageInBackGround(String message) {
return /* await (with async above) */ Future.delayed(Duration(milliseconds: 3000)).then((_) async{
It could be that the isolate shuts down before the request is made because you're not awaiting the Future
Function called from compute must be static or global.
Either i agree with pskink, compute here is not usefull.
Isolates communicate by passing messages back and forth. These messages can be primitive values, such as null, num, bool, double, or String, or simple objects such as the List in this example.
You might experience errors if you try to pass more complex objects, such as a Future or http.Response between isolates.
Got this from the documentation here
I have a call back function which is getting data from an external API and depends on a data check I have tried for a slot elicitation inside callback but looks like elicitation is not working inside the callback. Please find the code snippet below,
GetCustomerDetails().then(response => {
var serializedcustomerDetails = convert.xml2json(response.data, {
compact: true,
spaces: 2
});
var customerDetails = JSON.parse(serializedcustomerDetails);
let filteredCustomerDetails = _.filter(customerDetails.CustomerInfo.CustomerDetails, function (o) {
return o.CustomerName._text.includes(customerName);
})
if (filteredCustomerDetails.length == 1) {
callback(elicitSlot(outputSessionAttributes, intentRequest.currentIntent.name,
intentRequest.currentIntent.slots, '​CustomerCode', {
contentType: 'PlainText',
content: `Do you mean ${filteredCustomerDetails[0].CustomerName._text} of ${filteredCustomerDetails[0].SpecialityName._text} department?`
}));
return;
}
}).catch(error => {
console.log(`${error}`)
})
This is my first Awnser on stack so please bear with me.
I have come accross the same problem in a recent project and there are a few things that you can check.
How long does the API call take?
If your API call takes a long time it will be worth checking the timeout settings on your Lambda function. AWS Console -> Lambda -> Your Function -> Basic settings -> Timeout.
Does your Lambda function finish before the API call is done?
I fixed this issue by building a node module to handle my business logic, the module has a function called getNextSlot it returns as a Promise. Inside this function I check the incoming event and figure out which slot I need to elicit next, part of my flow is to call an API endpoint that takes around 10 seconds to complete.
I use the request-promise package to make the api call, this node module makes sure that the lambda function keeps running while the call is running.
exports.getData = function (url, data) {
var pr = require("request-promise");
var options = {
method: 'POST',
url: 'api.example',
qs: {},
headers:
{
'Content-Type': 'application/json'
},
body: {
"example": data
},
json: true,
timeout: 60000
};
return pr(options);
}
In my main code I call this function as:
apiModule.getData("test", "data")
.then(function (data) {
//Execute callback
})
.catch(function (error) {
console.log(error);
reject(error);
});
This solved the issue for me anyways.
Thanks,
Here is a problem. When I ran these code:
String responseText = null;
HttpRequest.getString(url).then((resp) {
responseText = resp;
print(responseText);
});
print(responseText);
In console:
{"meta":{"code":200},"data":{"username":"kevin","bio":"CEO \u0026 Co-founder of Instagram","website":"","profile_picture":"http:\/\/images.ak.instagram.com\/profiles\/profile_3_75sq_1325536697.jpg","full_name":"Kevin Systrom","counts":{"media":1349,"followed_by":1110365,"follows":555},"id":"3"}}
null
It running asynchronously. Is there JAVA way with synchronized method? That will be await while request is done?
I found only one tricky way to do it and its funny -- wait for three seconds:
handleTimeout() {
print(responseText);
}
const TIMEOUT = const Duration(seconds: 3);
new Timer(TIMEOUT, handleTimeout);
And of course it works with bugs. So any suggestions?
MattB way work well:
var req = new HttpRequest();
req.onLoad.listen((e) {
responseText = req.responseText;
print(responseText);
});
req.open('GET', url, async: false);
req.send();
First, I'm assuming you're using this as a client-side script and not server-side. Using HttpRequest.getString will strictly return a Future (async method).
If you absolutely must have a synchronous request, you can construct a new HttpRequest object and call the open method passing the named parameter: async: false
var req = new HttpRequest();
req.onLoad.listen((e) => print(req.responseText));
req.open('GET', url, async: false);
req.send();
However it is highly recommended that you use async methods for accessing network resources as a synchronous call like above will cause the script to block and can potentially make it appear as though your page/script has stopped responding on poor network connections.