I am writing a C++ addon with Node.js and I made an async function, I tried it with a callback and with a Promise. The callback took between 300ms and 1000ms to execute whereas the Promise took between 1800ms and 5000ms for the same work.
The addon is using the Minmax algorithm with the Markov Decision Process to decide a move to play on a game of connect four. The code is the following:
Callback:
socket.on('play', row => {
console.time('play');
play(socket, Game, row);
Game.minimax(AImove => {
play(socket, Game, AImove - 1);
console.timeEnd('play');
});
});
/*
* play: 1170.922ms
* play: 984.421ms
* play: 1324.559ms
* play: 1147.240ms
*/
ASYNC/AWAIT:
const minimax = Game => {
return new Promise(resolve => {
Game.minimax(AImove => {
resolve(AImove);
});
});
};
socket.on('play', async row => {
console.time('play');
play(socket, Game, row);
const AImove = await minimax(Game);
play(socket, Game, AImove - 1);
console.timeEnd('play');
});
/*
* play: 1838.339ms
* play: 3204.243ms
* play: 3245.432ms
* play: 4590.880ms
*/
I played the exact same sequence of moves, is writing ASYNC/AWAIT with a node addon a bad practice? Or did I do something wrong?
Typically when I see a Promise or an async function taking a long time to execute, it's because the function is not returning a value at the end. It will run through the end of the function without hitting a return, and just timeout. Also, nesting Promises is bad practice.
Related
I use Timer.periodic with a duration normally. This works perfectly for my current use case.
Duration fiveSecs = const Duration(seconds: 5);
new Timer.periodic(fiveSecs, checkChange);
void checkChange(Timer timer) async {
//do some network calls
}
In this particular case I make network calls that take no longer than 500ms, and doing them every 5 seconds is enough for whatever depends on what those calls return.
Now I want to check for new values as frequently as 2 seconds, however, based on what needs to be checked a network call could take anywhere from a few hundred milliseconds to even 10 seconds. I could give a large margin and use a frequency of like 20 seconds for the timer, but in this case I would even prefer a 1 second frequency if possible.
So is there a way the timer could wait for a function, and still have a fixed duration like 1 second. So the maximum time it would take would be 1 second + callback runtime? Or is there a better way this could be achieved, I'm open to suggestions. Thank you.
void startTimer() {
_timer = Timer.periodic(Duration(seconds: 1), (Timer t) async {
print("lets wait for 5 seconds");
_timer.cancel();
await Future.delayed(Duration(seconds: 5));
print("Job is done");
print(DateTime.now());
print("Do it again");
startTimer();
});
}
I have encountered the same situation lately,
what I did was (in my case inside a static class) to add a static boolean value and toggle it accoding to my situation needs, later checking it's value inside the Timer.Periodic callback.
Timer.periodic(ConstantValues.socketTimerOperationDelay, (Timer t) async{
if(_udpHandlerCompleted){
_udpHandlerCompleted = false;
if(!_shouldSendBroadcast || _shutDown)
t.cancel();
else
_writeAllToUdpSocket(_udpSocket, data, ConstantValues.udpMulticastGroup, ConstantValues.udpMulticastPort);
_udpHandlerCompleted = true;
}
});
As you can see in my situation, I had to wait for the callback to end before I move to the next one, I believe this is what you're looking for, but in case you need to await for a different method the solution would be similar,
simply toggle the boolean _udpHandlerCompleted (in my case) in the other function.
Edit:
If my soultion helped you, kindly mark it as the accepted answer. Good luck!
At the end checkChange add Future.delayed(const Duration(seconds: 5), checkChange) then call it once instead of running the timer. You can add/check a boolean flag if you need to kill it at any point.
I know Future will run in event queue.But event queue are also running on main isolate, if i do some heavy task (for example, calculate sum from 1 to 1000000) in future, it will block my ui code.
But Future in network operation will not block ui (such as await httpClient.getUrl(uri)).
Why does a network request using future take several seconds without blocking the UI, while computational operations block the UI?
#override
void initState() {
super.initState();
Future((){
var result;
for (var i = 0; i < 1000000; ++i) {
result = 'result is $i';
}
print(result);
});
}
if i do some heavy task using Future in initState(), the ui will be blocked.
Isolates in Dart are single-threaded. An isolate can do only one thing at a time.
Asynchronous functions are basically a form of cooperative multitasking. A function must yield (usually via await) to allow other operations to execute in the isolate.
Your computation doesn't yield, so it must run in its entirety before the UI can resume processing events, and the UI will be unresponsive. If you altered it:
Future(() async {
var result;
for (var i = 0; i < 1000000; ++i) {
result = 'result is $i';
await Future.delayed(Duration.zero);
}
print(result);
});
then you should find that the UI can process events regularly and should have the appearance of remaining responsive. (Note that your computation will take much longer to complete because of the additional extra overhead.)
Let me answer briefly, the network request (HttpClient in dart:io) actually ended up in another isolate.
find _NativeSocket section inside socket_patch.dart file, keep searching down and you will see this statement (the link is likely to point to the wrong line as the SDK is constantly updated in the future):
_EventHandler._sendData(this, eventPort!.sendPort, fullData);
Does it look familiar?
My application run on stm32F4 with FreeRTOS V9.0.0 and port files Source\portable\RVDS\ARM_CM4F (imported via RTE Keil).
The main, call some initialization functions, create the task and then call the vTaskStartScheduler.
The task simply call vTaskDelay(1000) which never return. The system is not is fault. The fault report dosen't show any error or problem.
The code is:
int main(void)
{
init_foo1()
init_foo2()
xTaskCreate(aTask, "name",1280, NULL, 6, NULL);
init_foo3();
vTaskStartScheduler();
}
void aTask()
{
vTaskDelay(1000);
bar();
}
What is wrong?
Thanks all
You need to put infinite loop firstly:
Example usage of vTaskDelay function accordinly to documentation:
void vTaskFunction( void * pvParameters )
{
/* Block for 500ms. */
const TickType_t xDelay = 500 / portTICK_PERIOD_MS;
for( ;; )
{
/* Simply toggle the LED every 500ms, blocking between each toggle. */
vToggleLED();
vTaskDelay( xDelay );
}
}
Also test the priority in xTaskCreate
UBaseType_t uxPriority
I just can't seem to get while loops to work for me inside Unity. No matter how simple, Unity always freezes on me.
function LoadingLevel (level : int) {
yield;
//progressBar.transform.localScale = Vector3(loadingProgress, 0, 0);
async = Application.LoadLevelAsync(1);
while (!async.isDone) {
loadingProgress = parseInt(async.progress * 100);
}
//Application.LoadLevel(level);
}
This is what I'm currently having trouble with: it compiles, but freezes at runtime. What am I doing wrong?
You are failing to understand how async stuff works in Unity.
Your while loop needs to yield. This is because Unity programs are single-threaded, and yield is how you give time to other "coroutines".
while (!async.isDone)
{
loadingProgress = parseInt(async.progress * 100);
yield;
}
Here are the Unity docs on how this works:
http://docs.unity3d.com/Manual/Coroutines.html
Edittted to ad clarity
I am looking to create an HTML5 Video playback that triggers events at specific regularly timed cue-points. For example, I'd like an event to fire every second during video playback that checks the contents of a textbox (i.e. at second1 textbox contained; at second2 textbox contained). The 'tricky' part is that I need it to work across all major platforms/browsers, and that includes IPhones and IPads.
IPhones particularly seem to be a problem in that no matter the player, the setting, the hack I've tried - when a video starts playing, the browser goes to the background and the video is played in a full-screen container (Quicktime?). When the video stops playing and control is back with the browser, I see that the cuepoint events fired, but that's of no use if the textbox is unreachable during video playback!
I am very familiar with FlowPlayer and have already done a bunch of work to ensure it works for playback across most relevant platforms; the cuepoint feature of its API seems to be exactly what we need BUT there's a warning/restriction specific to it:
Be aware that cuepoints are subject to device restrictions regarding the HTML5 video API. On devices which do not support inline video
because they delegate playback to a system component (e.g. QuickTime
on iPhone) the effect of cuepoints is next to none in real world
setups
Has anyone worked with Flowplayer cuepoints OR alternate tech on iPhones/iPads? Obviously, if I can maintain one code base that would be preferrable to having multiple platform-specific versions.
Here is a simple controller for the video element which tracks the timeupdate event fired by the video element to trigger callback functions at the specified timecodes.
It allows you to attach multiple callbacks for the same timecode.
The timeupdate event is fired at different rates on different devices, because of this there is a limitation in that cuepoints can only be attached at integer values so at 5 seconds not 5.5, you can potentially remove this but then there is a risk that the cuepoints will not be triggered
/**
* Function which allows you to register cuepoints to trigger callbacks
* at specific timecodes
* #param {HTMLElement} video_el The video element you want to track
*/
var VideoController = function(video_el){
/**
* The video element this controller is for
* #type {HTMLElement}
*/
this.element = video_el; //
/**
* Object containing all the cuepoints
* #type {Object}
*/
this.cuepoints = {};
/**
* The last processed_timecode so we dont fire events more than once
* #type {Number}
*/
this.processed_timecode = undefined;
/**
* Allows you to trigger a callback at a specific timecode
* #param {Number} timecode The timecode you want to trigger your callback
* #param {Function} callback Your callback function
*/
this.addCuepoint = function(timecode, callback){
//
timecode = Math.floor(timecode);
//
if(this.cuepoints[timecode] === undefined){
this.cuepoints[timecode] = [];
}
this.cuepoints[timecode].push(callback);
return this;
}
/**
* Internal method to track the videos current timecode and to trigger
* the cuepoints when neccesary
* #param {[type]} e A timeupdate event from the video
*/
this.timeupdate = function(e){
var timecode = Math.floor(e.target.currentTime);
// check to see if there is a callback registered for this timecode
if(this.cuepoints.hasOwnProperty(timecode) && this.cuepoints[timecode] !== undefined && this.processed_timecode !== timecode){
//if there is it loops through the array of callbacks and triggers them
for(var i = 0,l=this.cuepoints[timecode].length;i<l;i++){
this.cuepoints[timecode][i]();
}
}
//updates the processed_timecode so we do not fire these callbacks again
this.processed_timecode = timecode;
}.bind(this);
// add addEventListener to the video element to track the video timecode
this.element.addEventListener('timeupdate', this.timeupdate);
return this;
}
var video = document.getElementById('myVideoElement');
var video_controller = new VideoController(video);
video_controller.addCuepoint(2,function(){
console.log('do something at 2 seconds');
});
video_controller.addCuepoint(2,function(){
console.log('do something else at 2 seconds');
});
I've adapted #Irfan 's answer above to use requestAnimationFrame instead of relying on the timeupdate event.
This will allow for a more granular triggering of events on a half second etc, just adjust the .tofixed(1) as appropriate.
This will not likely help you with the iPhones web video restrictions, but for other use cases it should be helpful to anyone else who needs precise triggering or events based on video time.
// Request Animation Frame Shim
// Source: https://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
var VideoController = function(video_el){
this.element = video_el;
this.cuepoints = {};
this.processed_timecode = undefined;
var self = this;
this.addCuepoint = function(timecode, callback){
var timecode = timecode.toFixed(1);
if(this.cuepoints[timecode] === undefined){
this.cuepoints[timecode] = [];
}
this.cuepoints[timecode].push(callback);
return this;
}
this.rafUpdate = function(timestamp){
var timecode = video_el.currentTime.toFixed(1);
if(!video_el.duration){
requestAnimFrame(self.rafUpdate);
}else if( timecode < video_el.duration ){
requestAnimFrame(self.rafUpdate);
}
if( self.cuepoints.hasOwnProperty(timecode) && self.cuepoints[timecode] !== undefined && self.processed_timecode !== timecode ){
for(var i = 0,l=self.cuepoints[timecode].length;i<l;i++){
self.cuepoints[timecode][i]();
}
}
self.processed_timecode = timecode;
}
requestAnimFrame( this.rafUpdate ); // keeps better time than video.timeupdate
return this;
}
var video = document.getElementById('myvideo');
globalVars['video'] = video;
var video_controller = new VideoController(globalVars['video']);
video_controller.addCuepoint(10.2,function(){
endframe.play();
});