diff --git a/docs/source/_data/guide_toc.yml b/docs/source/_data/guide_toc.yml index a73015eadb..6d5750b5c3 100644 --- a/docs/source/_data/guide_toc.yml +++ b/docs/source/_data/guide_toc.yml @@ -32,7 +32,6 @@ Core: Tutorials: MySQL: /tutorials/mysql.html RESTful API: /tutorials/restful.html - Async Function: /tutorials/async-function.html # Passport: /core/passport.html Advanced: Loader: /advanced/loader.html diff --git a/docs/source/en/intro/egg-and-koa.md b/docs/source/en/intro/egg-and-koa.md index dde19095d8..10a189d970 100644 --- a/docs/source/en/intro/egg-and-koa.md +++ b/docs/source/en/intro/egg-and-koa.md @@ -129,11 +129,11 @@ More about plugin, please check [Plugin](../basics/plugin.md) section. #### Egg 1.x -When Egg 1.x released, the Node.js LTS version does not support async function,so Egg 1.x is based on Koa 1.x. On the basis of this, Egg has added full async function support. Egg is completely compatible with middlewares in Koa 2.x, all application layer are implemented based on [async function](../tutorials/async-function.md). +When Egg 1.x released, the Node.js LTS version does not support async function,so Egg 1.x is based on Koa 1.x. On the basis of this, Egg has added full async function support. Egg is completely compatible with middlewares in Koa 2.x, all application could write with `async function`. - The underlying is based on Koa 1.x, asynchronous solution is based on generator function wrapped by [co]. - Official plugin and core of Egg are written in generator function, keep supporting Node.js LTS version, use [co] when necessary to be compatiable with async function. -- Application developers can choose either async function (Node.js 7.6+) or generator function (Node.js 6.0+), **we recommend generator function way for ensuring you application can be runned on Node.js LTS version**. +- Application developers can choose either async function (Node.js 8.x+) or generator function (Node.js 6.x+). #### Egg 2.x diff --git a/docs/source/en/tutorials/async-function.md b/docs/source/en/tutorials/async-function.md deleted file mode 100644 index 498a4fe4cf..0000000000 --- a/docs/source/en/tutorials/async-function.md +++ /dev/null @@ -1,158 +0,0 @@ -title: Development with Async Function ---- - -[Previous chapter](../intro/egg-and-koa.md#async-function) introduces [async function] is a js language level's syntax that providing asynchronous solutions,and Node.js will upgrade v8 to version 5.5 since 7.6.0, then async function don't need to be enabled by flag. Framework default support async function,and can use async function to replace generator function. - -**Note:When writing async function,make sure you are running on Node.js 7.6+.** - -## Controller & Service - -In [controller],there are two methods to write a controller:Controller Class and Method. Any implementations using generator function can use async function instead without logic change,just modify yield syntax to await. -Also in [service] and [controller],all async function can use async function to replace generator function。 - -For example, replacing codes in the [controller] with async function: - -```js -// app/controller/post.js -module.exports = app => { - class PostController extends app.Controller { - // replace * create() with async create() - async create() { - const { ctx, service } = this; - const createRule = { - title: { type: 'string' }, - content: { type: 'string' }, - }; - // vaildate parameters - ctx.validate(createRule); - // assemble parameters - const author = ctx.session.userId; - const req = Object.assign(ctx.request.body, { author }); - // call service to handle business - // replace yield with await - const res = await service.post.create(req); - // set response content and status code - ctx.body = { id: res.id }; - ctx.status = 201; - } - } - - return PostController; -} -``` - -**Note: Using await to call `service.post.create()` in the controller, it might need be called after adjusting `service.post.create()` to async function if `service.post.create()` also use generator function** - -## Schedule - -[Schedule] also support async function,tasks need to follow the rules above to replace generator function with async function. - -```js -module.exports = { - // config the schedule tasks through the attributes of schedule - schedule: { - interval: '1m', // 1 minute interval - type: 'all', // assign all workers are needed to execute - }, - // task is the function that running within schedule tasks, first parameter is instance of anonymous ctx - async task(ctx) { - const res = await ctx.curl('http://www.api.com/cache', { - dataType: 'json', - }); - ctx.app.cache = res.data; - }, -}; -``` - -## Middleware - -All middlewares,including [Standard](../basics/middleware.md) and [Using Middleware in Router](../basics/router.md#Using Middleware) can be written with async function, but argument lists changed comparing to the middleware in generator function, similar to Koa v2.x: - -- first parameter `ctx`,the request context,instance of [Context](../basics/extend.md#Context) . -- second parameter `next`,use await to execute the logic of middleware. - -```js -// app/middleware/gzip.js - -const isJSON = require('koa-is-json'); -const zlib = require('zlib'); - -module.exports = (options, app) => { - return async function gzip(ctx, next) { - // next is a method and need to call comparing to the middleware with the generator function - await next(); - - // completed executing middleware then compress the response body to gzip - const body = ctx.body; - if(!body) return; - - // support options.threshold - if (options.threshold && ctx.length < options.threshold) return; - - if (isJSON(body)) body = JSON.stringify(body); - - // set gzip body and revise the response header - ctx.body = zlib.createGzip().end(body); - ctx.set('Content-Encoding', 'gzip'); - }; -}; -``` - -## Call generator function API - -Since some plugins providing the API of generator function that we cannot call async function using await directly. We can use [co] to provide a wrap method to wrap them to return a Promise then can be used in async function. - -```js -const co = require('co'); -app.mysql.query = co.wrap(app.mysql.query); -// need to bind this as app.mysql if want to use query directly -const query = co.wrap(app.mysql.query).bind(app.mysql); - -// can use async function after wrapping -async function getUser() { - // return await app.mysql.query(); - return await query(); -} -``` - -## Slight of Difference with Generator Function - -While both functions are exactly the same models, but [co] has some special handling such as supporting yield an array (object), which are not native in the async function. But you can easily implement these functions based on some Promise libraries and methods. - -- generator function - - ```js - function* () { - const tasks = [ task(1), task(2), task(3) ]; - let results; - // concurrency - results = yield tasks; - // set max concurrent as 2 - result = yield require('co-parallel')(tasks, 2); - } - ``` - -- async function - - ```js - async () => { - const tasks = [ task(1), task(2), task(3) ]; - let results; - // concurrency - results = await Promise.all(tasks); - // set max concurrent as 2 - results = await require('p-all')(tasks, { concurrency: 2} ); - } - ``` - -@sindresorhus writing lots of [helpers based on promise](https://github.com/sindresorhus/promise-fun),flexible use with async function can make code more readable. - ----- - -Full examples refer to [examples/hackernews-async](https://github.com/eggjs/examples/tree/master/hackernews-async). - -[async function]: https://github.com/tc39/ecmascript-asyncawait -[co]: https://github.com/tj/co -[controller]: ../basics/controller.md -[service]: ../basics/service.md -[schedule]: ../basics/schedule.md diff --git a/docs/source/en/tutorials/index.md b/docs/source/en/tutorials/index.md index da1e142b5c..563417736e 100644 --- a/docs/source/en/tutorials/index.md +++ b/docs/source/en/tutorials/index.md @@ -3,7 +3,6 @@ title: Tutorials - [Quick Start](../intro/quickstart.md) - [Progressive Development](./progressive.md) - [RESTful API](./restful.md) -- [Async Function](./async-function.md) ## Template Engines diff --git a/docs/source/languages/zh-cn.yml b/docs/source/languages/zh-cn.yml index 9cba39bb82..7ee8e19071 100644 --- a/docs/source/languages/zh-cn.yml +++ b/docs/source/languages/zh-cn.yml @@ -58,7 +58,6 @@ guide_toc: Plugin Development: 插件开发 Framework: 框架开发 Progressive: 渐进式开发 - Async Function: Async Function Deployment: 应用部署 # Loader: Community: 社区 diff --git a/docs/source/zh-cn/intro/egg-and-koa.md b/docs/source/zh-cn/intro/egg-and-koa.md index 64025189cf..c4242afaed 100644 --- a/docs/source/zh-cn/intro/egg-and-koa.md +++ b/docs/source/zh-cn/intro/egg-and-koa.md @@ -128,11 +128,11 @@ exports.handler = ctx => { #### Egg 1.x -Egg 1.x 发布时,Node.js 的 LTS 版本尚不支持 async function,所以 Egg 1.x 仍然基于 Koa 1.x 开发,但是在此基础上,Egg 全面增加了 async function 的支持,再加上 Egg 对 Koa 2.x 的中间件也完全兼容,应用层代码可以完全基于 [async function 来实现](../tutorials/async-function.md)。 +Egg 1.x 发布时,Node.js 的 LTS 版本尚不支持 async function,所以 Egg 1.x 仍然基于 Koa 1.x 开发,但是在此基础上,Egg 全面增加了 async function 的支持,再加上 Egg 对 Koa 2.x 的中间件也完全兼容,应用层代码可以完全基于 `async function` 来开发。 - 底层基于 Koa 1.x,异步解决方案基于 [co] 封装的 generator function。 - 官方插件以及 Egg 核心使用 generator function 编写,保持对 Node.js LTS 版本的支持,在必要处通过 co 包装以兼容在 async function 中的使用。 -- 应用开发者可以选择 async function(Node.js 7.6+) 或者 generator function(Node.js 6.0+)进行编写,**我们推荐 generator function 方案以确保应用可以运行在 Node.js LTS 版本上**。 +- 应用开发者可以选择 async function(Node.js 8.x+) 或者 generator function(Node.js 6.x+)进行编写。 #### Egg 2.x diff --git a/docs/source/zh-cn/migration.md b/docs/source/zh-cn/migration.md index 8c8ef9ba8b..67727cf901 100644 --- a/docs/source/zh-cn/migration.md +++ b/docs/source/zh-cn/migration.md @@ -25,7 +25,8 @@ Egg 的理念之一是`渐进式增强`,故我们为开发者提供`渐进升 ## 快速升级 - Node.js 使用最新的 LTS 版本(`>=8.9.0`)。 -- 执行 `egg-bin autod`,会自动升级 `egg` 的依赖为 `^2.0.0`,以及其他插件版本。 +- 修改 `package.json` 中 `egg` 的依赖为 `^2.0.0`。 +- 检查相关插件是否发布新版本(可选)。 - 重新安装依赖,跑单元测试。 **搞定!不需要修改任何一行代码,就已经完成了升级。** @@ -44,7 +45,9 @@ Egg 的理念之一是`渐进式增强`,故我们为开发者提供`渐进升 > 2.x 仍然保持对 1.x 风格的中间件的兼容,故不修改也能继续使用。 -- 返回的函数入参改为 `(ctx, next)` 的形式。 +- 返回的函数入参改为 Koa 2 的 `(ctx, next)` 风格。 + - 第一个参数为 `ctx`,代表当前请求的上下文,是 [Context](./basics/extend.md#Context) 的实例。 + - 第二个参数为 `next`,用 await 执行它来执行后续中间件的逻辑。 - 不建议使用 `async (ctx, next) => {}` 格式,避免错误堆栈丢失函数名。 - `yield next` 改为函数调用 `await next()` 的方式。 @@ -63,7 +66,7 @@ module.exports = () => { module.exports = () => { return async function responseTime(ctx, next) { const start = Date.now(); - // 注意:函数调用 + // 注意,和 generator function 格式的中间件不同,此时 next 是一个方法,必须要调用它 await next(); const delta = Math.ceil(Date.now() - start); ctx.set('X-Response-Time', delta + 'ms'); @@ -84,7 +87,7 @@ module.exports = () => { - generators (delegation) - generator functions (delegation) -而原生的 `yield` 和 `await` 只支持其中的一部分,故在移除 `co` 后,我们需要根据不同场景自行处理: +尽管 `generator` 和 `async` 两者的编程模型基本一模一样,但由于上述的 `co` 的一些特殊处理,导致在移除 `co` 后,我们需要根据不同场景自行处理: #### promise @@ -177,10 +180,11 @@ const { news, user } = await app.toPromise(ctx.service.biz.list(topic, uid)); - [toAsyncFunction][app.toAsyncFunction] 和 [toPromise][app.toPromise] 实际使用的是 [co] 包装,因此会带回对应的性能损失和堆栈问题,建议开发者还是尽量全链路升级。 - [toAsyncFunction][app.toAsyncFunction] 在调用 async function 时不会有损失。 +@sindresorhus 编写了许多[基于 promise 的 helper 方法](https://github.com/sindresorhus/promise-fun),灵活的运用它们配合 async function 能让代码更加具有可读性。 ## 插件升级 -`应用开发者`只需升级`插件开发者`修改后的依赖版本即可,可以用我们提供的命令 `egg-bin autod` 快速更新。 +`应用开发者`只需升级`插件开发者`修改后的依赖版本即可,也可以用我们提供的命令 `egg-bin autod` 快速更新。 以下内容针对`插件开发者`,指导如何升级插件: @@ -231,6 +235,12 @@ task = app.toAsyncFunction(schedule.task); - 修改 `README.md` 的示例为 async function。 - 修改 `test/fixtures` 为 async function,可选,建议分开另一个 PR 方便 Review。 +一般还会需要继续维护上一个版本,故需要: + - 对上一个版本建立一个 `1.x` 这类的 branch 分支 + - 修改上一个版本的 `package.json` 的 `publishConfig.tag` 为 `release-1.x` + - 这样如果上一个版本有 BugFix 时,npm 版本时就会发布为 `release-1.x` 这个 tag,用户通过 `npm i egg-xx@release-1.x` 来引入旧版本。 + - 参见 [npm 文档](https://docs.npmjs.com/cli/dist-tag)。 + [co]: https://github.com/tj/co [egg-schedule]: https://github.com/eggjs/egg-schedule diff --git a/docs/source/zh-cn/tutorials/async-function.md b/docs/source/zh-cn/tutorials/async-function.md deleted file mode 100644 index 40b5494cf0..0000000000 --- a/docs/source/zh-cn/tutorials/async-function.md +++ /dev/null @@ -1,157 +0,0 @@ -title: 使用 async function 开发应用 ---- - -[前面的章节](../intro/egg-and-koa.md#async-function)中介绍了 [async function] 是 js 语言层面提供的异步解决方案,而 Node.js 从 7.6.0 开始升级 v8 到 5.5, async function 将不再需要开启 flag 即可使用。框架也默认支持了 async function,在所有支持 generator function 的地方都可以使用 async function 替代。 - -**注意:在基于 async function 编写应用代码时,请确保你的代码运行在 Node.js 7.6+ 的版本上。** - -## controller & service - -在 [controller] 章节中,我们提供了 controller 的两种写法:基于类和普通方法,其中所有用 generator function 实现的地方都可以用 async function 来实现,代码逻辑没有任何变化,仅需要将 yield 语法改成 await 语法。 -而 [service] 和 [controller] 一样,所有的异步方法都可以用 async function 替换文档中的 generator function。 - -举个例子,将 [controller] 文档中的示例改造成 async function 模式: - -```js -// app/controller/post.js -module.exports = app => { - class PostController extends app.Controller { - // 从 * create() 换成 async create() - async create() { - const { ctx, service } = this; - const createRule = { - title: { type: 'string' }, - content: { type: 'string' }, - }; - // 校验参数 - ctx.validate(createRule); - // 组装参数 - const author = ctx.session.userId; - const req = Object.assign(ctx.request.body, { author }); - // 调用 service 进行业务处理 - // 从 yield 换成 await - const res = await service.post.create(req); - // 设置响应内容和响应状态码 - ctx.body = { id: res.id }; - ctx.status = 201; - } - } - - return PostController; -} -``` - -**注意:在上面的 controller 中,我们用 await 调用了 `service.post.create()` 方法,如果这个方法是 generator function 类型,则也需要改造成 async function 接口才可以被调用。** - -## 定时任务 - -框架提供的[定时任务]也支持 async function,只需要将 task 按照上面的规则,从 generator function 替换成 async function 即可。 - -```js -module.exports = { - // 通过 schedule 属性来设置定时任务的执行间隔等配置 - schedule: { - interval: '1m', // 1 分钟间隔 - type: 'all', // 指定所有的 worker 都需要执行 - }, - // task 是真正定时任务执行时被运行的函数,第一个参数是一个匿名的 Context 实例 - async task(ctx) { - const res = await ctx.curl('http://www.api.com/cache', { - dataType: 'json', - }); - ctx.app.cache = res.data; - }, -}; -``` - -## 中间件 - -框架中所有的中间件,包括[标准定义方式](../basics/middleware.md)以及在[路由中定义的中间件](../basics/router.md#中间件的使用)都可以通过 async function 来编写。但是和 generator function 格式的中间件稍有不同的是,中间件的参数列表变化了,和 Koa v2.x 一样: - -- 第一个参数为 `ctx`,代表当前请求的上下文,是 [Context](../basics/extend.md#Context) 的实例。 -- 第二个参数为 `next`,用 await 执行它来执行后续中间件的逻辑。 - -```js -// app/middleware/gzip.js -const isJSON = require('koa-is-json'); -const zlib = require('zlib'); - -module.exports = (options, app) => { - return async function gzip(ctx, next) { - // 注意,和 generator function 格式的中间件不同,此时 next 是一个方法,必须要调用它 - await next(); - - // 后续中间件执行完成后将响应体转换成 gzip - const body = ctx.body; - if(!body) return; - - // 支持 options.threshold - if (options.threshold && ctx.length < options.threshold) return; - - if (isJSON(body)) body = JSON.stringify(body); - - // 设置 gzip body,修正响应头 - ctx.body = zlib.createGzip().end(body); - ctx.set('Content-Encoding', 'gzip'); - }; -}; -``` - -## 调用 generator function API - -由于一些已有的插件直接提供的是 generator function 的 API,无法直接在 async function 中通过 await 调用,我们可以通过 [co] 提供的 wrap 方法将它们包装成一个返回 Promise 的接口即可在 async function 中使用。 - -```js -const co = require('co'); -app.mysql.query = co.wrap(app.mysql.query); -// 如果想要直接用 query 方法,需要绑定 this 为 app.mysql -const query = co.wrap(app.mysql.query).bind(app.mysql); - -// 包装之后即可在 async function 中使用 -async function getUser() { - // return await app.mysql.query(); - return await query(); -} -``` - -## 和 generator function 的细微差别 - -尽管两者的编程模型基本一模一样,但是 [co] 做了一些特殊处理,例如支持 yield 一个数组(对象),这些在 async function 中都无法原生做到,但是基于一些 Promise 提供的方法以及工具库,也可以轻松的实现这些功能。 - -- generator function - - ```js - function* () { - const tasks = [ task(1), task(2), task(3) ]; - let results; - // 并行 - results = yield tasks; - // 控制最大并发数为 2 - result = yield require('co-parallel')(tasks, 2); - } - ``` - -- async function - - ```js - async () => { - const tasks = [ task(1), task(2), task(3) ]; - let results; - // 并行 - results = await Promise.all(tasks); - // 控制最大并发数为 2 - results = await require('p-all')(tasks, { concurrency: 2} ); - } - ``` - -@sindresorhus 编写了许多[基于 promise 的 helper 方法](https://github.com/sindresorhus/promise-fun),灵活的运用它们配合 async function 能让代码更加具有可读性。 - ----- - -一个完整的例子可以参考 [examples/hackernews-async](https://github.com/eggjs/examples/tree/master/hackernews-async)。 - -[async function]: https://github.com/tc39/ecmascript-asyncawait -[co]: https://github.com/tj/co -[controller]: ../basics/controller.md -[service]: ../basics/service.md -[定时任务]: ../basics/schedule.md