ES2017 标准引入了 async 函数,使得异步操作变得更加方便。

async 函数是什么?一句话,它就是 Generator 函数的语法糖。研究 async 的原理,就必须先弄清楚 Generator 是个啥。

Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。

形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)

看一个例子:

function* gen(x) {
var y = yield x + 2;
return y;
}

var g = gen(1);
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }

上面代码中,调用 Generator 函数,会返回一个内部指针(即遍历器)g。这是 Generator 函数不同于普通函数的另一个地方,即执行它不会返回结果,返回的是指针对象。调用指针g的next方法,会移动内部指针(即执行异步任务的第一段),指向第一个遇到的yield语句,上例是执行到x + 2为止。

换言之,next方法的作用是分阶段执行Generator函数。每次调用next方法,会返回一个对象,表示当前阶段的信息(value属性和done属性)。value属性是yield语句后面表达式的值,表示当前阶段的值;done属性是一个布尔值,表示 Generator 函数是否执行完毕,即是否还有下一个阶段。

这样手工的执行next()函数,着实有些麻烦,能写个工具让他自动执行吗?那我们就来试试:

封装一个 spawn 函数,返回一个 spawn 函数,给函数传入 Generator函数作为参数,spawn 实现 next() 方法的执行。

function fn(args) {
return spawn(function* () {
// ...
});
}

spawn 函数的实现:

function spawn(genF) {
return new Promise(function(resolve, reject) {
const gen = genF();
function step(nextF) {
let next;
try {
next = nextF();
} catch(e) {
return reject(e);
}
if(next.done) {
return resolve(next.value);
}
Promise.resolve(next.value).then(function(v) {
step(function() { return gen.next(v); });
}, function(e) {
step(function() { return gen.throw(e); });
});
}
step(function() { return gen.next(undefined); });
});
}

应用这个方法执行一下第一个例子:

function fn(x) {
return spawn(function* gen() {
var y = yield x + 2
return y;
});
}

fn(1).then((result) => {
console.log(result) // 3
})

如果 yield 后面是个 Promise, 就可以实现异步了:

function fn(x) {
return spawn(function* gen() {
var y = yield new Promise((resolve) => {
setTimeout(() => {
resolve(x + 1)
}, 1000)
})
return y;
});
}

fn(1).then((result) => {
console.log(result) // 过一秒后打印 3
})

这样,过一秒后就打印 3 了。

从整个代码上来看,实现起来有些麻烦。Async 简化了一切,使用它,不再需要 spawn 函数,只需将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。改造一下:

async function fn(x) {
let result = await new Promise((resolve) => {
setTimeout(() => {
resolve(x + 2)
}, 1000)
})
return result
}

fn(1).then((result) => {
console.log(result)
})

真是简洁了很多。

最后看一个面试题,如何将程序的执行结果 1,3,2,改造为 1,2, 3

<script>
const getUser = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(2)
}, 0)
})
}

export default {
methods: {
async onGetUser() {
getUser().then((result) => {
console.log(result)
})
}
},

async created() {
console.log(1)
await this.onGetUser()
console.log(3)
}
}
</script>

<template>
<div>
hello world
</div>
</template>

<style lang="scss"> </style>

只需修改一个 onGetUser 函数即可:

async onGetUser() {
// getUser().then((result) => {
// console.log(result)
// })
let result = await getUser()
console.log(result)
}

以上就是async的原理,你学会了吗?

5分钟带你彻底搞懂async底层实现原理!的更多相关文章

  1. 8分钟带你深入浅出搞懂Nginx

    Nginx是一款轻量级的Web服务器.反向代理服务器,由于它的内存占用少,启动极快,高并发能力强,在互联网项目中广泛应用. 图基本上说明了当下流行的技术架构,其中Nginx有点入口网关的味道. 反向代 ...

  2. C++ 一篇搞懂多态的实现原理

    虚函数和多态 01 虚函数 在类的定义中,前面有 virtual 关键字的成员函数称为虚函数: virtual 关键字只用在类定义里的函数声明中,写函数体时不用. class Base { virtu ...

  3. C# 彻底搞懂async/await

    前言 Talk is cheap, Show you the code first! private void button1_Click(object sender, EventArgs e) { ...

  4. 一文带你快速搞懂动态字符串SDS,面试不再懵逼

    目录 redis源码分析系列文章 前言 API使用 embstr和raw的区别 SDSHdr的定义 SDS具体逻辑图 SDS的优势 更快速的获取字符串长度 数据安全,不会截断 SDS关键代码分析 获取 ...

  5. 十分钟搞懂Lombok使用与原理

    1 简介 Lombok是一款好用顺手的工具,就像Google Guava一样,在此予以强烈推荐,每一个Java工程师都应该使用它.Lombok是一种Java™实用工具,可用来帮助开发人员消除Java的 ...

  6. 面试都在问的微服务、服务治理、RPC、下一代微服务框架... 一文带你彻底搞懂!

    文章每周持续更新,「三连」让更多人看到是对我最大的肯定.可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇) 单体式应用程序 与微服务相对的另一个概念是传统的单体式应用程序( ...

  7. 面试都在问的「微服务」「RPC」「服务治理」「下一代微服务」一文带你彻底搞懂!

    ❝ 文章每周持续更新,各位的「三连」是对我最大的肯定.可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇) ❞ 单体式应用程序 与微服务相对的另一个概念是传统的「单体式应用程 ...

  8. 十分钟搞懂Elasticsearch数字搜索原理

    更多精彩内容请看我的个人博客或者扫描二维码,关注微信公众号:佛西先森 前言 Elasticsearch诞生的本意是为了解决文本搜索太慢的问题,ES会默认将所有的输入内容当作字符串来理解,对于字段类型是 ...

  9. 一文带大家彻底搞懂Hystrix!

    前言? Netflix Hystrix断路器是什么? Netflix Hystrix是SOA/微服务架构中提供服务隔离.熔断.降级机制的工具/框架.Netflix Hystrix是断路器的一种实现,用 ...

  10. Spirit带你彻底搞懂JS的6种继承方案

    JavaScript中实现继承的6种方案 01-原型链的继承方案 function Person(){ this.name="czx"; } function Student(){ ...

随机推荐

  1. 【BOOK】正则表达式

    正则表达式 1. 开源中国-正则表达式测试工具:https://tool.oschina.net/regex/ 2. 匹配规则 3. match() 从字符串起始位置匹配正则表达式 若从起始位置匹配不 ...

  2. Linux 第三节(重定向符,通配符,管道符,转义符,VIM编辑器)

    1.输入重定向符 < 2.输出重定向符 将我们的命令原本要输出到屏幕的内容,输出到文件里面 标准信息 >  覆盖>  追加>> 错误信息 2>  覆盖2>  ...

  3. web测试:test过程中接口报错 "Object reference not set to an instance of an object."

    "Object reference not set to an instance of an object." 对象引用未设置为对象的实例 可能原因: 1.参数类型传错,或少传参数 ...

  4. HTTP和HTTPS的定义和区别

    http是什么? 超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上.它指定了客户端可能发送给服务器什么样的消息以及 ...

  5. SQL查询 错误 [1843] [22008]: ORA-01843: 无效的月份

    dbeaver客户端运行sql查询Oracle库报错. 正确示例: select count(*) from PRODUCTS WHERE CREATE_TIME > '15-7月-2021 ' ...

  6. Spring之IOC(控制反转)入门理解

    在面向对象编程中,我们经常处理处理的问题就是解耦,程序的耦合性越低表明这个程序的可读性以及可维护性越高(假如程序耦合性过高,改一处代码通常要对其他地方也要做大量修改,难以维护).控制反转(Invers ...

  7. python 深拷贝及浅拷贝区别

    深拷贝及浅拷贝区别 浅拷贝copy: 可变类型:(列表,字典,集合)copy函数对可变类型的第一层对象进行拷贝,对拷贝的对象开辟新的内存空间进行存储,不会拷贝对象内部的子对象 不可变类型:(数字,字符 ...

  8. oracle转义单引号

    --笔记开始: 每次从表中取数据,然后都要在excel中添加单引号,再粘到查询语句中进行查询比较麻烦. 所以能在查出的结果直接加上单引号是很省时间的 . 这里用到转义. tablename: ep: ...

  9. python Elementtree 生成带缩进格式的xml文件

    示例 之前拿ET写xml,直接对root节点调用write函数,会出现产生的xml字符串没有缩进,是干巴巴的一行,可读性比较差,就像下面这样: <annotation><filena ...

  10. redis 简单安装

    参考官网,安装步骤基本照搬redis官网,其他只做简单说明https://redis.io/download/https://redis.io/docs/getting-started/install ...