koa中的异常处理
最后更新于:2022-04-01 22:07:15
# koa中的异常处理
比如router中某个yield可能抛错,这种情况下要返回请求是怎么做的呀,都是直接在app on error里面处理吗
在route里
```
co(function* () {
var result = yield Promise.resolve(true);
return result;
}).then(function (value) {
console.log(value);
}, function (err) {
// 异常处理
console.error(err.stack);
});
```
';
如何知道require模块的用法
最后更新于:2022-04-01 22:07:13
# 如何知道require模块的用法
比如
```
var router = require('koa-router')();
```
打开
https://www.npmjs.com/search?q=koa-router
找到
https://www.npmjs.com/package/koa-router
';
如何发布本书到git pages
最后更新于:2022-04-01 22:07:10
# gitbook发布说明
本书以gitbook为编辑器,发布到git pages上,为了便于大家使用,请按照如下办法生成并发布。
## 下载gitbook编辑器
https://www.gitbook.com/editor
## 生成html
> gitbook -> Book -> Preview Website
![](img/gitbook-preview.png)
## 发布到git pages上
```shell
gulp deploy
```
## 预览并确认
访问 http://base-n.github.io/koa-generator-examples/
';
FAQ
最后更新于:2022-04-01 22:07:08
# faq
';
最佳实践
最后更新于:2022-04-01 22:07:06
# 最佳实践
';
部署
最后更新于:2022-04-01 22:07:04
# deploy
';
Supertest
最后更新于:2022-04-01 22:07:01
# supertest
';
Mocha
最后更新于:2022-04-01 22:06:59
# mocha
';
测试
最后更新于:2022-04-01 22:06:57
# test
';
promise with bluebird
最后更新于:2022-04-01 22:06:55
# promise with bluebird
';
async/await
最后更新于:2022-04-01 22:06:52
# async/await
';
co = generator + promise
最后更新于:2022-04-01 22:06:50
# co
理解了co的核心代码就理解了koa的流程控制
```js
var ctx = this;
var args = slice.call(arguments, 1);
```
一开始保存上下文,把arguments的length属性去掉,剩余的参数转数组就是gen的参数
再来看return的promise内的代码
```js
if (typeof gen === 'function') gen = gen.apply(ctx, args);
if (!gen || typeof gen.next !== 'function') return resolve(gen);
```
先判断gen是不是generator function,如果是就转为generator,相当于```gen = new gen;```
转为generator之后就可以调用gen.next()了;
```
onFulfilled();
```
这是进入循环调用链的入口
```js
function onFulfilled(res) {
var ret;
try {
ret = gen.next(res);
} catch (e) {
return reject(e);
}
next(ret);
}
function onRejected(err) {
var ret;
try {
ret = gen.throw(err);
} catch (e) {
return reject(e);
}
next(ret);
}
function next(ret) {
if (ret.done) return resolve(ret.value);
var value = toPromise.call(ctx, ret.value);
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
+ 'but the following object was passed: "' + String(ret.value) + '"'));
}
```
ret是gen.next后的{value:''done:''}对象,value是yield后的表达式,done是执行状态.
判断ret.done是否为true来确定是否需要再执行下去.为true时,说明已经是generator的最后一步,promise转为resolve.不为true时,将yield后的表达式转化为promise.
先判断是否转化为了promise,转化成功,就通过```value.then(onFulfilled, onRejected)```执行onFulfilled或onRejected,再次调用next(),实现循环调用.
当value不能转为promise时,抛出错误,promise转为reject,停止继续运行.
下面写一个例子简单分析一下:
```js
var co = require('co');
var fs = require('fs');
function thunkRead(name) {
return function (cb) {
fs.readFile(name, function (err, file) {
cb(err, file);
});
}
}
co(function *() {
var file = yield thunkRead("package.json");
console.log(file);
return file;
}).then(function (file) {
console.log(file);
});
```
通过上面这段代码来看一下co的整个流程
先模拟一个名为thunkRead的thunk函数,再看co里面的代码,co里面是一个generator function,```gen = gen.apply(ctx, args);``` 通过这一句转化为了generator。
再进入onFulfilled()函数,第一个gen.next()之后ret是 ```{ value: [Function], done: false }``` 将ret传入next()中,done为false,所以toPromise,value是function,所以thunkToPromise.
```js
function thunkToPromise(fn) {
var ctx = this;
return new Promise(function (resolve, reject) {
fn.call(ctx, function (err, res) {
if (err) return reject(err);
if (arguments.length > 2) res = slice.call(arguments, 1);
resolve(res);
});
});
}
```
因为fn是thunk函数,参数只有一个回调函数, ```fn.call(ctx, function (err, res) {});``` 直接调用resolve,在上面的例子中是resolve(file);
然后回到next()中,此时已经是一个promise对象,调用value的then方法,onFulfilled的参数就是file,再运行gen.next(file),将上一步yield的结果file传入generator,因为在例子中最后return了file, ret是 ```{ value:, done: true }``` 最后不返回值的话应该是 ```{ value: undefined, done: true }
``` , 再进入到next()中, 此时done已经为true,说明已经是generator的最后一步,resolve(value);
co中的代码已经执行结束,因为co也是一个promise, 最后resolve(value),所有可以在then方法中得到这个value.
补充下toPromise支持转化thunks,array,objects,generators,generator functions.
所以可以yieldable的是以下6种:
- promises
- thunks (functions)
- array (parallel execution)
- objects (parallel execution)
- generators (delegation)
- generator functions (delegation)
';
es6的generator是什么?
最后更新于:2022-04-01 22:06:48
# es6的generator是什么?
generator指的是
```
function* xxx(){
}
```
是es6里的写法。
```
function* test() {
console.log('1');
yield 1;
console.log('2');
yield 2;
console.log('3');
}
```
代码中间插了两行yield,代表什么呢?
- 当test执行到 yield 1这一行的时候,程序将被挂起,要等待执行下一步的指令;
- 当接收到指令后,test将继续往下运行,直到yield 2这一行,然后程序又被挂起并等待指令;
- 收到指令后,test又将继续运行,而下面已经没有yield了,那么函数运行结束。
这是不是就像,我们调试代码的时候,给插的断点 ?
当然,断点这个比喻,只是表象上比较相像,实质原理还是有非常大差异。
yield就是让后面的generator执行完成后,才继续往下走。
要注意,function后面多了一个星号,这样是表明这个函数将变成一个生成器函数,而不是一个普通函数了。意思就是,test这个函数,将不能被这样执行
test();
但可以获得一个生成器
var gen = test(); // gen就是一个生成器了
然后,生成器可以通过next()来执行运行
gen.next();
也就是上面说的,让函数继续运行的指令。
简单地总结一下:
- 生成器通过yield设置了一些类似”断点“的东西,使得函数执行到yield的时候会被阻断;
- 生成器要通过next()指令一步一步地往下执行(两个yield之间为一步);
- yield 语句后面带着的表达式或函数,将在阻断之前执行完毕;
- yield 语句下面的代码,将不可能在阻断之前被执行;
由此可以看出,yield是如何将异步非阻塞代码,变成 异步阻塞代码。
';
generator/co
最后更新于:2022-04-01 22:06:46
# generator/co
';
流程控制
最后更新于:2022-04-01 22:06:43
# 流程控制
![](img/cb.png)
http://www.ruanyifeng.com/blog/2015/05/async.html
';
Mongo
最后更新于:2022-04-01 22:06:41
# mongodb
';
MySQL
最后更新于:2022-04-01 22:06:39
##封装思路
koa依赖co,其中间件对非阻塞异步代码的要求必须是Yieldables列表中的形式,而mysql库是回调函数的形式。因此,我们需要进行封装,使其接口符合要求。
目前我找到了四种方法,前三种使用开源库,第四种自己动手,将express下的dbHelper层封装成co最新支持的Promise形式。
##实现方法一(co-mysql)
co-mysql和mysql-co实现了对mysql或mysql2的封装转化。这两个库的思路差不多,mysql-co封装度更高,并使用速度更快的mysql2;而co-mysql更简单,只是将mysql.query封装成Promise形式。下面是基于co-mysql的示例代码:
```
var wrapper = require('co-mysql'),
mysql = require('mysql');
var options = { /* 数据库连接字串 */ };
var pool = mysql.createPool(options),
p = wrapper(pool);
...
var rows = yield p.query('SELECT 1');
yield this.render('index', {
title: rows[0].fieldName
});
...
})();
```
##实现方法二(promisify-node)
使用promisify-node库,可以将库整体转化为Promise形式,也可以方便的转化库中的指定接口函数,示例代码如下:
```
var promisify = require("promisify-node");
var db = promisify("dbHelper"); //express下的回调形式封装库
...
var rows = yield db.getById('tableName', {id:1});
yield this.render('index', {
title: rows[0].fieldName
});
...
```
##实现方法三(thunkify)
使用thunkify也能够完成封装,thunkify-wrap是一个增强版的thunkify。不过看说明,这种方法在未来的发展中可能会被淘汰,大概的使用方法如下:
```
var genify = require('thunkify-wrap').genify;
var db = genify("dbHelper"); //express下的回调形式封装库
...
var rows = yield db.getById('tableName', {id:1});
yield this.render('index', {
title: rows[0].fieldName
});
...
```
##实现方法四(直接方法)
直接改造原来express下的回调方式代码为Promise形式,代码及说明如下:
dbHelper.js
```
var options = { /* 数据库连接字串 */ };
var mysql = require('mysql');
var pool = mysql.createPool(options);
//原有非接口代码,对mysql的封装,执行sql语句
function execQuery(sql, values, callback) {
var errinfo;
pool.getConnection(function(err, connection) {
if (err) {
errinfo = 'DB-获取数据库连接异常!';
throw errinfo;
} else {
var querys = connection.query(sql, values, function(err, rows) {
release(connection);
if (err) {
errinfo = 'DB-SQL语句执行错误:' + err;
callback(err);
} else {
callback(null,rows); //注意:第一个参数必须为null
}
});
}
});
}
function release(connection) {
try {
connection.release(function(error) {
if (error) {
console.log('DB-关闭数据库连接异常!');
}
});
} catch (err) {}
}
//对外接口代码,包装成返回Promise函数的形式
exports.getById = function(tablename, id){
return new Promise(function(resolve, reject){
var values = {id:id};
var sql = 'select * from ?? where ?';
execQuery(sql,[tablename, values], function(err, rows){
if(err){
reject(err);
}else{
resolve(rows);
}
})
});
}
```
routes/index.js
```
var db = require("../dbHelper");
...
var rows = yield db.getById('tableName', {id:1});
yield this.render('index', {
title: rows[0].fieldName
});
...
```
##代码
示例部分代码取自该项目:
```
https://github.com/zhoutk/koadmin.git
```
##小结
koa框架以co库为核心组织,很好的用generator来解决了回调函数问题。进行Promise接口形式包装的时候,要注意:回调函数要完全符合其要求的形式:
```
function(err, rows){
if(err){
reject(err);
}else{
resolve(rows);
}
})
```
';
数据库
最后更新于:2022-04-01 22:06:37
# db
';
Post with raw
最后更新于:2022-04-01 22:06:34
# Post with raw(todo)
To get the raw body content of a request with Content-Type: "text/plain" into req.rawBody you can do:
https://gist.github.com/tj/3750227
req.rawBody已经被干掉了,现在只能用req.text
下面是tj给出的代码片段
```javascript
var express = require('./')
var app = express();
app.use(function(req, res, next){
if (req.is('text/*')) {
req.text = '';
req.setEncoding('utf8');
req.on('data', function(chunk){ req.text += chunk });
req.on('end', next);
} else {
next();
}
});
app.post('/', function(req, res){
res.send('got "' + req.text + '"');
});
app.listen(3000)
```
测试
```shell
$ npm test
```
使用Postman测试
![](img/post-rawdata.png)
';
文件上传(Post with form-data)
最后更新于:2022-04-01 22:06:32
# 文件上传 Post with form-data
主要目的是为了上传
koa-v1 要是用 koa-multer-v0.0.2 对应的 multer < 1,所以本处需要指定版本安装
```shell
$ npm install --save koa-multer@0.0.2
```
Usage
```javascript
var app = require('koa')()
, koa = require('koa-router')()
, logger = require('koa-logger')
, json = require('koa-json')
, views = require('koa-views')
, onerror = require('koa-onerror');
var multer = require('koa-multer');
app.use(multer({ dest: './uploads/'}));
```
You can access the fields and files in the request object:
```javascript
router.post('/post/formdata', function *(next) {
console.dir(this.req.body)
console.dir(this.req.files)
this.body = 'this a users response!';
});
```
重要提示: Multer will not process any form which is not multipart/form-data
[see more](https://github.com/koa-modules/multer)
测试
```shell
$ npm test
```
使用Postman测试
![](img/post-formdata.png)
';