JavaScript 异步函数解析
前言
在学习 JavaScript
的过程中,理解并灵活运用异步相关知识是一件不容易的事情,这体现在代码可读性、健壮性上,好在 ES6
出现后挽回了这一局面,我们不再需要编写可读性不高的回调嵌套,也不用为了代码的健壮性而处处小心,这得益于 Promise
与 async Function
,它们给我们带来了更优秀的异步方案,今天我们就来学习异步函数相关知识。
async Function
虽然 Promise
对于传统的异步编程而言已经足够优秀,但精益求精的前辈们仍觉不足,Promise
的代码执行顺序对于开发者而言依然不够直观,为次,ES6
又提出了异步函数的相关概念。
异步函数,即一个特殊函数,通过在函数声明前添加 async
关键字标识
async function () {}// orconst asyncArrow = async () => {}
在异步函数中,能够使用关键词 await
,它期待一个 Promise
值,并等待它决议,此时会暂停当前的程序并返回,控制权交还给主线程去执行其他任务
async function asyncFun() {await Promise.resolve();console.log('end');
}asyncFun();
console.log('start');
上述代码中的正确执行顺序是 start
、end
,这意味着函数调用后异步函数内的程序被暂停了,所以首先输出 start
,然后输出 end
,它的行为如下
1.调用异步函数
2.等待一个立即完成的 Promise
3.给立即完成的 Promise
添加一个微任务
4.返回
5.输出 start
6.微任务执行,asyncFun
程序恢复执行
await
虽然期待一个 Promise
值,但这不是必须的,它可以等待任何值,此时浏览器会将这个值包装为一个 Promise
async function asyncFun() {await 1;// 类似于await Promise.resolve(1);
}
await
还能够获取到 Promise
的完成值
async function asyncFun() {const result = await Promise.resolve('fulfilled');console.log(result);
}asyncFun(); // fulfilled
如果等待的 Promise
被拒绝,那么就会在程序停止的位置抛出异常,我们能够通过 try...catch
进行异常捕获
async function asyncFun() {try {await Promise.reject('出错了');} catch(err) {console.log(err); // 出错了}
}asyncFun();
异步函数的返回值会被包装成一个 Promise
,如果没有显示的返回一个值,那么 Promise
完成值就是隐式返回的 undefined
async function asyncFun() {return 'complete';
}asyncFun().then((value) => {console.log(value); // complete})
看到这里,其实异步函数的一些特性就讲完了,但是你有没有发现异步函数与生成器有很多相像的地方呢?
// async
async function asyncFun() {await Promise.resolve();
}// generator
function *generator() {yield Promise.resolve();
}
异步函数通过 async
来标识,而生成器通过在 function
关键字后添加 *
来标识,且它们都有各自的关键字能用于暂停程序的执行,但仅仅如此吗?看一下下面的代码
function *generator() {const result = yield Promise.resolve('generator');console.log(result); // generator
}function run(exec) {// 假设 exec 必定是一个生成器// 获取生成器对象const g = exec();// 启动生成器,获取 yield、return 出的值const p = g.next();// 假设 p.value 必定是一个 Promisep.value.then(value => g.next(value),err => g.throw(err));
}run(generator);
上述代码中,我们添加了一个 run
函数,用于控制生成器的执行,run
函数利用 yield
双向数据传递的特性,接受生成器返回的值,这里我们假定为 Promise
值,并给它添加一个 then
处理回调,当这个 Promise
完成时在成功回调中调用生成器对象的 next
方法并将完成值注入到上一个导致程序暂停的 yield
身上,如果失败则调用 throw
方法并注入失败原因。
现在你发现了吗?其实异步函数只是一个语法糖,基于 Promise
+ 生成器我们也能实现完全相同的功能,所缺的只是一个控制生成器执行流程的执行器而已。
实现执行器
我们已经了解了异步函数其实只是 Promise
+ 生成器的语法糖,但还缺少了一个关键的执行器函数,以下是摘抄自 《你不知道的JavaScript》 的实现
function run(exec, ...args) {// 调用生成器函数并传递参数const it = exec.apply(this, args);return Promise.resolve().then(function handleNext(value) { // 不断处理 next 调用const next = it.next(value); // 获取 next 返回值// 立即执行函数,传递 next 返回值return (function handleResult(next) {if (next.done) { // 执行完毕直接返回return next.value;} else {// 未执行完毕则给 yield 出的 Promise 添加then处理回调// 同时避免 yield 非 Promise 值,需要进行一层 resolve 包装return Promise.resolve(next.value).then(handleNext, // 成功回调,继续调用 handleNext 获取下一个值function handleErr(err) { // 失败回调return Promise.resolve(it.throw(err) // 注入异常,如果异常在生成器内部被处理则继续调用 handleResult).then(handleResult);})}})(next);})
}
拥有了这个执行器函数,我们就能够基于 Promise
+ 生成器来模拟异步函数的行为了
function *generator() {try {const result = yield Promise.reject('出错啦');} catch(err) {console.log(err); // 出错啦}const data = yield Promise.resolve('data');console.log(data);return 'complete';
}run(generator).then(value => {console.log(value); // complete})// 出错啦
// data
// complete
结语
本文讲述了异步函数的相关概念,并了解了异步函数其实只是语法糖,我们完全能够自己实现相同的功能,可以看到,生成器在这中间充当了非常重要的角色,以后可能还会基于生成器出现更多强大的模式。
最后
整理了75个JS高频面试题,并给出了答案和解析,基本上可以保证你能应付面试官关于JS的提问。
有需要的小伙伴,可以点击下方卡片领取,无偿分享
JavaScript 异步函数解析相关推荐
- 细说JavaScript异步函数发展历程
2019独角兽企业重金招聘Python工程师标准>>> < The Evolution of Asynchronous JavaScript >外文梳理了JavaScri ...
- async js 返回值_获取JavaScript异步函数的返回值
今天研究一个小问题: 怎么拿到JavaScript异步函数的返回值? 1.错误尝试 当年未入行时,我的最初尝试: function getSomething() { var r = 0; setTim ...
- javascript --- 异步函数的顺序进行
假设我们希望某一组异步函数能一次进行,在不使用的任何工具的情况下,可能会编写出类似下面的代码: funcs[0](function() {funcs[1](function() {funcs[2](o ...
- JavaScript异步函数同步方法
首先这里非常感谢慕课网蜗牛老湿,本文内容是基于老师讲解koa2实现原理的时候整理出来的,好吧,这里直接进入主题. 在我们平时进行前端开发的时候,经常需要用到异步函数,最常见的是发送网络请求,在发送网络 ...
- JavaScript异步函数Promise①——Promise筑基
期约是对尚不存在的一个替身.期约(promise)这个名字最早是由 Daniel Friedman和 David Wise在他们于 1976 年发表的论文"The Impact of App ...
- javascript eval函数解析json数据时为什加上圆括号eval((+data+))
原因很简单:因为在js中{}表示一个语句块(代码段),所有加上"()"表示表达式 转载于:https://www.cnblogs.com/lihongchen/p/4270334. ...
- ES2017异步函数现已正式可用
2019独角兽企业重金招聘Python工程师标准>>> ES2017标准已于2017年6月份正式定稿了,并广泛支持最新的特性:异步函数.如果你曾经被异步 JavaScript 的逻辑 ...
- ES2017 异步函数async/await
ES2017标准已于2017年6月份正式定稿了,并广泛支持最新的特性:异步函数.如果你曾经被异步 JavaScript 的逻辑困扰,这么新函数正是为你设计的. 异步函数或多或少会让你编写一些顺序的 J ...
- JavaScript 异步编程--Generator函数、async、await
JavaScript 异步编程–Generator函数 Generator(生成器)是ES6标准引入的新的数据类型,其最大的特点就是可以交出函数的执行的控制权,即:通过yield关键字标明需要暂停的语 ...
最新文章
- GGally包绘制的几个图形
- python list是一个有序列表 这个与java的list是无序的列表 具有明显不同,一定要注意
- 从 datetime2 数据类型到 datetime 数据类型的转换产生一个超出范围的值
- 生物信息学搞计算机,生物信息学前景展望,谈谈感想(已经停止)
- 轻触开源(一)-Java泛型Type类型的应用和实践
- 培养逻辑思维的app_久趣数学思维V1.4.1-久趣数学思维APP下载
- 监听listview item兼容于checkbox/textview/imageview
- C# WinForm 为 RichTextBox 添加行号小技巧
- 【TCP/IP】【测试】常用发流软件一览
- Android App加壳中的整体dex加壳的原理和脱壳
- 圆运动和椭圆运动公式
- win10系统怎么查看电脑配置,win10电脑配置查看
- usemvc永远不会被使用_你永远不会离开
- 北航计算机网络安全,李舟军
- (3)网页视频获取下载案例3
- 如何统计PHP程序的运行时间、耗费内存
- 项目启动报错No appropriate protocol (protocol is disabled or cipher suites are inappropriate) 解决办法
- Python h5py安装 HDF5 library version mismatched error
- 关于路由的这10个问题,网络工程师们应该掌握
- 大学生创新创业计划书参考
热门文章
- 【流行框架】Dubbo
- Java中数据类型 + 强制转换
- 为什么和平精英显示服务器未开放,《和平精英》为什么一直不开放“淘汰回放”,真的是技术原因吗?...
- 从喜马拉雅听下载音频文件
- cv2.putText字体比较
- 懿浪漫:不以结婚为目的单身人士时尚恋爱社区网站
- Python 循环嵌套
- api0.php,API接口 · HisiPHP V2开发手册 · 看云
- ashx 获取上传的文件_使用jQuery Post从.ashx获取文件
- CSS transition(过渡效果)详解