MorningSpace Lab
深入浅出LoopBack
第五幕
源自实践的经验谈
—— 深入LoopBack的方方面面
Speaker: MorningSpace
April, 2018
我是谁?
混迹IT圈,蹉跎十余载,如今依旧摸爬滚打于程序员一线的一名普通老兵。虽自觉资质愚钝,却向往程序人生。闲暇之余,偶有技术写译。拙笔译作见诸于市者,如:Manning的《相关性搜索》,O’Rielly的《集体智慧编程》与《EJB 3.0》等。
目前,忝为IBM社交应用产品IBM Connections的中国区Lead Developer。2016年开始接触LoopBack,并将其应用于产品开发。愿为优秀开源技术在国内技术社区的推广略尽绵薄之力。
## 我们将了解到什么?
* LoopBack使用的高阶议题,例如:
* 如何进行单元测试?
* 如何实现异步处理?
* 如何针对事件编程?
* 如何动态定义LoopBack对象?
## 我们将
* 针对Model的方法进行测试
* 针对REST API进行测试
* 针对特定数据库的测试
(详见后面的“演示时间:☑︎ TaskMe”)
## 基于Promise
* 一个Promise对象代表了一个异步操作的结果
* 具备如下几种状态:
* pending,初始状态
* fulfilled,异步操作成功,处于pending阶段的回调函数被触发
* rejected,异步操作失败,错误处理回调被触发
* settled,表示fulfilled或rejected
## LoopBack对Promise的支持
使用Promise前,传统的异步回调函数
MyModel.find(function(err, result){
});
使用Promise后,利用链式的then/catch
MyModel.find()
.then(function(result){
// called if the operation succeeds.
})
.catch(function(err){
// called if the operation encounters an error.
});
## 基于回调函数的例子
// test data
const TASK = { title: 'foo', description: 'bar' };
/* nested operations */
Task.create(TASK, (err, res) => {
taskId = res.id;
Task.getList('undone', () => {
Task.upsert({id: taskId, description: 'updated bar'}, () => {
// do something
Task.destroyById(taskId, () => {
// do something
});
});
});
});
## 基于Promise的例子
// test data
const TASK = { title: 'foo', description: 'bar' };
/* chained operations */
Task.create(TASK)
.then((res) => { taskId = res.id; })
.then(() => Task.getList('undone'))
.then(() => Task.upsert({id: taskId, description: 'updated bar'}))
.then(() => Task.destroyById(taskId));
## 用async/await改写后的样子
// test data
const TASK = { title: 'foo', description: 'bar' };
/* listed operations */
const runIt = async () => {
let res = await Task.create(TASK);
const taskId = res.id;
res = await Task.getList('undone');
res = await Task.upsert({id: taskId, description: 'updated bar'});
res = await Task.destroyById(taskId);
};
runIt();
## LoopBack的事件处理
* LoopBack的app对象是一个Node EventEmitter
* 可以直接在app上调用emit()和on()
* 除支持标准的Node事件机制外,LoopBack的app对象和Model还支持一些特定事件,例如:
* app在初始化阶段会发出booted和started等事件;
* Model实例在被成功创建/保存/更新时会发出changed事件;
* Model实例在被成功删除时会发出deleted事件;
## 事件处理的例子
定义事件响应函数,以响应app的booted事件
const app = require('../src/server/server');
app.on('booted', () => {
/* do something after Loopback app is booted */
});
定义事件响应函数,响应Model的changed事件
MyModel.on('changed', function(inst) {
console.log('model with id %s has been changed', inst.id);
});
## 动态定义数据源的例子
// create data source dynamically
const app = require('../src/server/server');
const dataSource = app.loopback.createDataSource('db', {
"name": "db",
"database": "taskme",
"connector": "mongodb",
});
// attach to model
const Task = app.models.Task;
Task.attachTo(dataSource);
## 动态定义Model的例子
调用app.registry的createModel方法
// require task.json
// ...
const Task = app.registry.createModel(json);
// require and run task.js
// ...
app.model(Task, { dataSource: 'db' });
调用DataSource的createModel方法
const Task = dataSource.createModel('Task', {
title: {'type': String, 'required': true},
description: String,
state: {'type': Number, 'default': 0}
}, options);
app.model(Task);
## ☑︎ 任务清单
* 为TaskMe增加基于Jasmine的单元测试逻辑,分别测试:
* TaskMe的Model方法
* TaskMe的REST API接口
* 基于MongoDB的search逻辑
* 增加测试覆盖(istanbul)
* 增加代码静态检查(eslint)
## 敬请期待
### 下一幕精彩内容 ☻
![](images/survey.jpg)