typescript在ES3(IE7)环境下使用async、await

因为公司产品需要搞个Web App,但是又需要兼容IE7,这时候整个应用会非常复杂,尤其是在处理异步的时候,在我的选择中有两个方案

  1. callback方案
  2. async/await

经过衡量以后,决定使用async/await方案

配置typescript环境

1
2
3
4
5
6
7
8
9
$ mkdir typescript-async            # 新建项目目录
$ cd typescript-async # 进入项目目录
$ npm init -y # 初始化项目
$ npm i webpack --save-dev # 安装webpack
$ npm i ts-loader --save-dev # 安装loader
$ npm i typescript --save-dev # 安装typescript编译器
$ npm i es3ify-webpack-plugin --save-dev
$ touch webpack.dev.js # 新建webpack配置文件
$ .\node_modules\.bin\tsc --init # 创建typescript配置文件

webpack.dev.js改为如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const path = require('path');
const es3ifyPlugin = require('es3ify-webpack-plugin');

module.exports = {
mode: 'development',
entry: './src/index.ts',
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].js',
},
module: {
rules: [{
test: /\.ts$/,
use: ['ts-loader'],
}],
},
resolve: {
extensions: ['.ts'],
},
devtool: "source-map",
plugins: [
new es3ifyPlugin(),
],
};

package.json修改为如下

1
2
3
4
5
6
7
8
{
// ...
"scripts": {
- "test": "echo \"Error: no test specified\" && exit 1",
+ "start": "webpack --config webpack.dev.js"
}
// ...
}

tsconfig.json修改为如下

1
2
3
4
5
6
7
8
9
10
11
{
"compilerOptions": {
// ...
- "target": "es5",
+ "target": "es3",
// ...
- "strict": true,
+ "strict": false,
// ...
}
}

配置测试

1
2
$ mkdir src
$ touch src/index.ts # 新建文件

index.ts改为如下

1
2
3
4
5
const log = (text: string) => console.log(text);

for (let i = 0; i < 5; i++) {
log(String(i));
}

编译源码

1
$ npm start

只要没有报错,就可以在看到build/main.js文件,这个文件就是编译后的结果,那么typescript的编译环境就搭建好了

支持async、await

1
$ npm i es6-promise --save          # 安装promise polyfill

webpack.dev.js改为如下

1
2
3
4
5
6
7
8
module.exports = {
// ...
resolve: {
- extensions: ['.ts'],
+ extensions: ['.js', '.ts'],
},
// ...
};

tsconfig.json修改为如下

1
2
3
4
5
6
7
8
9
10
11
{
"compilerOptions": {
// ...
+ "lib": [
+ "dom",
+ "es2015",
+ "scripthost"
+ ],
// ...
}
}

src/index.ts改为如下

1
2
3
4
5
6
7
8
import "es6-promise/auto";          // 低版本浏览器支持promise

const delay = (time: number) => new Promise(resolve => setTimeout(resolve, time));

(async () => {
await delay(1000);
alert('done.');
})();

编译源码

1
$ npm start

编译成功,async/await在ES3的环境下可以使用了

优化helpers代码

什么是helpers代码?直接看例子,有以下代码

src/index.ts改为如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import "es6-promise/auto";
import delayA from "./a";
import delayB from "./b";

const delay = (time: number) => new Promise(resolve => setTimeout(resolve, time));

(async () => {
await delay(1000);
alert('1');
await delayA(1000);
alert('2');
await delayB(1000);
alert('3');
})();

src/a.ts改为如下

1
2
3
4
5
6
7
const delay = (time: number) => new Promise(resolve => setTimeout(resolve, time));

async function delayA(time: number) {
await delay(time);
}

export default delayA;

src/b.ts改为如下

1
2
3
4
5
6
7
const delay = (time: number) => new Promise(resolve => setTimeout(resolve, time));

async function delayB(time: number) {
await delay(time);
}

export default delayB;

编译源码

1
$ npm start

查看生成后的代码build/main.js,会看到有以下部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// 省略以上代码

/************************************************************************/
/******/ ({

/***/ "./src/a.ts":
/*!******************!*\
!*** ./src/a.ts ***!
\******************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
// ...省略
};
var __generator = (this && this.__generator) || function (thisArg, body) {
// ...省略
};
exports.__esModule = true;
var delay = function (time) { return new Promise(function (resolve) { return setTimeout(resolve, time); }); };
function delayA(time) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, delay(time)];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
}
exports["default"] = delayA;


/***/ }),

/***/ "./src/b.ts":
/*!******************!*\
!*** ./src/b.ts ***!
\******************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
// ...省略
};
var __generator = (this && this.__generator) || function (thisArg, body) {
// ...省略
};
exports.__esModule = true;
var delay = function (time) { return new Promise(function (resolve) { return setTimeout(resolve, time); }); };
function delayB(time) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, delay(time)];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
}
exports["default"] = delayB;


/***/ }),

/***/ "./src/index.ts":
/*!**********************!*\
!*** ./src/index.ts ***!
\**********************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
// ...省略
};
var __generator = (this && this.__generator) || function (thisArg, body) {
// ...省略
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var _this = this;
exports.__esModule = true;
__webpack_require__(/*! es6-promise/auto */ "./node_modules/es6-promise/auto.js");
var a_1 = __importDefault(__webpack_require__(/*! ./a */ "./src/a.ts"));
var b_1 = __importDefault(__webpack_require__(/*! ./b */ "./src/b.ts"));
var delay = function (time) { return new Promise(function (resolve) { return setTimeout(resolve, time); }); };
(function () { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, delay(1000)];
case 1:
_a.sent();
alert('1');
return [4 /*yield*/, a_1["default"](1000)];
case 2:
_a.sent();
alert('2');
return [4 /*yield*/, b_1["default"](1000)];
case 3:
_a.sent();
alert('3');
return [2 /*return*/];
}
});
}); })();


/***/ })

/******/ });
//# sourceMappingURL=main.js.map

可以看到大量的重复的__awaiter__generator代码,这个就是helpers代码,我们需要去掉重复的代码,处理的方式有以下两种

方法1:importHelpers开关

1
$ npm i tslib --save          # 安装tslib

tsconfig.json修改为如下

1
2
3
4
5
6
7
{
"compilerOptions": {
// ...
+ "importHelpers": true,
// ...
}
}

注:上面这种方式需要支持Object.defineProperty这个方法,但是ES3环境不支持,所以ES3环境下不能用这个方式

方法2:noEmitHelpers开关

1
$ npm i tslib --save          # 安装tslib

tsconfig.json修改为如下

1
2
3
4
5
6
7
{
"compilerOptions": {
// ...
+ "noEmitHelpers": true,
// ...
}
}

src/index.ts改为如下

1
2
3
import "es6-promise/auto";
+ import 'tslib/tslib';
// ...

编译源码

1
$ npm start

查看生成后的代码build/main.js,可以看到重复的部分没有了

以上就是整个在ES3环境下使用async/await的方法