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 =&gt; …:Parallel.ForEach() does not play nice with async-await. 酌情使用其中一种,但不能同时使用。

wholeFile=ReadFile(currFile);:您在循环外定义变量,这意味着循环的所有迭代都共享一个变量。这对于简单的foreach 来说很好(尽管即使那样我也会认为它是一种糟糕的风格),但对于并行代码它就不能正常工作,因为线程会不断地重写彼此的变量,从而导致混乱的错误。

wholeFile=ReadFile(currFile); Task&lt;string&gt; parseData=ParseFile(wholeFile);:ReadFile 听起来像是一个 IO-bound 操作,这意味着让它异步是有意义的。 ParseFile 听起来像是受 CPU 限制的操作,因此将其设为异步可能没有意义。

await Task.WhenAll(parseData);:当您想同时await 多个Tasks 时使用Task.WhenAll()。你只有一个Task,所以你可以只使用await parseData

await parseData.ContinueWith(async (aa) =&gt; await SaveParsed(parsedData)):一般来说,async-await 会使ContinueWith() 过时。如果你想得到Task的结果,使用awaitContinueWith()简单得多。

Task.WhenAll(write);:这不会做任何事情,Task.WhenAll() 返回一个 Task 结合了其他几个 Tasks,它不会对其进行任何等待拥有。

你永远不会添加到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 调用异步方法的主要内容,如果未能解决你的问题,请参考以下文章

Ajax

JavaScript中的跨域详解(内附源码)

MVC 5 JavaScript 缓存 [重复]

javaScript与MVC

javascript中的MVC“〜”路径

MVC 视图和 JavaScript 文件