I have 2 ajax API calls one fetches data in a long process and and other gets the progress value using IProgress interface and executes every 5 seconds. When hitting the debugger I can see the value updates in ReportProgress
and this value gets set to _generateRecipeCardsStatus
property. My issue is when calling getProgress()
-> GeneratePrintRecipeCardsStatus
the property _generateRecipeCardsStatus
is always zero where am I going wrong?
<script>
getData();
getProgress()
// long process
function getData() {
const token = $('input[name="__RequestVerificationToken"]').val();
$.ajax({
headers: {
'X-Requested-With': 'XMLHttpRequest',
"X-ANTI-FORGERY-TOKEN": token
},
type: 'POST',
url: `@Url.Action("GeneratePrintRecipeCards", "MenuPrintout")?id=` + @Model.RecipeCardLabelId + `&menuHeaderId=` + @Model.MenuHeaderId,
success: function(response){
console.log(response)
const { errorMsg, progressValue } = response
inProgressEle.hide();
close.show();
if (errorMsg) {
toast(errorMsg, "error")
}
else if (progressValue > 0) {
console.log("progress value:" + progressValue);
}
else{
expand.hide()
collapse.hide()
statusEle.text("Ready");
completedEle.fadeIn()
}
}
});
}
// get progress
function getProgress() {
setInterval(function(){
$.ajax({
url: '@Url.Action("GeneratePrintRecipeCardsStatus", "MenuPrintout")',
type: "GET",
success: function (response) {
console.log(response)
$('#lblStatus').text(response)
}
})
}, 5000)
}
</script>
// controller
public class MenuPrintoutController : BaseController
{
private readonly IMenuLabelService _menuLabelService;
private int _generateRecipeCardsStatus;
private Progress<int> _progress;
public MenuPrintoutController(IMenuLabelService menuLabelServicee)
{
_menuLabelService = menuLabelService;
}
// Is always zero
public int GeneratePrintRecipeCardsStatus()
{
return _generateRecipeCardsStatus;
}
// Updates fine
private void ReportProgress(int progress)
{
_generateRecipeCardsStatus = progress;
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> GeneratePrintRecipeCards(int id, int menuHeaderId)
{
try
{
_progress = new Progress<int>(ReportProgress);
var data = await _menuLabelService.DoAsync(int menuHeaderId, _progress);
TempData.Put("PrintRecipeCards", data);
return Json(new { Success = true });
}
catch (Exception exp)
{
return Json(new { ErrorMsg = exp.Message });
}
}
}
// service
public interface IMenuLabelService
{
Task<object> DoAsync(int menuHeaderId, IProgress<int> progress);
}
public class MenuLabelService : IMenuLabelService
{
public async Task<object> DoAsync(int menuHeaderId, IProgress<int> progress)
{
var menuFoodItemsList = _context.FoodItemUnits
.Where(x => x.MenuSectionFoodItemUnits.Any(y => y.Menusection.MenuheaderId == menuHeaderId))
.Select(x => x.Fooditem)
.ToList();
var menuFoodItems = new List<PrintLabel>();
var donePointer = 0;
foreach (var menuFoodItem in menuFoodItemsList)
{
menuFoodItems.Add(menuFoodItem);
// ...
progress.Report((int)(++donePointer / (double)menuFoodItemsList.Count * 100));
donePointer++;
}
return menuFoodItems;
}
}
A new controller is created for each request, so there are two different _generateRecipeCardsStatus
variables.
If you only want to support a single call on a single server, you can just make the variable static.
If you want to allow multiple calls, then you'll need some kind of identifier/lookup mechanism. Note that you will also want a way to remove the statuses when they're too old (and presumably no longer needed), so this lookup is less of a dictionary and more of a cache.
If you want to deploy this to multiple servers, then you'll need an external cache rather than keeping it in memory.