async/await剖析
async/await剖析
JavaScript是单线程的,为了避免同步阻塞可能会带来的一些负面影响,引入了异步非阻塞机制,而对于异步执行的解决方案从最早的回调函数,到ES6的Promise对象以及Generator函数,每次都有所改进,但是却又美中不足,他们都有额外的复杂性,都需要理解抽象的底层运行机制,直到在ES7中引入了async/await,他可以简化使用多个Promise时的同步行为,在编程的时候甚至都不需要关心这个操作是否为异步操作。
分析
首先使用async/await执行一组异步操作,并不需要回调嵌套也不需要写多个then方法,在使用上甚至觉得这本身就是一个同步操作,当然在正式使用上应该将await语句放置于 try...catch代码块中,因为await命令后面的Promise对象,运行结果可能是rejected。
function promise(){
return new Promise((resolve, reject) => {
var rand = Math.random() * 2;
setTimeout(() => resolve(rand), 1000);
});
}
async function asyncFunct(){
var r1 = await promise();
console.log(1, r1);
var r2 = await promise();
console.log(2, r2);
var r3 = await promise();
console.log(3, r3);
}
asyncFunct();
async/await实际上是Generator函数的语法糖,如Promises类似于结构化回调,async/await在实现上结合了Generator函数与Promise函数,下面使用Generator函数加Thunk函数的形式实现一个与上边相同的例子,可以看到只是将async替换成了*放置在函数右端,并将await替换成了yield,所以说async/await实际上是Generator函数的语法糖,此处唯一不同的地方在于实现了一个流程的自动管理函数run,而async/await内置了执行器,关于这个例子的实现下边会详述。对比来看,async和await,比起*和yield,语义更清楚,async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
function thunkFunct(index){
return function f(funct){
var rand = Math.random() * 2;
setTimeout(() => funct(rand), 1000)
}
}
function* generator(){
var r1 = yield thunkFunct();
console.log(1, r1);
var r2 = yield thunkFunct();
console.log(2, r2);
var r3 = yield thunkFunct();
console.log(3, r3);
}
function run(generator){
var g = generator();
var next = function(data){
var res = g.next(data);
if(res.done) return ;
// console.log(res.value);
res.value(next);
}
next();
}
run(generator);
实现
async函数内置了执行器,能够实现函数执行的自动流程管理,通过Generator yield Thunk、Generator yield Promise实现一个自动流程管理,只需要编写Generator函数以及Thunk函数或者Promise对象并传入自执行函数,就可以实现类似于async/await的效果。
Generator yield Thunk
自动流程管理run函数,首先需要知道在调用next()方法时,如果传入了参数,那么这个参数会传给上一条执行的yield语句左边的变量,在这个函数中,第一次执行next时并未传递参数,而且在第一个yield上边也并不存在接收变量的语句,无需传递参数,接下来就是判断是否执行完这个生成器函数,在这里并没有执行完,那么将自定义的next函数传入res.value中,这里需要注意res.value是一个函数,可以在下边的例子中将注释的那一行执行,然后就可以看到这个值是f(funct){...},此时我们将自定义的next函数传递后,就将next的执行权限交予了f这个函数,在这个函数执行完异步任务后,会执行回调函数,在这个回调函数中会触发生成器的下一个next方法,并且这个next方法是传递了参数的,上文提到传入参数后会将其传递给上一条执行的yield语句左边的变量,那么在这一次执行中会将这个参数值传递给r1,然后在继续执行next,不断往复,直到生成器函数结束运行,这样就实现了流程的自动管理。
function thunkFunct(index){
return function f(funct){
var rand = Math.random() * 2;
setTimeout(() => funct(rand), 1000)
}
}
function* generator(){
var r1 = yield thunkFunct();
console.log(1, r1);
var r2 = yield thunkFunct();
console.log(2, r2);
var r3 = yield thunkFunct();
console.log(3, r3);
}
function run(generator){
var g = generator();
var next = function(data){
var res = g.next(data);
if(res.done) return ;
// console.log(res.value);
res.value(next);
}
next();
}
run(generator);
Generator yield Promise
相对于使用Thunk函数来做流程自动管理,使用Promise来实现相对更加简单,Promise实例能够知道上一次回调什么时候执行,通过then方法启动下一个yield,不断继续执行,这样就实现了流程的自动管理。
function promise(){
return new Promise((resolve,reject) => {
var rand = Math.random() * 2;
setTimeout( () => resolve(rand), 1000);
})
}
function* generator(){
var r1 = yield promise();
console.log(1, r1);
var r2 = yield promise();
console.log(2, r2);
var r3 = yield promise();
console.log(3, r3);
}
function run(generator){
var g = generator();
var next = function(data){
var res = g.next(data);
if(res.done) return ;
res.value.then(data => next(data));
}
next();
}
run(generator);
// 比较完整的流程自动管理函数
function promise(){
return new Promise((resolve,reject) => {
var rand = Math.random() * 2;
setTimeout( () => resolve(rand), 1000);
})
}
function* generator(){
var r1 = yield promise();
console.log(1, r1);
var r2 = yield promise();
console.log(2, r2);
var r3 = yield promise();
console.log(3, r3);
}
function run(generator){
return new Promise((resolve, reject) => {
var g = generator();
var next = function(data){
var res = null;
try{
res = g.next(data);
}catch(e){
return reject(e);
}
if(!res) return reject(null);
if(res.done) return resolve(res.value);
Promise.resolve(res.value).then(data => {
next(data);
},(e) => {
throw new Error(e);
});
}
next();
})
}
run(generator).then( () => {
console.log("Finish");
});
每日一题
https://github.com/WindrunnerMax/EveryDay
参考
https://segmentfault.com/a/1190000007535316
http://www.ruanyifeng.com/blog/2015/05/co.html
http://www.ruanyifeng.com/blog/2015/05/async.html
async/await剖析的更多相关文章
- 探索c#之Async、Await剖析
阅读目录: 基本介绍 基本原理剖析 内部实现剖析 重点注意的地方 总结 基本介绍 Async.Await是net4.x新增的异步编程方式,其目的是为了简化异步程序编写,和之前APM方式简单对比如下. ...
- C# 探索c#之Async、Await剖析
探索c#之Async.Await剖析 作者:蘑菇先生 出处:http://mushroom.cnblogs.com/
- c#之Async、Await剖析
c#之Async.Await剖析 探索c#之Async.Await剖析 2015-06-15 08:35 by 蘑菇先生, 1429 阅读, 5 评论, 收藏, 编辑 阅读目录: 基本介绍 基本原理剖 ...
- 聊聊多线程那一些事儿 之 五 async.await深度剖析
hello task,咱们又见面啦!!是不是觉得很熟读的开场白,哈哈你哟这感觉那就对了,说明你已经阅读过了我总结的前面4篇关于task的文章,谢谢支持!感觉不熟悉的也没有关系,在文章末尾我会列出前四 ...
- [.NET] 利用 async & await 的异步编程
利用 async & await 的异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/5922573.html 目录 异步编程的简介 异 ...
- [.NET] 怎样使用 async & await 一步步将同步代码转换为异步编程
怎样使用 async & await 一步步将同步代码转换为异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6079707.html ...
- [.NET] 利用 async & await 进行异步 IO 操作
利用 async & await 进行异步 IO 操作 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6082673.html 序 上次,博主 ...
- [C#] 走进异步编程的世界 - 开始接触 async/await
走进异步编程的世界 - 开始接触 async/await 序 这是学习异步编程的入门篇. 涉及 C# 5.0 引入的 async/await,但在控制台输出示例时经常会采用 C# 6.0 的 $&qu ...
- [C#] 走进异步编程的世界 - 开始接触 async/await(转)
原文链接:http://www.cnblogs.com/liqingwen/p/5831951.html 走进异步编程的世界 - 开始接触 async/await 序 这是学习异步编程的入门篇. 涉及 ...
随机推荐
- SpringBoot外部化配置使用Plus版
本文如有任何纰漏.错误,请不吝指正! PS: 之前写过一篇关于SpringBoo中使用配置文件的一些姿势,不过嘛,有句话(我)说的好:曾见小桥流水,未睹观音坐莲!所以再写一篇增强版,以便记录. 序言 ...
- eatwhatApp开发实战(二)
上期,我们做了个小app“eatwhat”,接下来每期都会为其添加新的功能.本期,我们为店铺增加添加店铺的功能. 还是先设置个布局: <RelativeLayout android:layout ...
- [工具-002]把png图片转换成ico图标
最近我收到了上级的一个需求,我们需要使用产品的png图片,批量转换成ico图片,然后调用上一篇的方法,替换可执行程序的图标.一开始查看资料的时候,C#有直接可以转成ico图片的方法,很简单.但是生成的 ...
- 读Pyqt4教程,带你入门Pyqt4 _006
窗口组件是应用程序的基本构建块.PyQt4编程工具包拥有范围广泛的各种窗口组件.按钮.选择框.滑块.列表框等等,程序员工作所需要的一切.在教程的这部分中,我们将介绍一些有用的窗口组件. QCheckB ...
- 小谢第2问:后端返回为数组list时候,怎么实现转为tree
要求后端返回给我的list时候,在数组中定义有id , parentid, 可以用双重循环的方法,得到tree需要的数据结构,这样得到的数据就可以直接复制给树组件的data啦const oldData ...
- Java实现 LeetCode 817 链表组件(暴力)
817. 链表组件 给定一个链表(链表结点包含一个整型值)的头结点 head. 同时给定列表 G,该列表是上述链表中整型值的一个子集. 返回列表 G 中组件的个数,这里对组件的定义为:链表中一段最长连 ...
- Java实现 LeetCode 783 二叉搜索树节点最小距离(遍历)
783. 二叉搜索树节点最小距离 给定一个二叉搜索树的根节点 root,返回树中任意两节点的差的最小值. 示例: 输入: root = [4,2,6,1,3,null,null] 输出: 1 解释: ...
- Java实现 LeetCode 715 Range 模块(选范围)
715. Range 模块 Range 模块是跟踪数字范围的模块.你的任务是以一种有效的方式设计和实现以下接口. addRange(int left, int right) 添加半开区间 [left, ...
- Java实现 LeetCode 482 密钥格式化
482. 密钥格式化 给定一个密钥字符串S,只包含字母,数字以及 '-'(破折号).N 个 '-' 将字符串分成了 N+1 组.给定一个数字 K,重新格式化字符串,除了第一个分组以外,每个分组要包含 ...
- Java实现 LeetCode 344 反转字符串
344. 反转字符串 编写一个函数,其作用是将输入的字符串反转过来.输入字符串以字符数组 char[] 的形式给出. 不要给另外的数组分配额外的空间,你必须原地修改输入数组.使用 O(1) 的额外空间 ...