npm run watch-compile
Can't Stop the ProcessES6/7 support a mass of new features that bring us great convenience and efficiency. For example, we use ES6 */yield
and ES7 async/await
feature to resolve async callback hell problem. And use arrow function to resolve this
scope problem. Or use class
grammar to resolve class inherit problem.
Although Node.js hasn't support all of those features, we can use them in Node.js stable environment in advance with the help of Babel. It's so good that we can enjoy the convenience and efficiency because of those new features.
npm run watch-compile
Can't Stop the ProcessVersion 2.0.6 has removed this command, beacause this version has supported auto-compile featrue, so all you need to do is to start the service by run npm start
.
Due to the working manner of Node.js, you must restart the service to make the modification to ta effect by default. It's so inconvenience to us. New version of ThinkJS supports auto update file mechanism to apply modification without restart.
Auto update may influence performance, so this feature turns on only in development
mode. For online code, we advise you use pm2
module.
By default, view files' path is view/[module]/[controller]_[action].html
. In this example, controller and action was join by _
. If you want change joiner to /
, you can change configuration file src/common/config/view.js
like this:
export default {
file_depr: '/', //change joiner to /
}
For online code, you can improve its performance by make use of multi-core CPU to heighten concurrence computing.
You can open src/common/config/env/production.js
, and add the following option to it:
export default {
cluster_on: true //turn on cluster
}
The default timeout in ThinkJS is 120 seconds, you can modify it by open src/common/config/config.js
, and add the following option:
export default {
timeout: 30, // Change timeout to 30 seconds
}
Using JS's try/catch
can't catching exceptions come out of asynchronous code, but after using async/await
you can use it:
export default class extends think.controller.base {
async indexAction(){
try{
await this.getFromAPI1();
await this.getFromAPI2();
await this.getFromAPI3();
}catch(err){
//err.message is error message
return this.fail(err.message);
}
}
}
Although you can catch the exception, but you can't know which code has triggered it.
In practice, this is not convenient because you ofen need to give error messages to users based on the errors.
At this time, you can check the specific error through single async request:
export default class extends think.controller.base {
async indexAction(){
//ignore this error
await this.getFromAPI1().catch(() => {});
//return false when exception occurs
let result = await this.getFromAPI2().catch(() => false);
if(result === false){
return this.fail('API2 ERROR');
}
}
}
By returning different values for each exception, you can then return different error messages to users.
When using async/await
, if Promise returned a rejected Promise, an exception will be throwed. If it is not important and you want to ignore it, you can make the catch method to return a resolve Promise:
export default class extends think.controller.base {
async indexAction(){
//returns undefined mean this exception will be ignore
await this.getAPI().catch(() => {});
}
}
After calling some methods such as success
, you may find an error message named PREVENT_NEXT_PROCESS
in console. This error is introduced by ThinkJS to prevent from the running of subsequent processes. If you want to check PREVENT_NEXT_PROCESS
in catch method, you can use think.isPrevent
:
module.exports = think.controller({
indexAction(self){
return self.getData().then(function(data){
return self.success(data);
}).catch(function(err){
//ignore PREVENT_NEXT_PROCESS error
if(think.isPrevent(err)){
return;
}
console.log(err.stack);
})
}
})
Another handling method: don't use return
before methond such as success
, then there has no this error in catch
.
While using sync/await
, your code is excuted serially. But mostly you want to excute parallely to have higher execution efficiency. For this, you can use Promise.all
to implement it.
export default class extends think.controller.base {
async indexAction(){
let p1 = this.getServiceData1();
let p2 = this.getAPIData2();
let [p1Data, p2Data] = await Promise.all([p1, p2]);
}
}
p1
and p2
are processed parallelly and then get both data by using Promise.all
.
If your projects need to output data like images and other file types, you can do it as following:
export default class extends think.controller.base {
imageAction(){
//image buffer data, you can read it locally or remotely
let imageBuffer = new Buffer();
this.type('image/png');
this.end(imageBuffer);
}
}
Environments varies, the configuration may be vary too. For example, development environment and production environments should use different database configurations. You can modify src/common/config/env/[env].js
to implement this. The option [env]
has three default values: development
, testing
and production
.
If you want to config production environment database, you can modify src/common/config/env/production.js
:
export default {
db: { //there has one level named db
type: 'mysql',
adapter: {
mysql: {
host: '',
port: ''
}
}
}
}
You can know more about configuration in here。
Because of root_path
setting in nunjucks, you should using relative path when you want to extend template:
{% extends "./parent.html" %} //same level parent.html file
{% extends "../layout.html" %} //parent level layout.html file
Action can be excuted by user requests or CLI calls. You can use isCli
to judge when you want to allow Action call in CLI only:
export default class extends think.controller.base {
indexAction(){
//ban URL request this Action
if(!this.isCli()){
this.fail('only allow invoked in cli mode');
}
...
}
}
You may have some requests about calling function across modules when your projects is complicated.
You can call controller in other modules by using this.controller
and pass the second parameter in:
export default class extends think.controller.base {
indexAction(){
//get user controller instance in admin module
let controllerInstance = this.controller('user', 'admin');
//then you can use user controller function after getting instance
let bar = controllerInstance.foo();
}
index2Action(){
// or use this simple way
let controllerInstance = this.controller('admin/user');
let bar = controllerInstance.foo();
}
}
You can call actions in other modules by using this.action
:
export default class extends think.controller.base {
async indexAction(){
//get user controller instance in admin module
let controllerInstance = this.controller('user', 'admin');
//call test action in controller, it will also call `__before` and `__after` magic function automatically.
let data = await this.action(controllerInstance, 'test')
}
async index2Action(){
//you can also assign controller by string
let data = await this.action('admin/user', 'test')
}
}
tip
: All actions will return Promise object, and it won't call logic when you call action alone.
You can call models in other modules by using this.model
:
export default class extends think.controller.base {
indexAction(){
//get user model instance in admin module
let modelInstance1 = this.model('user', {}, 'admin');
// or use this simple type
let modelInstance2 = this.model('admin/user');
}
}