We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
作者:Marius Schulz 译者:前端小智 来源:Marius Schulz
作者:Marius Schulz
译者:前端小智
来源:Marius Schulz
阿里云最近在做活动,低至2折,真心觉得很划算了,可以点击本条内容或者链接进行参与: https://promotion.aliyun.com/ntms/yunparter/invite.html?userCode=pxuujn3r
腾讯云最近在做活动,百款云产品低至 1 折,可以点击本条内容或者链接进行参与
为了保证的可读性,本文采用意译而非直译。
自2015年11 发布1.7版以来,TypeScript 已支持 async/await 关键字。编译器使用 yield 将异步函数转换为生成器函数。这意味着咱们无法针对 ES3 或 ES5,因为生成器仅在 ES6 中引入的。
async/await
yield
TypeScript 2.1 现在支持将异步函数编译为 ES3 和 ES5。与生成的其余代码一样,它们在所有 JS 环境中运行。(这甚至包括IE6,当然不建议在去兼容这么古老的浏览器了)
下面是一个简单的函数,它在给定的毫秒数之后解析一个 Promise 。使用内置的 setTimeout 函数在 ms 毫秒过后调用 resolve 回调:
Promise
setTimeout
ms
resolve
function delay(ms: number) { return new Promise<void>(function(resolve) { setTimeout(resolve, ms) }) }
delay 函数返回一个 promise,调用时可以使用 await 来等待这个 promise,如下所示:
delay
promise
await
function delay(ms: number) { return new Promise<void>(function(resolve) { setTimeout(resolve, ms); }) } async function asyncAwait () { console.log('开始执行...'); await delay(1000); console.log('1 秒过后') await delay(1000); console.log('过 2 秒后执行完成'); }
现在,如果调用 asyncAwait 函数,在控制台会看到三个消息,一个接一个,每个消息之间都有一个暂停。
asyncAwait
asyncAwait(); // 开始执行... // 1 秒过后 // 过 2 秒后执行完成
现在,来看一下针对 ES2017,ES2016/ES2015 和 ES5/ES3 时 TypeScript 生成的 JS 代码。
异步函数是一种JavaScript语言功能,在 ES2017 中进行标准化。
因此,在面向 ES2017 时,TypeScript 编译器无需将 async/await 重写为其他某种构造,因为两个异步函数均已被原生支持。生成的 JS 代码与 TypeScript 代码相同,除了已除去所有类型注释和空白行:
function delay(ms) { return new Promise(function(resolve) { setTimeout(resolve, ms); }) } async function asyncAwait () { console.log('开始执行...'); await delay(1000); console.log('1 秒过后') await delay(1000); console.log('过 2 秒后执行完成'); }
这里没什么可说的,这是咱们自己编写的代码,只是没有类型注释。
针对 ES2015,TypeScript 编译器使用生成器函数和 yield 关键字重写 async/await。它还会生成__awaiter 帮助方法作为异步函数的运行程序。以上 asyncAwait 函数的结果编译成 JS 代码如下所示:
__awaiter
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments)).next()); }); }; function delay(ms) { return new Promise(function (resolve) { setTimeout(resolve, ms); }); } function asyncAwait() { return __awaiter(this, void 0, void 0, function* () { console.log('开始执行...'); yield delay(1000); console.log('1 秒过后'); yield delay(1000); console.log('过 2 秒后执行完成'); }); }
辅助代码的 generated 可以忽略不计,但是也不错。如果想在 Node 6.x 或 7.x 应用程序中使用 async/await,需要的配置中设置target 为 ES2015 或 ES2016。
target
ES2015
ES2016
请注意,ES2016 标准化的惟一特性是求幂运算符和 Array.prototype.includes 方法,这里两个方法都不使用。因此,针对 ES2016 生成的 JS 代码与针对 ES2015 生成的代码相同。
Array.prototype.includes
有趣的地方是,使用 TypeScript 2.1,可以让编译器将异步函数降级到 ES3 或 ES5,下面是咱们之前的例子:
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; function delay(ms) { return new Promise(function (resolve) { setTimeout(resolve, ms); }); } function asyncAwait() { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: console.log('开始执行...'); return [4 /*yield*/, delay(1000)]; case 1: _a.sent(); console.log('1 秒过后'); return [4 /*yield*/, delay(1000)]; case 2: _a.sent(); console.log('过 2 秒后执行完成'); return [2 /*return*/]; } }); }); } asyncAwait();
里面有很多帮助代码。除了前面已经看到的 __awaiter 函数之外,编译器还注入了另一个名为generator的帮助函数,它使用一个状态机来模拟一个可以暂停和恢复的生成器函数。
generator
注意,为了让各位的代码在 ES3 或 ES5 环境中成功运行,需要提供Promise polyfill,因为 Promise 只在 ES2015 中引入。另外,你必须让TypeScript知道在运行时,它可以找到 Promise 函数。这在上一章TypeScript 2.0:内置类型声明 有讲过了。
有了它,async/await 在所有 JS 引擎中都可以运行。
,async/await
接下来,来看看如何避免在编译中的每个 TypeScript 文件一遍又一遍地将这些辅助函数写入。
在某些情况下,TypeScript 编译器会将帮助函数注入到在运行时调用的生成输出代码中。每个这样的帮助函数都模拟编译目标(target) (ES3、ES5、ES2015) 本身不支持的特定语言特性的语义。
目前,TypeScript 中有以下帮助函数
__decorate
__param
__metadata
__generator
带有 extends 的 ES6 类的典型用例是如下所示的 React 组件:
extends
import * as React from "react"; export default class FooComponent extends React.Component<{}, {}> { render() { return <div>Foo</div>; } }
如果配置的 target 是ES5,那么 TypeScript 编译器将生成(emit )以下 JS 代码,其中既不支持 class ,也不支持 extends :
class
"use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var React = require("react"); var FooComponent = (function (_super) { __extends(FooComponent, _super); function FooComponent() { return _super.apply(this, arguments) || this; } FooComponent.prototype.render = function () { return (React.createElement("div", null, "Foo")); }; return FooComponent; }(React.Component)); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = FooComponent;
虽然这种方法对于这样的简单示例很有效,但是它有一个很大的缺点:将 __extends 帮助函数代码注入到使用带有extends语句的类的每个编译文件中。也就是说,为应用程序中每个基于类的 React 组件触发帮助函数。
__extends
对于一个包含数十个或数百个 React 组件的中型应用程序,对于__extends 函数来说是大量重复的代码。大量重复的代码也会增加包大小,所以下载时间也会随之增加。
这个问题只会对于其它的帮助的函数也会存在,如开头讲的如何将 async/await 降级到 ES3/ES5 中的 __awaiter 和 __generator 帮助函数也很大。注意,它们被注入到每个使用 async/await 关键字的文件中:
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments)).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t; return { next: verb(0), "throw": verb(1), "return": verb(2) }; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [0, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } };
在 1.5 版中,TypeScript 增加 --noEmitHelpers 标志。当指定此编译器选项时,TypeScript 不会在编译后生成任何帮助函数。这样,捆绑包的大小会减少很多。
--noEmitHelpers
下面是之前的 React 组件,用 --noEmitHelpers 标志编译:
"use strict"; var React = require("react"); var FooComponent = (function (_super) { __extends(FooComponent, _super); function FooComponent() { return _super.apply(this, arguments) || this; } FooComponent.prototype.render = function () { return (React.createElement("div", null, "Foo")); }; return FooComponent; }(React.Component)); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = FooComponent;
注意,对 __extends 的调用仍然存在。毕竟,使 React 组件工作是必需的。如果咱们使用 --noEmitHelpers 标志,那么咱们就需要提供所需的所帮助函数,因为TypeScript 假设它们在运行时可用。
但是,手动跟踪所有这些帮助函数非常麻烦。咱必须检查应用程序需要哪些包,然后以某种方式使它们在包中可用。一点都不好玩了。还好,TypeScript 团队提出了一个更好的解决方案。
TypeScript 2.1 引入了一个新的 --importHelpers 标志,它使编译器从tslib(一个外部帮助库)导入帮助函数,而不是将它们内联到每个文件中。安装 tslib 方式如下:
--importHelpers
tslib
npm install tslib --save
再次编译 Reac t组件,这次使用 --importHelpers 标志:
"use strict"; var tslib_1 = require("tslib"); var React = require("react"); var FooComponent = (function (_super) { tslib_1.__extends(FooComponent, _super); function FooComponent() { return _super.apply(this, arguments) || this; } FooComponent.prototype.render = function () { return (React.createElement("div", null, "Foo")); }; return FooComponent; }(React.Component)); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = FooComponent;
请注意第2行中的 require("tslib") 调用和第5行中的 tslib_1.__extends 调用。此文件中不再内嵌帮助函数,而是从 tslib 模块导入 __extends 函数。这样,每个帮助函数仅在程序中包含一次,完美。
2
require("tslib")
5
tslib_1.__extends
编辑中可能存在的bug没法实时知道,事后为了解决这些bug,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug。
原文: https://mariusschulz.com/blog/external-helpers-library-in-typescript https://mariusschulz.com/blog/compiling-async-await-to-es3-es5-in-typescript
干货系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。
https://github.com/qq449245884/xiaozhi
因为篇幅的限制,今天的分享只到这里。如果大家想了解更多的内容的话,可以去扫一扫每篇文章最下面的二维码,然后关注咱们的微信公众号,了解更多的资讯和有价值的内容。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
阿里云最近在做活动,低至2折,真心觉得很划算了,可以点击本条内容或者链接进行参与:
https://promotion.aliyun.com/ntms/yunparter/invite.html?userCode=pxuujn3r
腾讯云最近在做活动,百款云产品低至 1 折,可以点击本条内容或者链接进行参与
为了保证的可读性,本文采用意译而非直译。
自2015年11 发布1.7版以来,TypeScript 已支持
async/await
关键字。编译器使用yield
将异步函数转换为生成器函数。这意味着咱们无法针对 ES3 或 ES5,因为生成器仅在 ES6 中引入的。TypeScript 2.1 现在支持将异步函数编译为 ES3 和 ES5。与生成的其余代码一样,它们在所有 JS 环境中运行。(这甚至包括IE6,当然不建议在去兼容这么古老的浏览器了)
使用异步函数
下面是一个简单的函数,它在给定的毫秒数之后解析一个
Promise
。使用内置的setTimeout
函数在ms
毫秒过后调用resolve
回调:delay
函数返回一个promise
,调用时可以使用await
来等待这个promise
,如下所示:现在,如果调用
asyncAwait
函数,在控制台会看到三个消息,一个接一个,每个消息之间都有一个暂停。现在,来看一下针对 ES2017,ES2016/ES2015 和 ES5/ES3 时 TypeScript 生成的 JS 代码。
编译 async/await 到 ES2017
异步函数是一种JavaScript语言功能,在 ES2017 中进行标准化。
因此,在面向 ES2017 时,TypeScript 编译器无需将
async/await
重写为其他某种构造,因为两个异步函数均已被原生支持。生成的 JS 代码与 TypeScript 代码相同,除了已除去所有类型注释和空白行:这里没什么可说的,这是咱们自己编写的代码,只是没有类型注释。
编译 async/await 到 ES2015/ES2016
针对 ES2015,TypeScript 编译器使用生成器函数和
yield
关键字重写async/await
。它还会生成__awaiter
帮助方法作为异步函数的运行程序。以上asyncAwait
函数的结果编译成 JS 代码如下所示:辅助代码的 generated 可以忽略不计,但是也不错。如果想在 Node 6.x 或 7.x 应用程序中使用
async/await
,需要的配置中设置target
为ES2015
或ES2016
。请注意,ES2016 标准化的惟一特性是求幂运算符和
Array.prototype.includes
方法,这里两个方法都不使用。因此,针对 ES2016 生成的 JS 代码与针对 ES2015 生成的代码相同。编译 async/await 到 ES3/ES5
有趣的地方是,使用 TypeScript 2.1,可以让编译器将异步函数降级到 ES3 或 ES5,下面是咱们之前的例子:
里面有很多帮助代码。除了前面已经看到的
__awaiter
函数之外,编译器还注入了另一个名为generator
的帮助函数,它使用一个状态机来模拟一个可以暂停和恢复的生成器函数。注意,为了让各位的代码在 ES3 或 ES5 环境中成功运行,需要提供
Promise
polyfill,因为Promise
只在 ES2015 中引入。另外,你必须让TypeScript知道在运行时,它可以找到Promise
函数。这在上一章TypeScript 2.0:内置类型声明 有讲过了。有了它
,async/await
在所有 JS 引擎中都可以运行。接下来,来看看如何避免在编译中的每个 TypeScript 文件一遍又一遍地将这些辅助函数写入。
TypeScript 中的外部帮助库
在某些情况下,TypeScript 编译器会将帮助函数注入到在运行时调用的生成输出代码中。每个这样的帮助函数都模拟编译目标(target) (ES3、ES5、ES2015) 本身不支持的特定语言特性的语义。
目前,TypeScript 中有以下帮助函数
__decorate
,__param
,__metadata
__awaiter
和__generator
用于async/await
带有
extends
的 ES6 类的典型用例是如下所示的 React 组件:如果配置的
target
是ES5,那么 TypeScript 编译器将生成(emit )以下 JS 代码,其中既不支持class
,也不支持extends
:虽然这种方法对于这样的简单示例很有效,但是它有一个很大的缺点:将
__extends
帮助函数代码注入到使用带有extends
语句的类的每个编译文件中。也就是说,为应用程序中每个基于类的 React 组件触发帮助函数。对于一个包含数十个或数百个 React 组件的中型应用程序,对于
__extends
函数来说是大量重复的代码。大量重复的代码也会增加包大小,所以下载时间也会随之增加。这个问题只会对于其它的帮助的函数也会存在,如开头讲的如何将
async/await
降级到 ES3/ES5 中的__awaiter
和__generator
帮助函数也很大。注意,它们被注入到每个使用async/await
关键字的文件中:--noEmitHelpers 标志
在 1.5 版中,TypeScript 增加
--noEmitHelpers
标志。当指定此编译器选项时,TypeScript 不会在编译后生成任何帮助函数。这样,捆绑包的大小会减少很多。下面是之前的 React 组件,用
--noEmitHelpers
标志编译:注意,对
__extends
的调用仍然存在。毕竟,使 React 组件工作是必需的。如果咱们使用--noEmitHelpers
标志,那么咱们就需要提供所需的所帮助函数,因为TypeScript 假设它们在运行时可用。但是,手动跟踪所有这些帮助函数非常麻烦。咱必须检查应用程序需要哪些包,然后以某种方式使它们在包中可用。一点都不好玩了。还好,TypeScript 团队提出了一个更好的解决方案。
--importHelpers 标志和 tslib
TypeScript 2.1 引入了一个新的
--importHelpers
标志,它使编译器从tslib
(一个外部帮助库)导入帮助函数,而不是将它们内联到每个文件中。安装tslib
方式如下:再次编译 Reac t组件,这次使用
--importHelpers
标志:请注意第
2
行中的require("tslib")
调用和第5
行中的tslib_1.__extends
调用。此文件中不再内嵌帮助函数,而是从tslib
模块导入__extends
函数。这样,每个帮助函数仅在程序中包含一次,完美。编辑中可能存在的bug没法实时知道,事后为了解决这些bug,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug。
原文:
https://mariusschulz.com/blog/external-helpers-library-in-typescript
https://mariusschulz.com/blog/compiling-async-await-to-es3-es5-in-typescript
交流
干货系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。
因为篇幅的限制,今天的分享只到这里。如果大家想了解更多的内容的话,可以去扫一扫每篇文章最下面的二维码,然后关注咱们的微信公众号,了解更多的资讯和有价值的内容。
The text was updated successfully, but these errors were encountered: