前言

在学习 JavaScript 的过程中,理解并灵活运用异步相关知识是一件不容易的事情,这体现在代码可读性、健壮性上,好在 ES6 出现后挽回了这一局面,我们不再需要编写可读性不高的回调嵌套,也不用为了代码的健壮性而处处小心,这得益于 Promiseasync 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');

上述代码中的正确执行顺序是 startend,这意味着函数调用后异步函数内的程序被暂停了,所以首先输出 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 异步函数解析相关推荐

  1. 细说JavaScript异步函数发展历程

    2019独角兽企业重金招聘Python工程师标准>>> < The Evolution of Asynchronous JavaScript >外文梳理了JavaScri ...

  2. async js 返回值_获取JavaScript异步函数的返回值

    今天研究一个小问题: 怎么拿到JavaScript异步函数的返回值? 1.错误尝试 当年未入行时,我的最初尝试: function getSomething() { var r = 0; setTim ...

  3. javascript --- 异步函数的顺序进行

    假设我们希望某一组异步函数能一次进行,在不使用的任何工具的情况下,可能会编写出类似下面的代码: funcs[0](function() {funcs[1](function() {funcs[2](o ...

  4. JavaScript异步函数同步方法

    首先这里非常感谢慕课网蜗牛老湿,本文内容是基于老师讲解koa2实现原理的时候整理出来的,好吧,这里直接进入主题. 在我们平时进行前端开发的时候,经常需要用到异步函数,最常见的是发送网络请求,在发送网络 ...

  5. JavaScript异步函数Promise①——Promise筑基

    期约是对尚不存在的一个替身.期约(promise)这个名字最早是由 Daniel Friedman和 David Wise在他们于 1976 年发表的论文"The Impact of App ...

  6. javascript eval函数解析json数据时为什加上圆括号eval((+data+))

    原因很简单:因为在js中{}表示一个语句块(代码段),所有加上"()"表示表达式 转载于:https://www.cnblogs.com/lihongchen/p/4270334. ...

  7. ES2017异步函数现已正式可用

    2019独角兽企业重金招聘Python工程师标准>>> ES2017标准已于2017年6月份正式定稿了,并广泛支持最新的特性:异步函数.如果你曾经被异步 JavaScript 的逻辑 ...

  8. ES2017 异步函数async/await

    ES2017标准已于2017年6月份正式定稿了,并广泛支持最新的特性:异步函数.如果你曾经被异步 JavaScript 的逻辑困扰,这么新函数正是为你设计的. 异步函数或多或少会让你编写一些顺序的 J ...

  9. JavaScript 异步编程--Generator函数、async、await

    JavaScript 异步编程–Generator函数 Generator(生成器)是ES6标准引入的新的数据类型,其最大的特点就是可以交出函数的执行的控制权,即:通过yield关键字标明需要暂停的语 ...

最新文章

  1. GGally包绘制的几个图形
  2. python list是一个有序列表 这个与java的list是无序的列表 具有明显不同,一定要注意
  3. 从 datetime2 数据类型到 datetime 数据类型的转换产生一个超出范围的值
  4. 生物信息学搞计算机,生物信息学前景展望,谈谈感想(已经停止)
  5. 轻触开源(一)-Java泛型Type类型的应用和实践
  6. 培养逻辑思维的app_久趣数学思维V1.4.1-久趣数学思维APP下载
  7. 监听listview item兼容于checkbox/textview/imageview
  8. C# WinForm 为 RichTextBox 添加行号小技巧
  9. 【TCP/IP】【测试】常用发流软件一览
  10. Android App加壳中的整体dex加壳的原理和脱壳
  11. 圆运动和椭圆运动公式
  12. win10系统怎么查看电脑配置,win10电脑配置查看
  13. usemvc永远不会被使用_你永远不会离开
  14. 北航计算机网络安全,李舟军
  15. (3)网页视频获取下载案例3
  16. 如何统计PHP程序的运行时间、耗费内存
  17. 项目启动报错No appropriate protocol (protocol is disabled or cipher suites are inappropriate) 解决办法
  18. Python h5py安装 HDF5 library version mismatched error
  19. 关于路由的这10个问题,网络工程师们应该掌握
  20. 大学生创新创业计划书参考

热门文章

  1. 【流行框架】Dubbo
  2. Java中数据类型 + 强制转换
  3. 为什么和平精英显示服务器未开放,《和平精英》为什么一直不开放“淘汰回放”,真的是技术原因吗?...
  4. 从喜马拉雅听下载音频文件
  5. cv2.putText字体比较
  6. 懿浪漫:不以结婚为目的单身人士时尚恋爱社区网站
  7. Python 循环嵌套
  8. api0.php,API接口 · HisiPHP V2开发手册 · 看云
  9. ashx 获取上传的文件_使用jQuery Post从.ashx获取文件
  10. CSS transition(过渡效果)详解