So I am trying to add an async progress bar on a really slow and long query that inserts a bunch of rows to a database. My implementation is based off this example: http://blog.janjonas.net/2012-01-02/asp_net-mvc_3-async-jquery-progress-indicator-long-running-tasks
Here is the javascript code for the progress bar
function updateMonitor(taskId, status) {
$("#" + taskId).html("Task [" + taskId + "]: " + status);
}
//other code
if (doSend == true) {
$.post("/SendBatch/HandleBatchRequest", {
//other code
},
function (taskId) {
// Init monitors
//breakpoint here does not stop it, it never enters this somehow?
$("#monitors").append($("<p id='" + taskId + "'/>"));
updateMonitor(taskId, "Started");
// Periodically update monitors
var intervalId = setInterval(function () {
$.post("SendBatch/Progress", { id: taskId }, function (progress) {
if (progress >= 100) {
updateMonitor(taskId, "Completed");
clearInterval(intervalId);
} else {
updateMonitor(taskId, progress + "%");
}
});
}, 100);
}
,"html");
Then there is the DIV within the display part of the website
<div id="monitors"></div>
Here is how the controller looks
public SendBatchController
//some code
private static IDictionary<Guid, int> tasks = new Dictionary<Guid, int>();
public ActionResult HandleBatchRequest(
//some code
)
{
var taskId = Guid.NewGuid();
tasks.Add(taskId, 0);
var batchId = Guid.NewGuid().ToString("N");
var costd = cost.ToDecimal();
IEnumerable<BatchListModel> customers;
try
{
customers = new CustomerService(_customerRepository.Session).GetCustomers(
//some code
);
}
catch (Exception err)
{
return Json(err.Message);
}
if (doSend)
{
var sent = 0;
foreach (var c in customers)
{
try
{
var usr = _customerRepository.LoadByID(c.ID);
var message = new ComLog
{
//insertions to log
};
_comLogRepository.Save(message);
sent++;
//progress bar part inside here that is important comes here:
tasks[taskId] = sent;
}
catch (Exception e)
{
Log.WriteLine("ERR:" + e);
}
tasks.Remove(taskId);
}
return Json(taskId);
}
return Json(customers.Count() + " customers");
}
public ActionResult Progress(Guid id)
{
return Json(tasks.Keys.Contains(id) ? tasks[id] : 100);
}
This does not work. The process works in the background. It is only the div that never shows up and never gives any indication. I know this is a lot of code to read but I am really stuck and would love some input on how to fix this.
try change your updateMonitor function into this :
function updateMonitor(taskId, status) {
$("#monitors").html("Task [" + taskId + "]: " + status);
}
Related
I am new to Unity and C# and trying to query my Firebase Realtime Database, but the code does not block for the callback to finish.
I have tried implementing callbacks but this does not seem to work.
static public void ReadFromDb(int level, Action<int> callback)
{
int return_value = -1;
string sessionId = PlayerPrefs.GetString("SessionID");
FirebaseDatabase.DefaultInstance.GetReference("users/"+sessionId).GetValueAsync().ContinueWith(task => {
if (task.IsFaulted)
{
// Handle the error...
Debug.Log("Task faulted");
callback(return_value);
}
else if (task.IsCompleted)
{
DataSnapshot snapshot = task.Result;
string score_string = (snapshot.Child(level.ToString()).Value).ToString();
Debug.Log("score_string " + score_string);
return_value = int.Parse(score_string);
callback(return_value);
}
});
}
public void LevelComplete()
{
DatabaseCode.writeDatabase(SceneManager.GetActiveScene().buildIndex + 1, counter);
DatabaseCode.ReadFromDb(SceneManager.GetActiveScene().buildIndex + 1, (result) => {
prevlevelscore = result;
Debug.Log("result " + result.ToString());
});
prevscore = prevlevelscore;
Debug.Log("Returned value: " + prevlevelscore.ToString());
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
In LevelComplete(), Debug.Log("Returned value: " + prevlevelscore.ToString()); executes before prevlevelscore = result;
I want to make sure that the value of prevlevelscore is updated before executing Debug.Log.
Put the rest of the code inside the callback too:
public void LevelComplete()
{
DatabaseCode.writeDatabase(SceneManager.GetActiveScene().buildIndex + 1, counter);
DatabaseCode.ReadFromDb(SceneManager.GetActiveScene().buildIndex + 1, (result) => {
Debug.Log("result " + result.ToString());
prevscore = result;
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
});
}
Your problem is that your ReadFromDb method returns before being finished. You can solve this issue by putting all your code in the callback (but you won't be able to do that all the time) or you can use the async await pattern.
Make ReadFromDb async:
static public async Task ReadFromDb(int level, Action<int> callback)
{
int return_value = -1;
string sessionId = PlayerPrefs.GetString("SessionID");
await FirebaseDatabase.DefaultInstance.GetReference("users/"+sessionId).GetValueAsync().ContinueWith(task => {
if (task.IsFaulted)
{
// Handle the error...
Debug.Log("Task faulted");
callback(return_value);
}
else if (task.IsCompleted)
{
DataSnapshot snapshot = task.Result;
string score_string = (snapshot.Child(level.ToString()).Value).ToString();
Debug.Log("score_string " + score_string);
return_value = int.Parse(score_string);
callback(return_value);
}
});
}
Note the await keywork before your GetValueAsync().ContinueWith because this precise code is asynchronous and needs to be awaited if you want to hold code execution until the result has been fetched.
And in your caller:
public async Task LevelComplete()
{
DatabaseCode.writeDatabase(SceneManager.GetActiveScene().buildIndex + 1, counter);
await DatabaseCode.ReadFromDb(SceneManager.GetActiveScene().buildIndex + 1, (result) => {
prevlevelscore = result;
Debug.Log("result " + result.ToString());
});
prevscore = prevlevelscore;
Debug.Log("Returned value: " + prevlevelscore.ToString());
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
This method becomes async as well (because asynchronicity propagates). Again, the await keyword will hold on execution until the readFromDb method has finished. Which means that your data will be ready.
I have an "Update" function in which the end isn't executed, then I use the function "Console.WriteLine" to know where the function is interrupted. Here is the Udate function:
async Task Update()
{
count++;
try
{
List<string> cmds = new List<string>();
if (Ev3Messaging.IsConnected())
{
Tuple<string, string> response = null;
Console.WriteLine("Test1"); // Displayed
try
{
response = await Ev3Messaging.ReceiveText(true);
}
catch(Exception ex)
{
Console.WriteLine(ex.Source + ": " + ex.Message); // Not displayed
}
Console.WriteLine("Test2"); // Not displayed
while (response != null)
{
Console.WriteLine("Test2: " + response.Item2);
string[] list = response.Item2.Split(';');
foreach (string cmd in list)
cmds.Add(cmd);
response = await Ev3Messaging.ReceiveText(false);
}
}
Canvas canvas = _screenSV.Holder.LockCanvas();
if (canvas != null)
{
foreach (string cmd in cmds)
ExecuteCommand(cmd, canvas);
_screenSV.Holder.UnlockCanvasAndPost(canvas);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Source + ": " + ex.Message);
}
}
The Update function is called each 100ms by the following code:
var timer = new System.Threading.Timer((e) =>
{
RunOnUiThread(async () => await Update());
}, null, 0, 100);
It displays "Test1", but it displays neither the exception (no problem in the "EV3Messaging.ReceiveText" function), nor "Test2" (function interrupted before reaching "ShowAlert("Test2");" )
How is it possible ?
If you want me to post the "Ev3Messaging.ReceiveText" function, please ask me. I didn't post it since it is big enough, and I just want to know how is it possible to get this problem...
Thanks in advance.
I am a newbie to C# & am trying to use the Lync SDK to search for a Lync user programmatically to get their status. I am not sure how to pass the results to the webservice response when the async callback gets executed.
This is the code:
Webservice Controller GET endpoint:
// GET: api/Lync/5
public String Get(int id)
{
log.Info("[GET] Search for Lync User: " + id);
Lync lync = new Lync();
////lync.signIn();
Boolean isSignedIn = lync.isUserSignedIn();
if (isSignedIn)
{
log.Info("User is Signed In");
Console.Write("Enter search key : ");
lync.Search("medina");
//lync.Search("medina",
//(lyncContacts) =>
//{
// log.Debug("Search Results Callback fired!");
// log.Info("Results found: " + lyncContacts.Count);
// return lyncContacts.ToString();
//});
//Console.WriteLine(name);
//Console.ReadLine();
return "testUser";
}
else
{
log.Info("User is not Signed In!");
// TODO: Return status 500
return "testUser";
}
//Console.ReadLine();
//return "testUser";
}
The above method calls the business service lync.search(..) which is as follows:
public void Search(string searchKey)
{
List<LyncContact> contactList = new List<LyncContact>();
//List<ContactInformationType> ContactInformationList = new List<ContactInformationType>();
//ContactInformationList.Add(ContactInformationType.Activity);
//ContactInformationList.Add(ContactInformationType.Availability);
// ContactInformationList.Add(ContactInformationType.CapabilityString);
//ContactSubscription contactSubscription = LyncClient.GetClient().ContactManager.CreateSubscription();
Console.WriteLine("Searching for contacts on " + searchKey);
LyncClient.GetClient().ContactManager.BeginSearch(
searchKey,
(ar) =>
{
SearchResults searchResults = LyncClient.GetClient().ContactManager.EndSearch(ar);
if (searchResults.Contacts.Count > 0)
{
log.Info("Search results found: " + searchResults.Contacts.Count);
Console.WriteLine(searchResults.Contacts.Count.ToString() + " found");
foreach (Contact contact in searchResults.Contacts)
{
String displayName = contact.GetContactInformation(ContactInformationType.DisplayName).ToString();
ContactAvailability currentAvailability = (ContactAvailability)contact.GetContactInformation(ContactInformationType.Availability);
Console.WriteLine(
contact.GetContactInformation(ContactInformationType.DisplayName).ToString() + " " + contact.GetContactInformation(ContactInformationType.Availability).ToString());
log.Debug("Display Name: " + displayName);
log.Debug("Availability: " + currentAvailability);
LyncContact lyncContact = new LyncContact.Builder().DisplayName("Snehil").Availability("Busy").build();
contactList.Add(lyncContact);
//done(contactList);
}
return;
}
else
{
log.Info("No Results found!");
//done(contactList);
return;
}
},
null);
} else
{
log.Info("No Results found!");
//done(contactList);
return;
}
},
null);
}
I tried to use Task+await but I am having a hard time trying to figure out how can i return the results from the callback funtion as results from the search method. How do i capture this in the controller class?
If you would like to use the async/await pattern for this use Task.FromAsync https://msdn.microsoft.com/en-us/library/system.threading.tasks.taskfactory.fromasync(v=vs.110).aspx
Google for examples. Like this:
public async Task<List<LyncContact>> SearchAsync(string searchKey)
{
List<LyncContact> contactList = new List<LyncContact>();
Console.WriteLine("Searching for contacts on " + searchKey);
var cm = LyncClient.GetClient().ContactManager;
var searchResults = await Task<SearchResults>.Factory.FromAsync<String>(
cm.BeginSearch,
cm.EndSearch, searchKey, null);
if (searchResults.Contacts.Count > 0)
{
Console.WriteLine(searchResults.Contacts.Count.ToString() + " found");
foreach (Contact contact in searchResults.Contacts)
{
String displayName = contact.GetContactInformation(ContactInformationType.DisplayName).ToString();
ContactAvailability currentAvailability = (ContactAvailability)contact.GetContactInformation(ContactInformationType.Availability);
Console.WriteLine(
contact.GetContactInformation(ContactInformationType.DisplayName).ToString() + " " + contact.GetContactInformation(ContactInformationType.Availability).ToString());
LyncContact lyncContact = new LyncContact.Builder().DisplayName("Snehil").Availability("Busy").build();
contactList.Add(lyncContact);
}
}
return contactList
}
In the controller you then can do:
public async Task<String> Get(int id)
{
log.Info("[GET] Search for Lync User: " + id);
Lync lync = new Lync();
////lync.signIn();
Boolean isSignedIn = lync.isUserSignedIn();
if (isSignedIn)
{
log.Info("User is Signed In");
Console.Write("Enter search key : ");
var lyncContacts = await SearchAsync("medina");
log.Info("Results found: " + lyncContacts.Count);
return lyncContacts.ToString();
}
else
{
log.Info("User is not Signed In!");
// TODO: Return status 500
return "testUser";
}
//Console.ReadLine();
//return "testUser";
}
Or you can use a TaskCompletionSource, see http://blog.stephencleary.com/2012/07/async-interop-with-iasyncresult.html (older post but principle is still valid.
Warning
I cannot compile this since I do not have access to the libraries you use. It should work but there might be some compile errors
What does it do
Callbacks are not a nice model to work with, as you have found out. using Tasks and the async/await model are far easier to work with. Luckily for you and me they have created several methods that can act as a bridge between the old and the new model. Using Task.FromAsync you can transform IAsyncResult methods to an easier to use Task based methodology. Stephen Cleary has written some nice blogs about them. See the post I mentioned earlier.
It looks like, by looking at your commented out code, that you tried to provide some sort of callback to your Search function. That would work, it should be of type Action<Microsoft.Lync.Model.SearchResults>
public void Search(string searchKey, Action<SearchResults> callback)
{
....
LyncClient.GetClient().ContactManager.BeginSearch(
searchKey,
(ar) =>
{
SearchResults searchResults = LyncClient.GetClient().ContactManager.EndSearch(ar);
callback(searchResults);
});
....
}
Then just uncomment the code in your controller:
lync.Search("medina",
(lyncContacts) =>
{
log.Debug("Search Results Callback fired!");
log.Info("Results found: " + lyncContacts.AllResults.Count);
});
I have this code to load and count data from API server;
class TestNetWork
{
private Task taskFillPicker;
private List<CityItemDB> itemsCity;
private CustomPicker cpCity;
public async Task FillPicker()
{
try {
JObject res = await SuperFUNC.GET_CITY_ACTIVE_SENDER();
if(res == null){
//null
}else{
string message = res["message"].ToString();
if(message.Equals("Success")){
itemsCity.Clear();
cpCity.Items.Clear();
JArray data = (JArray)res["data"];
int count = data.Count;
for (int i = 0; i < count; i++) {
CityItemDB node = new CityItemDB();
node.cityId = Int32.Parse(data[i]["cityId"].ToString());
node.cityName = data[i]["cityName"].ToString();
itemsCity.Add(node);
cpCity.Items.Add(node.ToString());
}
}else{
//null
}
}
} catch (Exception ex) {
Debug.WriteLine (TAG + " : " + ex.StackTrace);
}
}
public TestNetWork()
{
this.itemsCity = new List<CityItemDB> ();
this.cpCity = new CustomPicker {
HeightRequest = 40,
TextColor = Color.FromHex("#5a5a5a"),
Title = "City Choose",
};
taskFillPicker = FillPicker ();
Debug.WriteLine (COUNT + " : " + itemsCity.Count);
}
}
But console print me COUNT : 0, I'm sure code get and parse json from internet is correct, picker show full data but List<CityItemDB> itemsCity count 0.
Thank for read, sorry my english not good!
You need to await the task, otherwise execution might continue before FillPicker has completed:
taskFillPicker = await FillPicker ();
As this code is in a constructor where await is not possible, I suggest moving it to a separate async method:
public async Task Init()
{
taskFillPicker = await FillPicker ();
Debug.WriteLine (COUNT + " : " + itemsCity.Count);
}
You have to write a little bit more code to construct the object now:
var n = new TestNetWork();
await n.Init();
I am using ASP.NET MVC 4 application, I need to Display messages in the Client, by sending messages from Controller to Client.
My requirement is user click a button in UI and i will process the files on the server and Display message in UI on end of each foreach file i process. i need to show the File names in the Client Using ASP.NET MVC.
Can any one Help how to show the messages in the Client by calling client method from server on for-each loop each time.
I am able to call the controller and end of each controller I am sending final message to UI, but how to send on each foreach loop iteration?
Try this:
Script method to update progress based on predefined interval you want
Controller:
public class HomeController : Controller
{
private static IDictionary<Guid, int> tasks = new Dictionary<Guid, int>();
public ActionResult Index()
{
return View();
}
public ActionResult Start()
{
var taskId = Guid.NewGuid();
tasks.Add(taskId, 0);
Task.Factory.StartNew(() =>
{
for (var i = 0; i <= 100; i++)
{
tasks[taskId] = i; // update task progress
Thread.Sleep(50); // simulate long running operation
}
tasks.Remove(taskId);
});
return Json(taskId);
}
public ActionResult Progress(Guid id)
{
return Json(tasks.Keys.Contains(id) ? tasks[id] : 100);
}
}
View:
<script type="text/javascript">
function updateMonitor(taskId, status) {
$("#" + taskId).html("Task [" + taskId + "]: " + status);
}
$(function () {
$("#start").click(function (e) {
e.preventDefault();
$.post("Home/Start", {}, function (taskId) {
// Init monitors
$("#monitors").append($("<p id='" + taskId + "'/>"));
updateMonitor(taskId, "Started");
// Periodically update monitors
var intervalId = setInterval(function () {
$.post("Home/Progress", { id: taskId }, function (progress) {
if (progress >= 100) {
updateMonitor(taskId, "Completed");
clearInterval(intervalId);
} else {
updateMonitor(taskId, progress + "%");
}
});
}, 100);
});
});
});
Start new task …
You have to write an ActionResult that progressively write result to the response. so you can show the user some data in every foreach loop iteration. I have written a simple ActionResult that writes a number every 2 seconds:
public class ProgressiveResult : ActionResult
{
public override void ExecuteResult(ControllerContext context)
{
for (int i = 0; i < 20; i++)
{
context.HttpContext.Response.Write(i.ToString());
Thread.Sleep(2000);
context.HttpContext.Response.Flush();
}
context.HttpContext.Response.End();
}
}
and this is an action that returns this result:
public ActionResult LongProcess()
{
return new ProgressiveResult();
}
So you can write an ActionResult and write your foreach code in ExecuteResult method.
UPDATE:
You can make this call with an Ajax request and return result with a simple code like the following code:
var result = "";
function showResult() {
if (result !== oReq.responseText) {
result = oReq.responseText;
console.log(result);
}
}
var oReq = new XMLHttpRequest();
oReq.open("get", "/Home/LongProcess", true);
oReq.send();
setInterval(showResult, 1000);