前言

上一篇 JavaScript – Promise 介绍了如何用 JS 编写可读性高的异步函数. 但其实呢, Promise 还不是最好的.

在 es6 之前, Promise 比起回调地狱是好了很多, 但是还不够美.

一堆的 .then 和回调函数. 还不够美. 于是就有了用了 es6 的新特性 Generator 来优化 Promise 的写法. 要读懂这篇, 请先看 JavaScript – Promise 和 JavaScript – Generator Function.

参考

阮一峰 – Generator 函数的异步应用

最终画面

除去了一堆的 .then 回调.

Generator 看上去和同步代码差不多. 只是多了一些 yield 而已. 完美.

原理

Generator 的内容是 "代码块", 每一个 yield 就是一个切分.

调用 Generator 会得到 Iterator. 通过 Iterator.next 分段执行 Generator 内的代码, 这整套就是 Generator 的玩法.

上面的 myAsync Generator 函数, 把每一个异步函数切分成一个代码块.

当 Iterator.next 的时候就会得到一个 Promise. 然后由外部去执行, 等待 callback.

callback 的时候就接着下一个 .next 把 promise 返回值再交回给 Generator 函数内. 通过这样一种反向控制. 就把异步编程写成同步了.

是不是很聪明?

上面我们只写了 Generator 函数, 它还不完整, 因为没有 Iterator 它是不会跑的. 而如上所说 Iterator 执行时还要处理 promise .then 回调等等, 代码可不少.

但不用担心, 逻辑是一样的, 只要写一次就可以了.

自执行 Iterator

上面只定义了 Generator 还需要一个执行

const iterator = myAsync();
const recursiveNext = () => {
const { value: promise, done } = iterator.next();
if (!done) {
promise!.then(() => {
recursiveNext();
});
}
};
recursiveNext();

Generator 负责 yield 返回 promise. Iterator 负责 .then 注册回调. 回调的时候继续 .next

思路和实现大概就是上面这样. .next 还可以传入 promise resolve 的值哦.

当然, 真正的自执行比这个复杂许多. 可以看上面阮一峰的教程. 里面讲的很详细了.

await async (es2017)

搞明白了 Generator 运行异步函数的概念. 了解 await async 就简单多了.

它们就是 Generator 的语法糖而已.

async function myAsync() {
await delayAsync(3000);
console.log('a'); await delayAsync(2000);
console.log('b'); await delayAsync(1000);
console.log('c');
} (async () => {
await myAsync();
})();

async 就是 Generator 函数. 它里面的 yield 则换成了 await

await 就是执行自执行 Iterator (游览器 / Node.js 都封装了这个函数)

规则还是一样的. yield 必须返回 promise, await 也必须是一个 promise.

因为自执行 Iterator 负责等待和调用 callback, 所以它和 Generator 需要有一个共识的接口.

Promise 很适合作为这个共识接口. 但它也不是唯一的. 只要是可以把 callback 切分出来让另一方调用就可以作为共识的接口了.

比如 Thunk 函数

readFile 经过 Thunk 后, Iterator 就可以调用它, 然后控制 callback 了.

Thunk 和 Promise 都做到了把 callback 独立出来, 所以都可以满足自执行 Generator 的条件. 但现在大家应该都是统一用 Promise 了.

执行同步或异步函数

参考: 阮一峰 – Promise.try()

如果有一个函数, 你不清楚它是同步的还是异步的. 你该怎么去调用它呢?

答案是 wrap 起来.

const fn = () => {};
(async () => {
await (async () => fn())();
})();

因为 async 函数允许返回普通值和 promise 对象.

ECMA 有一个最新的提案叫 Promise.try, 做的事情就是 wrap 一个函数, 让它变成 promise, 就像上面那样.

但目前还不可以用.

async await & try catch

在之前的 JavaScript – Promise 里有讲过 Promise 的 catch 功能.

使用 async await 也可以直接使用 catch

(async () => {
try {
await Promise.resolve();
await Promise.reject('error');
await Promise.resolve(); // won't be called
} catch (e) {
console.log(e); // error
}
})();

任何一行代码发生错误都直接去到 catch.

有时候可能会希望针对不同的 promise 做 catch 可以这样写

(async () => {
try {
await Promise.reject().catch(() => {
console.log('error');
});
await Promise.resolve(); // 注意: this will be called
} catch (e) {
console.log(e); // 注意: won't be called
}
})();

注意哦, catch 之后如果没有再往外 throw, 那么后续的代码会继续执行哦.

继续 throw 的写法

(async () => {
try {
await Promise.reject().catch(() => {
throw new Error('first promise failed');
}); await Promise.reject().catch(() => {
throw new Error('second promise failed');
}); await Promise.resolve(); // won't be called
} catch (e) {
console.log(e); // first promise failed
}
})();

此外 Golang 还有一种写法是不要使用 try catch 的

我个人觉得其实差别不大. 只是把 try catch 变成 if 判断而已.

有兴趣可以看这篇 Youtube – Async Await try-catch hell

JavaScript – 用 Generator 运行异步函数 & await async的更多相关文章

  1. 更优雅的方式: JavaScript 中顺序执行异步函数

    火于异步 1995年,当时最流行的浏览器--网景中开始运行 JavaScript (最初称为 LiveScript). 1996年,微软发布了 JScript 兼容 JavaScript.随着网景.微 ...

  2. C# 异步锁 await async锁,lock,Monitor,SemaphoreSlim

    异步方法内无法使用Monitor 和lock 所以只能用System.Threading.SemaphoreSlim了 //Semaphore (int initialCount, int maxim ...

  3. 『审慎』.Net4.6 Task 异步函数 比 同步函数 慢5倍 踩坑经历

    异步Task简单介绍 本标题有点 哗众取宠,各位都别介意(不排除个人技术能力问题) —— 接下来:我将会用一个小Demo 把 本文思想阐述清楚. .Net 4.0 就有了 Task 函数 —— 异步编 ...

  4. javascript异步编程的前世今生,从onclick到await/async

    javascript与异步编程 为了避免资源管理等复杂性的问题, javascript被设计为单线程的语言,即使有了html5 worker,也不能直接访问dom. javascript 设计之初是为 ...

  5. 5分种让你了解javascript异步编程的前世今生,从onclick到await/async

      javascript与异步编程 为了避免资源管理等复杂性的问题,javascript被设计为单线程的语言,即使有了html5 worker,也不能直接访问dom. javascript 设计之初是 ...

  6. 异步编程的上下文与操作符--await/async generator/yield

    上下文的保存机制: 1.保存到异步类型中:promise & future & closure & observable: 2.栈帧保存:其它保存机制: 3.保存到服务提供方的 ...

  7. generator函数与async/await

    理解async函数就要先理解generator函数,因为async就是Generator函数的语法糖 Generator 函数 Generator 函数是 ES6 提供的一种异步编程解决方案,可以先理 ...

  8. 异步函数async await在wpf都做了什么?

    首先我们来看一段控制台应用代码: class Program { static async Task Main(string[] args) { System.Console.WriteLine($& ...

  9. .net 异步函数 Async await

    .net  异步函数  Async await 一旦为函数添加async关键字 该函数就是一个异步函数. 异步方法必须返回 void 或 Task<> 类型. public static ...

  10. C# Async/Await异步函数原理

    原理 与同步函数相比,CLR在执行异步函数时有几个不同的特点: 1.        并非一次完成,而且分多次完成 2.        并非由同一个线程完成,而是线程池每次动态分配一个线程来处理: 结合 ...

随机推荐

  1. Django 自定义创建密码重置确认页面

    要实现上述功能,你需要修改模板文件以添加"忘记密码"链接,并创建新的视图函数来处理密码丢失修改页面.验证和密码修改.下面是你可以进行的步骤: 1. 修改模板文件 在登录页面的表单下 ...

  2. [oeasy]python0079_控制序列_光标位置设置_ESC_逃逸字符_CSI

    光标位置 回忆上次内容 上次我们研究的比较杂 类型转化 进制转化 捕获异常 版本控制 生成帮助文档 变量的常用类型 变量的生命周期控制   数据类型主要研究了两个 字符串 str   整型数字 int ...

  3. CF1956B Nene and the Card Game 题解

    Nene and the Card Game 题意 有 \(2n\) 张牌,\(1,2,3,\dots,n\) 皆有两张. 有两个人在玩游戏,每个人有 \(n\) 张卡片,当一人出了一张编号为 \(k ...

  4. Maven创建Web项目(idea)

    web项目创建 Web项目开发需要导入许多的第三方jar包,用Maven创建web项目就可以将这一操作免去. 本项目是在idea下创建的,idea自带Maven,如果还未了解Maven的同学可以看上一 ...

  5. win10安装和使用wireshark

    win10安装和使用wiresharkhttps://blog.csdn.net/qq_34732729/article/details/105126146https://blog.csdn.net/ ...

  6. 【IDEA】使用Maven骨架创建JavaWeb项目

    IDEA版本:2020.1 骨架选项名称: org.apache.maven.archetypes:maven-archetype-webapp 本项目的Maven坐标设置: 设置优先从本地获取骨架: ...

  7. 深度学习的始祖框架,grandfather级别的框架 —— Theano —— 示例代码学习(5)

    代码1:(求雅可比矩阵, jacobian矩阵求解) import theano from theano import tensor # Creating a vector x = tensor.dv ...

  8. 大连人工智能计算平台——华为昇腾AI平台——高性能计算HPC——如何在MPI中支持multiprocessing和fork操作——如何在HPC平台上使用pytorch——是否可以通过调度器的提交参数绕过HPC的计费系统

    本文要讨论的就是如何在MPI中支持multiprocessing和fork操作,但是这个问题同时也是如何在HPC平台如何使用pytorch的问题,可以说这两个问题其实是同一个问题,而这个问题的解决过程 ...

  9. 【转载】 vim中常用折叠命令

    原文地址: https://www.cnblogs.com/litifeng/p/11675547.html 个人推荐的一个视频教程地址: 上古神器Vim:从恶言相向到爱不释手 - 终极Vim教程01 ...

  10. python 音频处理(2)——提取PPG特征之whisper库的使用(2.1)

    提取PPG特征之--whisper库的使用(2.1) 1 安装对应的包 方法一(自用): 直接pip即可: pip install openai-whisper 成功后如下图所示 方法二: 当时用了他 ...