异步编程学习笔记
(这里只提供简单总结,C#详细的异步编程另开)
JS
回调函数
如JS的SetInterval() setTimeout()等
传统的实现异步的回调函数如果需要多个异步操作串联就会导致代码右移,层数非常多
为了解决回调地狱(回调嵌套导致代码复杂度高),引入了异步函数和await async标识符
Promise对象、fetch函数等(异步函数)
可以串联,避免了异步串联的多层嵌套,还能支持catch/finally
fetch('url') //跨网络异步获取资源
.then((response)=> response.json())
.then((json) => {
console.log(json);
})
.catch((error)=>{
console.log(error);
})
.finally(() =>{
//sth
});
当接收到一个代表错误的 HTTP 状态码时,从 fetch() 返回的 Promise 不会被标记为 reject,即使响应的 HTTP 状态码是 404 或 500。
-相反,它会将 Promise 状态标记为 resolve (如果响应的 HTTP 状态码不在200 - 299 的范围内,则设置 resolve 返回值的 ok 属性为 false ),
-仅当网络故障时或请求被阻止时,才会标记为 reject。
fetch 不会发送跨域 cookies,除非你使用了 credentials 的初始化选项
await/async标识符[js ECMA17标准引入]
async修饰的为异步函数,内部必须有异步行为(可以不生效或被拦截式返回)
async function f(){
const response = await fetch("url");
}
f();
Promise对象
async function f(){
const promiseA = await fetch("url1");
const promiseB = await fetch("url2");
const [a,b] = await Promise.all([promiseA,promiseB]);
//...
}
for await()
为了方便实用,js提供了支持等待异步回调的增强for
const promises = [
someAsyncOperation(),
someAsyncOperation(),
someAsyncOperation()
];
for await (let result of promises){
//...
}
注意
异步函数不支持forEach()、map()等方法,因为这些方法立刻就会返回结果,
-不等异步的结果回传或给Result赋值
想要循环可以使用传统的for循环或者使用异步for: for await()
不能在全局或者普通函数中直接使用async await异步赋值
如果必须用,也可以先定义一个异步函数,然后在异步函数中用关键字赋值
旧版JS项目如果想用异步,可以在新版的环境中写好代码用版本转译器翻译代码
C#
Thread(回调)
回调地狱改为由Thread触发(只能嵌套不能串联)
虽然开始线程的时候可以方便地传入数据,但是当Join的时候,很难从线程获得返回值
可能需要设置一些共享字段
如果操作要抛出异常,捕获和传播该异常都很麻烦
无法告诉线程在结束时开始做另外的工作,你必须进行Join操作(在进程中阻塞当前线程)
很难使用较小的并发(concurrent)来组建大型的并发
导致了对手动同步的更大依赖以及随之而来的问题
Task
Task是一个相对高级的抽象抽象:他代表了一个并发操作(concurrent)
Task是可组合的(可使用Continuation把他们穿成链)
执行
Task.Run(()=> sth)
返回值
Task<TResult> result = Task.Run(...);
异常
比Thread容易出异常,就会被抛出给:
调用了Wait()的地方
访问了Task<TResult>的Result属性的地方
异常包裹在AggregateException里
Task.IsCanceled为true,说明一个OperationCanceledException为该Task抛出了
Task.IsFaulted为True,说明另一个类型的异常被抛出了,而Exception属性也将指明错误
Continuation
Task结束后再执行一些事(可以用Task的参数)//需要实现接口使用返回值,复杂
ContinuaWith()
另外一种附加Continuation的方式就是调用task的ContinueWith()方法
Task.ContinuaWith()
.ContinuaWith()
.ContinuaWith()
...
同步上下文
能够让你读取和控制当前异步上下文的静态成员。当前同步上下文是当前线程的一个属性。
-其思想是在特殊线程中运行的任何时刻,你都能够去获取当前线程并且存储它
同步上下文出现了,OnCompleted自动捕获它,将Continuation提交到这个上下文中
Task.Delay()
相当于异步的Thread.sleep()
await/async标识符
与js用法相同,基于Task的INotifyConpletion接口或属性方法
异步Lambda表达式
匿名方法(包括lambda表达式),通过使用async也可以变成异步方法
myButton.Click += async() => {
await Task.Delay(1000);
myButton.Content = "Done";
}
取消执行
cancellation
使用取消标志来实现对并发操作进行取消,可以封装一个类以供异步操作取消异步请求
delay
CLR里大部分的异步方法都支持CancellationToken,包括Delay方法
...Foo(CancellationToken cancellationToken){
......Task.Delay(1000,cancellationToken); ....
}
进度报告
即获取异步任务的进度在 XX%,比较复杂,用到再去查
Task组合器
Task.WhenAny
当一组Task中任何一个Task完成时,Task.WhenAny会返回完成的Task
Task<> winningTask = await Task.WhenAny(Delay1(),Delay2(),Delay3());
Console.WriteLine("Done");
Console.WriteLine(winningTask.Result); //1
因为Task.WhenAny本身就返回一个Task,我们对它进行await,就会返回最先完成的Task
Task.WhenAll
当传给他的所有的Task都完成后,Task.WhenAll会返回一个Task
await Task.WhenAll(Delay1(),Delay2(),Delay3());
可自定义Task组合器,需要时再去查

浙公网安备 33010602011771号