使用async/await获取信息

本文关键字:获取 信息 await async 使用 | 更新日期: 2023-09-27 17:51:17

我有一个列表,我需要加载产品名称,如:

List<string> prodNames= new List<string>();

产品数据在我的系统中不存在。所以,我必须先下载它们。

产品数据逐条下载到本地文件系统。下载一个产品会触发一个我处理的事件处理程序,该处理程序提供我需要添加到列表中的产品名称。

我知道在任何时候有多少个产品需要这样做,比如5个。

我正试图弄清楚我如何知道当所有5个产品名称已添加到我的列表,以便我可以继续显示它们。下面是伪代码

List ids = // list of product ids 
List<string> prodNames= new List<string>();  //list to fill with prod names once they are downloaded
this.EventHandler += OnDownloadedProduct; //event fired once each product is downloaded
void GetProducts()
{
  foreach (var id in ids)
  {
     // NOTE that I cannot modify neither downloader object nor GetProduct() 
     // method.  These are not my code but come from a component for which
     // I have no code access.
     downloader.GetProduct(id);  //fires OnDownloadedProduct once product is downloaded
  } 
}
//this event is fired once each product data is downloaded.
// ProductDataArgs is provided to me and I cannot modify it.
void OnDownloadedProduct(object sender, ProductDataArgs e)
{
   // this is where I get product name and add it to the list
   string prodName = e.getProdName();
   prodNames.Add(prodName);
   // <-- I could show prodName here in a message box but that
   // would show one message box for all 5 products.  I don't
   // want that.  I want to show all 5 product names in one
   // message box.
}

我需要在表单或消息框中显示所有5个产品名称。我可以在上面的事件中这样做,但是对于每个产品。这将显示5个消息框(或表单)。但我需要在一个消息框中显示所有5个。因此,我需要在caller:

中输入这样的内容
List<string> prodNames = await.GetProducts(ids);
var names = null;
foreach(var prod in prodNames)
{
  names += string.Format("{0}'n");
}
MesageBox.Show(names);

问题:我面临的问题是,操作可能需要时间,我不知道列表何时加载所有5个产品名称(因为名称在GetProducts方法中不可用,但在ondownloaddproduct事件中不可用)。

在我自己的一些研究之后,Tasks/async/await似乎提供了解决方案,但到目前为止我看到的所有示例都没有解决我上面描述的情况,我依赖于一个事件处理程序来获得我需要的东西(在这种情况下,所有5个产品名称)。

感谢下面的@Kris评论,我已经能够将我的逻辑重写为这个,但它不能正常工作:

public class ProductRetriver
{
    List<int> ids = new List<int>();  // product ids
    List<string> prodNames = new List<string>();  // product names
    private TaskCompletionSource<List<string>> prodsTask;
    this.EventHandler += OnDownloadedProduct; 
    Task<List<string>> async GetProducts(List<int> _ids)
    {
        ids = _ids;
        prodsTask = new TaskCompletionSource<List<string>>();
        foreach (var id in ids)
        {
            //PROBLEM: GetProduct() is not awaited, so method will finish
            //before list is filled.  Also, it seem to add duplicates for
            //some reason to the list.  I cannot modify the method as it is
            //not code available to me.
            downloader.GetProduct(id);  //fires OnDownloadedProduct 
        }
        return prodNames;
    }
    //this event is fired once each product data is downloaded
    void OnDownloadedProduct(object sender, ProductDataArgs e)
    {
        // this is where I get product name and add it to the list
        string prodName = e.getProdName();
        prodNames.Add(prodName);
        if (prodNames.Count == ids.Count)
        {
            prodsTask.SetResult(prodNames);
        }
    }
}

使用async/await获取信息

我不知道这个下载程序是什么,它来自哪里,如果eventhandler是外面的下载程序试试这个

 public class ProductRetriver
{
    List<int> ids = new List<int>();
    List<string> prodNames = new List<string>();  //list to fill with prod names once they are downloaded
    private TaskCompletionSource<List<string>> prodsTask;
        this.EventHandler += OnDownloadedProduct; //event fired once each product is downloaded
        Task<List<string>> async GetProducts(List<int> _ids)
    {
        ids = _ids;
        prodsTask = new TaskCompletionSource<List<string>>();
        foreach (var id in ids)
        {
            downloader.GetProduct(id);  //fires OnDownloadedProduct once product is downloaded
        }
    }
    //this event is fired once each product data is downloaded
    void OnDownloadedProduct(object sender, ProductDataArgs e)
    {
        // this is where I get product name and add it to the list
        string prodName = e.getProdName();
        prodNames.Add(prodName);
        if (prodNames.Count == ids.Count)
        {
            prodsTask.SetResult(prodNames);
        }
    }
}

如果eventandler是downloader的一部分,你可以这样做

Task<List<string>> async GetProducts(List<int> ids)
    {
        List<string> prodNames = new List<string>();
        downloder.EventHandler += (s, e) => {
            string prodName = e.getProdName();
            prodNames.Add(prodName);
            if (prodNames.Count == ids.Count)
            {
                prodsTask.SetResult(prodNames);
            }
        };
        foreach (var id in ids)
        {
            downloader.GetProduct(id);  //fires OnDownloadedProduct once product is downloaded
        }
        return prodNames;
    }

你可以这样调用

Products = await getproducts(ids);

如果我理解正确,您不需要收到来自downloader.GetProduct(id)的结果吗?如果是这样——你想要什么?只需创建任务列表,然后等待它们全部完成,就像这样:

public class ProductRetriver
{
List<int> ids = new List<int>();  // product ids
List<string> prodNames = new List<string>();  // product names
private TaskCompletionSource<List<string>> prodsTask;
this.EventHandler += OnDownloadedProduct; 
public static List<Task> TaskList = new List<Task>();
Task<List<string>> async GetProducts(List<int> _ids)
{
    ids = _ids;
    prodsTask = new TaskCompletionSource<List<string>>();
    foreach (var id in ids)
    {
       TaskList.Add(downloader.GetProduct(id));
    }
    Task.WaitAll(TaskList.ToArray());
    return prodNames;
}
//this event is fired once each product data is downloaded
void OnDownloadedProduct(object sender, ProductDataArgs e)
{
    // this is where I get product name and add it to the list
    string prodName = e.getProdName();
    prodNames.Add(prodName);
    if (prodNames.Count == ids.Count)
    {
        prodsTask.SetResult(prodNames);
    }
}

}