如何从非异步方法调用异步方法
本文关键字:异步方法 调用 | 更新日期: 2023-09-27 18:14:48
我在xamarin表格工作。我正在尝试在日历控制中绑定Web服务。参考下面的日历控件链接(XamForms.Controls.Calendar)。
https://github.com/rebeccaXam/XamForms.Controls.Calendar第一个函数是创建7*6=42个标签和按钮,然后使用"callWebService"方法调用服务函数,这是异步方法,从服务中获取响应。
protected void FillCalendarWindows()
{
try
{
for (int r = 0; r < 6; r++)
{
for (int c = 0; c < 7; c++)
{
if (r == 0)
{
labels.Add(new Label
{
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
TextColor = Color.Black,
FontSize = 18,
FontAttributes = FontAttributes.Bold
});
DayLabels.Children.Add(labels.Last(), c, r);
}
buttons.Add(new CalendarButton
{
BorderRadius = 0,
BorderWidth = BorderWidth,
BorderColor = BorderColor,
FontSize = DatesFontSize,
BackgroundColor = DatesBackgroundColor,
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand
});
buttons.Last().Clicked += DateClickedEvent;
MainCalendar.Children.Add(buttons.Last(), c, r);
}
}
flag = 1;
//Device.BeginInvokeOnMainThread(() => CallWebService(StartDate.Month, StartDate.Year));
CallWebService(StartDate.Month, StartDate.Year);
//CallServiceInNewTask(StartDate.Month, StartDate.Year);
//Device.BeginInvokeOnMainThread(() => ChangeCalendar(CalandarChanges.All));
}
catch (Exception e)
{
}
}
第二个函数是"callWebService"函数,我在列表收集对象中收集响应,然后调用"ChangeClaendar"函数,该函数用于绑定标签和按钮文本并填充适当的颜色。
public async void CallWebService(int Month, int Year)
{
try
{
var response = await GetResponseFromWebService.GetResponse<ServiceClasses.RootObject_AttendanceTable>(ServiceURL.GetAttendanceTableList + "Month=" + Month + "&Year=" + Year + "&EmpCd=" + _empCode);
if (response.Flag == true)
{
if (ListObjAttendanceTblList == null)
{
ListObjAttendanceTblList = new List<LstAttendanceDtl>();
}
for (int i = 0; i < response.lstAttendanceDtl.Count; i++)
{
var objAttendanceTableList = new LstAttendanceDtl();
objAttendanceTableList.AttendanceDt = response.lstAttendanceDtl[i].AttendanceDt;
objAttendanceTableList.EarlyDeparture = response.lstAttendanceDtl[i].EarlyDeparture;
objAttendanceTableList.InTime = response.lstAttendanceDtl[i].EarlyDeparture;
objAttendanceTableList.LateArrival = response.lstAttendanceDtl[i].EarlyDeparture;
objAttendanceTableList.OutTime = response.lstAttendanceDtl[i].OutTime;
objAttendanceTableList.OverTime = response.lstAttendanceDtl[i].OverTime;
objAttendanceTableList.Reason = response.lstAttendanceDtl[i].Reason;
objAttendanceTableList.Remark = response.lstAttendanceDtl[i].Remark;
objAttendanceTableList.Shift = response.lstAttendanceDtl[i].Shift;
objAttendanceTableList.WorkingHrs = response.lstAttendanceDtl[i].WorkingHrs;
ListObjAttendanceTblList.Add(objAttendanceTableList);
}
}
else
{
}
if (flag == 1)
{
ChangeCalendar(CalandarChanges.All);
}
else
{
ChangeCalendar(CalandarChanges.StartDate);
}
}
catch (WebException e)
{
}
}
第三个函数是"ChangeCalendar"
protected void ChangeCalendar(CalandarChanges changes)
{
try
{
if (changes.HasFlag(CalandarChanges.StartDate))
{
Device.BeginInvokeOnMainThread(() => CenterLabel.Text = StartDate.ToString(TitleLabelFormat));
}
var start = CalendarStartDate;
var beginOfMonth = false;
var endOfMonth = false;
for (int i = 0; i < buttons.Count; i++)
{
endOfMonth |= beginOfMonth && start.Day == 1;
beginOfMonth |= start.Day == 1;
LstAttendanceDtl objAttendanceDtl = ListObjAttendanceTblList.Find(s => s.AttendanceDt.Equals(start.Date.ToString("dd/MM/yyyy")));
string remarks = string.Empty;
if (i < 7 && WeekdaysShow && changes.HasFlag(CalandarChanges.StartDay))
{
Device.BeginInvokeOnMainThread(() => labels[i].Text = start.ToString(WeekdaysFormat));
//labels[i].Text = start.ToString(WeekdaysFormat);
//DateTime d = Convert.ToDateTime(objAttendanceDtl.AttendanceDt).Date;
}
if (changes.HasFlag(CalandarChanges.All))
{
Device.BeginInvokeOnMainThread(()=>buttons[i].Text = string.Format("{0}", start.Day));
//buttons[i].Text = string.Format("{0}", start.Day);
}
else
{
Device.BeginInvokeOnMainThread(() => buttons[i].TextWithoutMeasure = string.Format("{0}", start.Day));
}
buttons[i].Date = start;
var isInsideMonth = beginOfMonth && !endOfMonth;
if (objAttendanceDtl != null)
{
remarks = objAttendanceDtl.Remark;
if ((remarks.ToLower()).Trim() == stringFullDay.ToLower().Trim())
{
SetButtonPresent(buttons[i], isInsideMonth);
}
else if (remarks.ToLower().Trim() == stringAbsent.ToLower().Trim())
{
SetButtonAbsent(buttons[i], isInsideMonth);
}
else if (remarks.ToLower().Trim() == stringWeekOff.ToLower().Trim())
{
SetButtonWeekendMood(buttons[i], isInsideMonth);
}
else if (remarks.ToLower().Trim() == stringHolidays.ToLower().Trim())
{
SetButtonHolidays(buttons[i], isInsideMonth);
}
else if (remarks.ToLower().Trim() == stringSecondhalfAbsent.ToLower().Trim() ||
remarks.ToLower().Trim() == stringFirsthalfAbsent.ToLower().Trim())
{
SetButtonHalfDayMood(buttons[i], isInsideMonth);
}
else
{
SetButtonDisabled(buttons[i]);
}
}
else
{
SetButtonOutSideMonth(buttons[i]);
}
SpecialDate sd = null;
if (SpecialDates != null)
{
sd = SpecialDates.FirstOrDefault(s => s.Date.Date == start.Date);
}
if (sd != null)
{
SetButtonSpecial(buttons[i], sd);
}
else if (SelectedDate.HasValue && start.Date == SelectedDate.Value.Date)
{
SetButtonSelected(buttons[i], isInsideMonth);
}
start = start.AddDays(1);
}
}
catch (Exception e)
{
}
}
问题是:
1。在"Changecalendar"函数中,当我尝试直接填写标签列表时
labels[i].Text = start.ToString(WeekdaysFormat);
显示错误
"UIKit一致性错误:你正在调用一个只能从UI线程调用的UIKit方法"所以为了消除这个错误,我写了
Device.BeginInvokeOnMainThread(() => labels[i].Text = start.ToString(WeekdaysFormat));
Device.BeginInvokeOnMainThread(()=>buttons[i].Text = string.Format("{0}", start.Day));
Device.BeginInvokeOnMainThread(() => buttons[i].TextWithoutMeasure = string.Format("{0}", start.Day));
但是显示错误
系统。参数outofrangeexception:索引超出范围。必须非负且小于集合的大小。
2。如果我把函数"CallWebService"answers"ChangeCalendar"一个接一个地放在"FillCalendarWindow"中,那么列表对象就不会被绑定,控制就会从函数中出来,直接调用ChangeCalendar函数并给我null引用对象。
问题没有提供测试解决方案所需的完整源代码。
回答你关于在非异步方法中调用可等待函数的问题。你可以使用
CallWebService().Wait(optional timeout);
或
CallWebService().GetAwaiter().GetResult();
你也应该改变你的函数定义
async void CallWebService(int Month, int Year)
async Task CallWebService(int Month, int Year);
正确处理异常和线程切换
如果你可以在不阻塞的情况下调用CallWebService,那么你也可以执行
CallWebService(1,2).ContinueWith((task) =>
{
});
尝试以下
protected async Task FillCalendarWindows()
{
try
{
for (int r = 0; r < 6; r++)
{
for (int c = 0; c < 7; c++)
{
if (r == 0)
{
labels.Add(new Label
{
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
TextColor = Color.Black,
FontSize = 18,
FontAttributes = FontAttributes.Bold
});
DayLabels.Children.Add(labels.Last(), c, r);
}
buttons.Add(new CalendarButton
{
BorderRadius = 0,
BorderWidth = BorderWidth,
BorderColor = BorderColor,
FontSize = DatesFontSize,
BackgroundColor = DatesBackgroundColor,
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand
});
buttons.Last().Clicked += DateClickedEvent;
MainCalendar.Children.Add(buttons.Last(), c, r);
}
}
flag = 1;
//Device.BeginInvokeOnMainThread(() => CallWebService(StartDate.Month, StartDate.Year));
await CallWebService(StartDate.Month, StartDate.Year);
//CallServiceInNewTask(StartDate.Month, StartDate.Year);
//Device.BeginInvokeOnMainThread(() => await ChangeCalendar(CalandarChanges.All));
}
catch (Exception e)
{
}
}
你的Webservice应该像
public async Task CallWebService(int Month, int Year)
{
try
{
var response = await GetResponseFromWebService.GetResponse<ServiceClasses.RootObject_AttendanceTable>(ServiceURL.GetAttendanceTableList + "Month=" + Month + "&Year=" + Year + "&EmpCd=" + _empCode);
if (response.Flag == true)
{
if (ListObjAttendanceTblList == null)
{
ListObjAttendanceTblList = new List<LstAttendanceDtl>();
}
for (int i = 0; i < response.lstAttendanceDtl.Count; i++)
{
var objAttendanceTableList = new LstAttendanceDtl();
objAttendanceTableList.AttendanceDt = response.lstAttendanceDtl[i].AttendanceDt;
objAttendanceTableList.EarlyDeparture = response.lstAttendanceDtl[i].EarlyDeparture;
objAttendanceTableList.InTime = response.lstAttendanceDtl[i].EarlyDeparture;
objAttendanceTableList.LateArrival = response.lstAttendanceDtl[i].EarlyDeparture;
objAttendanceTableList.OutTime = response.lstAttendanceDtl[i].OutTime;
objAttendanceTableList.OverTime = response.lstAttendanceDtl[i].OverTime;
objAttendanceTableList.Reason = response.lstAttendanceDtl[i].Reason;
objAttendanceTableList.Remark = response.lstAttendanceDtl[i].Remark;
objAttendanceTableList.Shift = response.lstAttendanceDtl[i].Shift;
objAttendanceTableList.WorkingHrs = response.lstAttendanceDtl[i].WorkingHrs;
ListObjAttendanceTblList.Add(objAttendanceTableList);
}
}
else
{
}
if (flag == 1)
{
await ChangeCalendar(CalandarChanges.All);
}
else
{
await ChangeCalendar(CalandarChanges.StartDate);
}
}
catch (WebException e)
{
}
}
ChnageCalender方法应该是
protected async Task ChangeCalendar(CalandarChanges changes)
{
try
{
if (changes.HasFlag(CalandarChanges.StartDate))
{
Device.BeginInvokeOnMainThread(() => CenterLabel.Text = StartDate.ToString(TitleLabelFormat));
}
var start = CalendarStartDate;
var beginOfMonth = false;
var endOfMonth = false;
for (int i = 0; i < buttons.Count; i++)
{
endOfMonth |= beginOfMonth && start.Day == 1;
beginOfMonth |= start.Day == 1;
LstAttendanceDtl objAttendanceDtl = ListObjAttendanceTblList.Find(s => s.AttendanceDt.Equals(start.Date.ToString("dd/MM/yyyy")));
string remarks = string.Empty;
if (i < 7 && WeekdaysShow && changes.HasFlag(CalandarChanges.StartDay))
{
Device.BeginInvokeOnMainThread(() => labels[i].Text = start.ToString(WeekdaysFormat));
//labels[i].Text = start.ToString(WeekdaysFormat);
//DateTime d = Convert.ToDateTime(objAttendanceDtl.AttendanceDt).Date;
}
if (changes.HasFlag(CalandarChanges.All))
{
Device.BeginInvokeOnMainThread(()=>buttons[i].Text = string.Format("{0}", start.Day));
//buttons[i].Text = string.Format("{0}", start.Day);
}
else
{
Device.BeginInvokeOnMainThread(() => buttons[i].TextWithoutMeasure = string.Format("{0}", start.Day));
}
buttons[i].Date = start;
var isInsideMonth = beginOfMonth && !endOfMonth;
if (objAttendanceDtl != null)
{
remarks = objAttendanceDtl.Remark;
if ((remarks.ToLower()).Trim() == stringFullDay.ToLower().Trim())
{
SetButtonPresent(buttons[i], isInsideMonth);
}
else if (remarks.ToLower().Trim() == stringAbsent.ToLower().Trim())
{
SetButtonAbsent(buttons[i], isInsideMonth);
}
else if (remarks.ToLower().Trim() == stringWeekOff.ToLower().Trim())
{
SetButtonWeekendMood(buttons[i], isInsideMonth);
}
else if (remarks.ToLower().Trim() == stringHolidays.ToLower().Trim())
{
SetButtonHolidays(buttons[i], isInsideMonth);
}
else if (remarks.ToLower().Trim() == stringSecondhalfAbsent.ToLower().Trim() ||
remarks.ToLower().Trim() == stringFirsthalfAbsent.ToLower().Trim())
{
SetButtonHalfDayMood(buttons[i], isInsideMonth);
}
else
{
SetButtonDisabled(buttons[i]);
}
}
else
{
SetButtonOutSideMonth(buttons[i]);
}
SpecialDate sd = null;
if (SpecialDates != null)
{
sd = SpecialDates.FirstOrDefault(s => s.Date.Date == start.Date);
}
if (sd != null)
{
SetButtonSpecial(buttons[i], sd);
}
else if (SelectedDate.HasValue && start.Date == SelectedDate.Value.Date)
{
SetButtonSelected(buttons[i], isInsideMonth);
}
start = start.AddDays(1);
}
}
catch (Exception e)
{
}
}
我假设FillCalendarWindows()方法是从UI线程调用的,但你不希望UI线程等待并冻结控件,而外部操作GetResponseFromWebService。GetResponse发生,这就是为什么在CallWebService()中等待异步调用的原因。
我认为使用异步CallWebService的一个更好的方法是使用"异步所有的方式",所以你将不得不改变CallWebService上游的所有方法为异步方法。这有一个额外的好处,你不需要做Device。由于async/await捕获了调用者的同步上下文,因此ChangeCalendar将在UI线程上被调用。
async void SomeEventHandler()
{
// called from the UI thread (or its equivalent in Xamarin)
await FillCalendarWindows();
}
protected async Task FillCalendarWindows()
{
try
{
//create 7*6 = 42 labels and buttons
await CallWebService(StartDate.Month, StartDate.Year);
}
catch (Exception e)
{
}
}
public async Task CallWebService(int Month, int Year)
{
try
{
await GetResponseFromWebService.GetResponse... ;
// .... same code as in your example
ChangeCalendar(....);
}
catch /*... */
{
}
}
protected void ChangeCalendar(int changes)
{
try
{
/* no need to do Device.BeginInvokeOnMainThread () so you can replace all that with normal calls*/
}
catch (Exception e)
{
/* ... */
}
}
不确定系统如何。ArgumentOutOfRangeException会被引发,我无法在github上找到正确的代码版本,所以我无法调查那个特定的错误。我的猜测是你有多个线程修改"按钮"集合,当你调用设备。你可能会发现集合的元素比预期的少。
TL;DR:始终使用async/await而不是以同步方式调用async方法,这应该更容易找到问题的原因
我从你的评论中至少看到了两个问题
-
"If(response.flag==true)(控制超出函数)并直接调用ChangeCalendar() -显然是因为你没有等待。
-
你的按钮数量(42)与标签数量(7)不一样,所以当你尝试用相同的"i"去标签[i]和按钮[i]时,你会得到标签索引的ArgumentOutOfRangeException。注:
if(r==0)
限制标签数量为7。for (int r = 0; r < 6; r++) { for (int c = 0; c < 7; c++) { if (r == 0) { labels.Add(new Label