读书笔记,微博头阵

编排分界面如图:

假如阅读本文的您曾经对await/async有了一定精通。

料定,async方法只可以够回到void,Task和Task<T>。

参考资料:明白javaScript中的async/await,谢谢原版的书文小编的下结论,本文在理解的功底上做了一些细微修改,主要为了深化本身的知识点精通

澳门葡京备用网址 1

.net中的新珍视字await出来很久,刚接触时心中对它特别争执,习于旧贯了用过去的异步模型,认为已经够用本人实现种种想要的意义,认为新出的await简化了代码,必然捐躯代码的狡滑。那段时光公司项目不忙,左近同事一同座谈await用法,研究之间开采我们对其精通各不相同,那……激发本身对await的递进研商的志趣。

 

学完了Promise,我们领会能够用then链来化解多层回调难题,可是那还不是最出彩的操作,大家供给调用很七个then链本事达到须要,那么有未有壹种更简便易行代码量更加少的法门落成then链同样的结果吧?asynv和await就很好地消除了那些标题,首先用async声雅培(Abbott)个异步函数,然后再用await等待异步结果,把在此以前then链的结果放到直接放在await,卓殊便于。

澳门葡京备用网址 2澳门葡京备用网址 3

首先说几点周围的误区,随后介绍下await的高端用法。

对于再次来到void的async方法,它并不是awaitable,所以任何措施不能够用await方法来调用它,而回到Task的async方法则足以。

那正是说,async和await原理是什么吧?为何能够用如此的语法来优化then链呢?

private async void button1_Click(object sender, EventArgs e)
        {
            #region 单个执行的异步,效率慢
            HttpClient wc = new HttpClient();
            string s1 = await wc.GetStringAsync(textBox1.Text);
            label1.Text = s1.Length.ToString();
            string s2 = await wc.GetStringAsync(textBox2.Text);
            label2.Text = s2.Length.ToString();
            string s3 = await wc.GetStringAsync(textBox3.Text);
            label3.Text = s3.Length.ToString();
            #endregion

            #region 并发全部完成的异步更快
            HttpClient hc = new HttpClient();
            var task1 = hc.GetStringAsync(textBox1.Text);
            var task2 = hc.GetStringAsync(textBox2.Text);
            var task3 = hc.GetStringAsync(textBox3.Text);
            Task.WaitAll(task1, task2, task3);
            label1.Text = task1.Result.Length.ToString();
            label2.Text = task2.Result.Length.ToString();
            label3.Text = task3.Result.Length.ToString();
            #endregion

        }

误区一:带有async/await关键字的法子难道正是异步方法了吗?

 

1. async/await是什么?

async/await其实是Promise的语法糖,它能促成的职能都能用then链来贯彻,那也和我们在此以前提到的等同,它是为优化then链而开垦出来的。从字面上来看,async是“异步”的简写,await译为等待,所以大家很好掌握async评释function是异步的,await等待有个别操作完毕。当然语法上强制规定await只好冒出在asnyc函数中,大家先来探望async函数重返了哪些:

async function testAsy(){   return 'hello world';}let result = testAsy(); console.log

澳门葡京备用网址 4

本条async表明的异步函数把return前面直接量通过Promise.resolve()重回Promise对象,所以借使那么些最外层未有用await调用的话,是足以用原来then链的艺术来调用的:

async function testAsy(){   return 'hello world'}let result = testAsy() console.logresult.then(v=>{    console.log   //hello world})

联想一下Promise特点——异步无等待,所以当未有await语句实行async函数,它就会即时推行,重回一个Promise对象,非阻塞,与常见的Promise对象函数1致。

入眼就在await,它等待什么啊?

依据语法表明,await等待的是3个Promise对象,可能是任何值(也便是说能够等待任何值),假如等待的是Promise对象,则赶回Promise的管理结果;假设是别的值,则赶回该值自个儿。并且await会暂停当前async
function的施行,等待Promise的管理完了。若Promise常常管理(fulfillded),其将回调的resolve函数参数作为await表达式的值,继续试行async
function;若Promise管理非凡,await表明式会把Promise相当原因抛出;此外假设await操作符前面包车型大巴表达式不是二个Promise对象,则赶回该值自己。

View Code

答案是:不是,有async关键字只是告诉编写翻译器作者那些办法能够用await。实际上没发起任何异步操作,

那便是说当async方法重临Task后,接着await,那被await的Task是一个怎么样概念?是async方法中第二个被await的Task?不,它意味着目标async方法的满贯实行,当中包罗被await分割的连接Task,不过不包涵非await变成的多线程实施。

二. 浓密了然async/await

大家来详细说雅培下async/await的作用。await操作符前边能够是放肆值,当是Promise对象的时候,会一噎止餐async
function实施。也便是说,必须得等待await后边的Promise管理到位才干继续:

 function testAsy{   return new Promise(resolve=>{setTimeout => {       resolve;     }, 3000)    }   )}async function testAwt(){      let result =  await testAsy('hello world');  console.log;    // 3秒钟之后出现hello world}testAwt();

await 表达式的演算结果有赖于它等的事物。

假使它等到的不是二个 Promise 对象,这 await
表明式的演算结果正是它等到的事物。

假定它等到的是一个 Promise 对象,await
就忙起来了,它会堵塞前面包车型大巴代码,等着 Promise 对象 resolve,然后拿走
resolve 的值,作为 await 表明式的运算结果。

大家再把地点的代码修改一下,好好体会“阻塞”那个词

 function testAsy{   return new Promise(resolve=>{setTimeout => {       resolve;     }, 3000)    }   )}async function testAwt(){      let result =  await testAsy('hello world');  console.log;    // 3秒钟之后出现hello world  console.log   // 3秒钟之后出现tangj}testAwt();console.log('tangSir')  //立即输出tangSir

那正是 await 必须用在 async 函数中的原因。async
函数调用不会招致堵塞,它在那之中有着的堵塞都被封装在三个 Promise
对象中异步实行。await暂停当前async的实践,所以’tangSir”初始输出,hello
world’和‘tangj’是3分钟后还要出现的。

使用异步方法不自然都以 await,唯有须求按序实践采用await。上边的程序改成并发下载, Task.WaitAll 是等待全部任务成功。

就算再有await也不是用来倡导异步操作的。await仅仅是一个高等的handler。

 

三. async和await轻松利用

地方已经证实了 async 会将其后的函数(函数表明式或
Lambda)的再次来到值封装成二个 Promise 对象,而 await 会等待那几个 Promise
落成,并将其 resolve 的结果回到出来。

现在比如,用 setTimeout模拟耗费时间的异步操作,先来探望不用 async/await
会怎么写

function takeLongTime() {    return new Promise(resolve => {        setTimeout => resolve("long_time_value"), 1000);    });}takeLongTime().then(v => {    console.log("got", v); //一秒钟后输出got long_time_value});

只要改用 async/await 呢,会是那样

function takeLongTime() {    return new Promise(resolve => {        setTimeout => resolve("long_time_value"), 1000);    });}async function test() {    const v = await takeLongTime();    console.log;  // 一秒钟后输出long_time_value}test();

tankLongTime()自身就是回到的 Promise 对象,所以加不加 async结果都无差异。

 

误区贰: 大家一般只用await后跟一个格局比如:

读书笔记,微博头阵。如下代码,在doo是二个赶回Task的async方法,然后在另四个方法test中await调用doo,然后在Main方法中调用test(由于Main方法不一致意加async,所以要求别的加一个async方法来使用await)

4. 处理then链

前方大家说了,async和await是拍卖then链的语法糖,今后大家来看看具体是怎么落到实处的:

1经五个事情,分四个步骤实现,各样步骤都以异步的,而且信赖于上叁个手续的结果。我们照旧用setTimeout来模拟异步操作:

/** * 传入参数 n,表示这个函数执行的时间 * 执行的结果是 n + 200,这个值将用于下一步骤 */function takeLongTime {    return new Promise(resolve => {        setTimeout => resolve(n + 200), n);    });}function step1 {    console.log(`step1 with ${n}`);    return takeLongTime;}function step2 {    console.log(`step2 with ${n}`);    return takeLongTime;}function step3 {    console.log(`step3 with ${n}`);    return takeLongTime;}

近年来用 Promise 格局来达成那八个步骤的管理。

function doIt(){    console.time('doIt');    let time1 = 300;    step1        .then => step2        .then => step3          .then => {            console.log(`result is ${result}`);            console.timeEnd("doIt");        })}doIt();//执行结果为://step1 with 300//step2 with 500//step3 with 700//result is 900//doIt: 1510.2490234375ms

出口结果resultstep3()的参数700 + 200=900doIt()次第推行了多个步骤,一共用了300 + 500 + 700 = 1500毫秒,和console.time()/console.timeEnd()测算的结果1致。

借使用 async/await 来得以落成啊,会是如此:

async function doIt() {    console.time('doIt');    let time1 = 300;    let time2 = await step1;//将Promise对象resolve的值赋给time2    let time3 = await step1;    let result = await step1;    console.log(`result is ${result}`);    console.timeEnd('doIt');}doIt();//执行结果为://step1 with 300//step2 with 500//step3 with 700//result is 900//doIt: 1512.904296875ms

显著大家用async/await简单多了。

      await SomeMethod();

static void Main(string[] args)

伍. Promise管理结果为rejected

await 命令前边的 Promise 对象,运维结果恐怕是 rejected,所以最佳把 await
命令放在 try…catch 代码块中。

async function myFunction() {    try {        await somethingThatReturnAPromise();    } catch {        console.log;    }}//另一种写法async function myFunction() {    await somethingThatReturnAPromise().catch(function {        console.log;    })}

   await不是用来调用方法的前缀。它是用来接收贰个异步的回来值例如Task;

{

能够这样写:

    test();

var result=SomeMethod();

    log(“Main:调用test后”);

await result;

    Thread.Sleep(Timeout.Infinite);

误区3:await的调控流:微软做了汪洋的努力通过await/async关键字,把前面包车型大巴光景颠倒,跳来跳去的操纵流串成大家代码所写的次第,

}

为了加强阅读性,让我们易于调节和测试和支付。很两人(包蕴在此之前的本人)真的认为带有async的函数正是3个线程的依次实践的调控流。

 

实际上不是那样。副一张图介绍下。澳门葡京备用网址 5

//Main方法区别意加async,所以我们用这一个法子运用await

实线箭头为主线程的调节流,虚线箭头为接受异步回调开启的新线程的调节流。再看三个例证。澳门葡京备用网址 6

static async void test()

当今咱们制止了地点的误区能够更轻松的表明了。

{

最后二个介绍才是本文的入眼:

    log(“test: await之前”);

await的高档应用,自定义awaiter。让大家的await能够await1切。

    await doo();

事先我们的await只好这么用的:

    log(“test: await之后”);

await SomeMethod();

}

await Task.delay(5000);

 

或者

//返回Task的async方法

var result=SomeMethod();

static async Task doo()

await result;

{

大家有未有诸如此类用过?

    log(“doo: Task结果:” + await Task.Run(() => {
Thread.Sleep(1000); log(“Task”); return 1; }));

await
5000;//延迟5秒;等于awaitTask.delay(5000);

    log(“doo: Task结果:” + await Task.Run(() => {
Thread.Sleep(1000); log(“Task”); return 2; }));

string s=await
“;

    log(“doo: Task结果:” + await Task.Run(() => {
Thread.Sleep(1000); log(“Task”); return 3; }));

居然足以那样

    Thread.Sleep(1000);

Task.Run(()=>{

    Console.WriteLine(“doo中在Task外的Thread.Sleep实行完结”);

string s=”hello”;

}

this.SomeTextBlock.Text=s;

 

});

//输出方法:展现当前线程的ManagedThreadId

地点的写法确定是会报错的,因为无法从任何线程给UI线程的控件赋值。

static void log(string msg)

自家本人包装了三个线程同步的主意:

{

Task.Run(()=>{

    Console.WriteLine(“{0}: {1}”, Thread.CurrentThread.ManagedThreadId,
msg);

string s=”hello”;

}

await this.SomeTextBlock;

 

this.SomeTextBlock.Text=s;

地点代码会输出:

});

1: test: await之前

await一下以此想要被操作的控件就可以赋值了。

1: Main:调用test后

 是还是不是比相当的甜美?

3: Task

笔者来一步一步告诉大家怎么达成。

3: doo: Task结果:1

await接收的是TaskAwaiter 或TaskAwaiter <T> 的归来值
所以大家定义1个扩充方法能够让await当做自定义类型的前缀。

4: Task

 public static class AwaitHelper
    {
        //public static TaskAwaiter GetAwaiter(this TimeSpan timeSpan)
        //{

        //    return TaskEx.Delay(timeSpan).GetAwaiter();
        //}
        public static TaskAwaiter GetAwaiter(this int ms)
        {
            return Task.Delay(ms).GetAwaiter();
        }

        public static TaskAwaiter GetAwaiter(this string s)
        {
            return new Task(() => { }).GetAwaiter();
        }

        public static AllanControlAwaiter GetAwaiter(this DependencyObject control)
        {
            return new AllanControlAwaiter(control);
        }
    }

4: doo: Task结果:2

来看此间大家就精晓

3: Task

await 5000;//延迟5秒;

3: doo: Task结果:3

是怎么得以完结的啊。

澳门葡京备用网址 ,doo中在Task外的Thread.Sleep实行实现

上边大家一而再封装3个AllanControlAwaiter实现线程同步操作

3: test: await之后

 public class AllanControlAwaiter : INotifyCompletion
    {
        private readonly DependencyObject m_control;
        private bool _IsCompleted = false;
        public AllanControlAwaiter(DependencyObject control)
        {
            m_control = control;
        }

        public bool IsCompleted
        {
            get { return _IsCompleted; }
        }

        public void OnCompleted(Action continuation)
        {
            var result = m_control.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => continuation());
            result.AsTask().Wait();
            _IsCompleted = true;
        }

        public void GetResult() { }


    }

 

到那边大功告成。

前两句轻巧,调用test方法,await后的剧情会被加在目标Task的背后,然后test马上再次回到,于是输出“Main:调用test后”,同时他们都以在主线程中施行的,所以ManagedThreadId都以壹。

快去体验一下可以await一切的认为吗!

 

继而后边正是另多个Task的进行(当然在另3个线程,也是test方法中await的对象Task)。那些所谓的Task正是doo方法的1体施行。所以doo中多少个顺序推行的Task(通过await二个3个总是)依次实施,所以Task输出结果一,二,三。第5个Task的ManagedThreadId是三,第二个是肆,第四个又是三,原因是Task的里边施行使用了CL帕Jero的线程池,所以线程获得了再次使用。

 

接着doo方法还从未完,最终一个await产生doo方法前面包车型大巴代码在那几个await针对的Task试行后继续实践,于是输出:doo中Task外的Thread.Sleep试行落成。

 

提及底当doo通透到底推行完test的await才结束,所以最终1行输出:test:await之后。

 

 

地点小编说过:被await的async方法再次来到的Task代表“目标async方法的方方面面进行,个中包罗被await分割的连接Task,然则不包蕴非await产生的三十二线程试行”。

于是1旦把再次回到Task的async方法(也等于上例中的doo方法)改成那样:

//返回Task的async方法

static async Task doo()

{

    log(“doo: Task结果:” + await Task.Run(() => {
Thread.Sleep(1000); log(“Task”); return 1; }));

    log(“doo: Task结果:” + await Task.Run(() => {
Thread.Sleep(1000); log(“Task”); return 2; }));

    log(“doo: Task结果:” + await Task.Run(() => {
Thread.Sleep(1000); log(“Task”); return 3; }));

 

    //不使用await:线程池八线程

    ThreadPool.QueueUserWorkItem(_ =>

        {

            Thread.Sleep(1000);

            Console.WriteLine(“ThreadPool.QueueUserWorkItem”);

        });

 

    //不使用await:Task多线程

    Task.Run(() =>

        {

            Thread.Sleep(1000);

            Console.WriteLine(“Task.Run”);

        });

}

 

咱们进入了不要await的二10拾二线程试行,分别选拔ThreadPool和Task,整个程序会输出那样的结果:

1: test: await之前

1: Main:调用test后

3: Task

3: doo: Task结果:1

4: Task

4: doo: Task结果:2

3: Task

3: doo: Task结果:3

3: test: await之后

Task.Run

ThreadPool.QueueUserWorkItem

 

不选拔await的102线程完全剥离了test方法中await的Task,是运维在test的await之后的。

 

别的Visual Studio会对Task.Run代码做如下警告:
  澳门葡京备用网址 7

 

提示:Because this call is not awaited, execution of the current method
continues before the call is completed. Consider applying the ‘await’
operator to the result of the call.

身为,假若不加await,当前方法会继续实行直到甘休,不用管她,因为我们今后正是在做在async方法中不用await的测试,呵呵。www.二cto.com

 

 

大概你会问,为啥要用那样的方法去await另1个async方法重回的Task呢?大家间接在研究再次来到Task的async方法,作者认为看二个回来Task<T>的async方法能够越来越好地解释那一个标题。

 

下边大家把地点的代码改成形似的回到Task<int>的async方法施行,那么doo方法再次来到Task<T>,他把温馨形式内三个awaited
Task的结果统一相加,最终回到结果并作为协和回来的Task的结果。然后在test方法中输出doo再次来到的结果。

 

完全代码:

static void Main(string[] args)

{

    test();

    log(“Main:调用test后”);

    Thread.Sleep(Timeout.Infinite);

}

 

//Main方法不允许加async,所以我们用那些办法应用await

static async void test()

{

    log(“test: await之前”);

    Console.WriteLine(“doo结果:{0}”, await doo());

    log(“test: await之后”);

}

 

//返回Task的async方法

static async Task<int> doo()

{

    var res1 = await Task.Run(() => { Thread.Sleep(1000);
log(“awaited Task1执行”); return

    var res2 = await Task.Run(() => { Thread.Sleep(1000);
log(“awaited Task2执行”); return

    var res3 = await Task.Run(() => { Thread.Sleep(1000);
log(“awaited Task3执行”); return

 

    //不应用await:线程池多线程

    ThreadPool.QueueUserWorkItem(_ =>

        {

            Thread.Sleep(1000);

            Console.WriteLine(“ThreadPool.QueueUserWorkItem”);

        });

 

    //不使用await:Task多线程

    Task.Run(() =>

        {

            Thread.Sleep(1000);

            Console.WriteLine(“Task.Run”);

        });

 

    return res1 + res2 + res3;

}

 

//输出方法:展现当前线程的ManagedThreadId

static void log(string msg)

{

    Console.WriteLine(“{0}: {1}”, Thread.CurrentThread.ManagedThreadId,
msg);

}

 

先看结果:

1: test: await之前

1: Main:调用test后

3: awaited Task1执行

4: awaited Task2执行

4: awaited Task3执行

doo结果:6

4: test: await之后

ThreadPool.QueueUserWorkItem

Task.Run

 

 

和上一个回到Task的事例同样,当在test方法中await
doo方法重返的Task,doo内awaited
Task都被先等了,而从不awaited的线程都并未被等,这是为什么呢(也等于上面留下的不胜标题)?下边用那一个重返Task<int>的例子解释一下:

在test中await
doo重返的Task,那么此时我们须求他的结果,而他的结果是亟需和煦方式内所富含的其余awaited结果,能够知晓成被等的子结果。所以本身的结果要求其余的结果,那么等这些结果必须需求等那一个被重视的结果也出来。所以test方法await
doo方法的结果会同样等待全部doo内的await,不会管其余doo内非await的拾二线程实行(当然从本事角度讲,也是不容许的,因为async/await能够如此全靠的是编写翻译器)。
 

摘自 Mgen

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website