MVC Javascript xmlhttprequest 调用异步方法
Posted
技术标签:
【中文标题】MVC Javascript xmlhttprequest 调用异步方法【英文标题】:MVC Javascript xmlhttprequest calling async method 【发布时间】:2016-02-17 13:40:02 【问题描述】:我正在转换处理 - 解析 - 上传文件的旧 - 旧 - Web 应用程序。它使用纯 javascript + MVC。
/* javascript */
function Parse(files)
var idFiles=document.getElementById('inputF').value;
CallServer('ProcessFile/Parse', idFiles, function (m)
if (m != null)
var om = JSON.parse(m);
MsgLog('Parse finished ' + om.msg);
);
var CallServer = function (url, content, callback)
var rqs = new XMLHttpRequest();
rqs.open('POST', url, true);
rqs.onreadystatechange = function ()
if (rqs.readyState == 4 && rqs.status == 200)
callback(rqs.responseText);
return true;
else return false;
;
rqs.send(content);
/* View */
...
<a href="#" id="parseFiles" onclick="Parse()" title="Parse selected documents">Parse</a>
...
/* Controller */
public JsonResult Parse(string idFiles)
// parses input string and returns an array of int
int[] idf=getIds(idFiles);
string wholeFile=string.Empty;
string parsedData=string.Empty;
string outputFileName=string.Empty;
List<string> doneFiles=new List<string>();
foreach(int f in idf)
wholeFile=ReadFile(f);
parsedData=ParseFile(wholeFile);
outputFileName=SaveParsed(parsedData);
doneFiles.Add(outputFileName);
return Json(new doneFiles );
控制器返回数据以查看显示已解析文件的列表。 由于解析需要很长时间才能运行,我正在尝试将此代码转换为 async/await Task + Parallel (?) 代码。我重写了 Parse 方法,但我对 async/await Task + Parallel 的东西有点困惑。 此外,我不知道是否必须更改 javascript。 现在控制器看起来像:
public JsonResult Parse(string idFiles)
int[] idf=getIds(idFiles);
string wholeFile=string.Empty;
string parsedData=string.Empty;
string outputFileName=string.Empty;
List<string> doneFiles=new List<string>();
await Task.Run(() =>Parallel.ForEach(idf, async currFile =>
wholeFile=ReadFile(currFile);
Task<string> parseData=ParseFile(wholeFile);
await Task.WhenAll(parseData);
Task<string> write = await parseData.ContinueWith(async (aa) => await SaveParsed(parsedData));
Task.WhenAll(write);
return Json(new doneFiles );
我的需求将是一个真正的异步 Parse 方法,完成后更新视图中已解析文件的列表... file1 - 已解析 file2 - 已解析 文件3 -
【问题讨论】:
你的问题到底是什么? 我不知道我使用语句 Task.Run 和 Parallel.ForEach 使 Parse 方法“异步”的方式是否正确,以及是否必须更改 javascript 调用。 【参考方案1】:由于解析需要很长时间才能运行,我正在尝试将此代码转换为 async/await Task + Parallel (?) 代码。
这些都不会神奇地提高代码的性能。你需要知道你的代码做了什么,为什么它很慢,然后使用正确的工具来完成这项工作。
例如,如果慢速部分正在与服务器通信,则使用 async
-await
使该通信并发可能会有所帮助。如果慢的部分是 CPU 绑定代码,Parallel.ForEach()
可能是正确的解决方案。
话虽如此,您的代码包含很多错误:
await Task.Run(…)
:这只是将您的代码切换为在不同的线程上执行,它不会以任何方式提高性能。它唯一要做的就是增加一些开销。
Parallel.ForEach(idf, async currFile => …
:Parallel.ForEach()
does not play nice with async
-await
. 酌情使用其中一种,但不能同时使用。
wholeFile=ReadFile(currFile);
:您在循环外定义变量,这意味着循环的所有迭代都共享一个变量。这对于简单的foreach
来说很好(尽管即使那样我也会认为它是一种糟糕的风格),但对于并行代码它就不能正常工作,因为线程会不断地重写彼此的变量,从而导致混乱的错误。
wholeFile=ReadFile(currFile); Task<string> parseData=ParseFile(wholeFile);
:ReadFile
听起来像是一个 IO-bound 操作,这意味着让它异步是有意义的。 ParseFile
听起来像是受 CPU 限制的操作,因此将其设为异步可能没有意义。
await Task.WhenAll(parseData);
:当您想同时await
多个Task
s 时使用Task.WhenAll()
。你只有一个Task
,所以你可以只使用await parseData
。
await parseData.ContinueWith(async (aa) => await SaveParsed(parsedData))
:一般来说,async
-await
会使ContinueWith()
过时。如果你想得到Task
的结果,使用await
比ContinueWith()
简单得多。
Task.WhenAll(write);
:这不会做任何事情,Task.WhenAll()
返回一个 Task
结合了其他几个 Task
s,它不会对其进行任何等待拥有。
你永远不会添加到doneFiles
,所以结果总是空的。但是你不能只从并行循环内部调用Add()
,因为它不是线程安全的。
要使用await
,您需要将您的方法标记为async
,并将返回类型更改为Task
。
你怎么能让这一切正常工作?就像我说的,这在很大程度上取决于代码的性能特征。因此,我将假设同时读取和写入文件是有意义的,或者至少它不会造成伤害(它可以,尤其是对于普通的旋转硬盘驱动器)。而且我还将假设并行解析是有意义的(例如,如果您的服务器总是在处理许多请求,它可能不会)。
为此,您的代码的简单版本可能如下所示:
private async Task<string> ParseOne(int fileId)
string wholeFile = await ReadFile(fileId);
string parsedData = ParseFile(wholeFile);
string outputFileName = await SaveParsed(parsedData);
return outputFileName;
public async Task<JsonResult> Parse(string idFiles)
int[] idf = getIds(idFiles);
var doneFiles = await Task.WhenAll(idf.Select(id => ParseOne(id)));
return Json(new doneFiles );
此代码的更复杂版本也可能更合适。例如,使用SemaphoreSlim
限制并发。甚至是 TPL 数据流,以便更好地控制解析过程的每个部分。
【讨论】:
感谢您的所有澄清和 cmets。谢谢!我要改变我的实现。以上是关于MVC Javascript xmlhttprequest 调用异步方法的主要内容,如果未能解决你的问题,请参考以下文章