## focus-flow
[![gitHub](https://img.shields.io/badge/GitHub-hengshanMWC-green.svg)](https://github.com/hengshanMWC/focus-flow)  [![Version](https://img.shields.io/npm/v/focus-flow.svg)](https://www.npmjs.com/package/focus-flow)

让代码变成一条流

[API Documentation](https://hengshanmwc.github.io/focus-flow/)
## Features
* 将后端中间件方式移植到前端，通过维护ctx上下文来处理业务逻辑，降低函数间的耦合
* 实现节流，队列，池等概念
* 支持async/await
## Installing
```
// npm
npm i -S focus-flow
import FocusFlow from 'focus-flow'
// script
<script src="https://unpkg.com/focus-flow/dist/focusFlow.js"></script>
```
## Chestnut
曾几何时，你有没有被反复无常的需求弄得心烦意乱。

举个例子：一个商城代理模块，当代理要发展下线的时候，要通过二维码让新用户扫码，才可以绑定用户。
```
function isRegister(){
  if(is){
    getUserInfo()
  } else {
    register()
  }
}
agent(id){
  //是否有代理id
  if(id) 
}
register(){
  agent()
  getUserInfo()
}
```
好不容易把业务写好了，过了数天，产品：“小马啊，发展下线这个逻辑改成-----非代理用户，第一次扫代理二维码的时候就成为代理的下线”。

这时候又得屁颠颠的从register函数上找到agent注释掉，放到合适的地方。

又过了数天，产品走过来一边帮按摩一边说：”小马啊，发展下线这个逻辑改成......“（产品猝

遇到类似这样的变更，都得到之前的业务逻辑上来找这函数，如果时间一长，或者代码一庞大，改起来就会变得缩手缩脚，特别是别人来接手，函数调来调去，看起来不知道那是头那是尾。

如果把函数按照一个从上而下的流来引用，每个函数封装成一个中间件，通过维护上下文ctx来降低函数间的耦合度，是不是会更好？

然后搞搞，终于把业务代码变成了下面这样
```
//每个回调函数会接受到3个参数，分别是ctx、next、close
const master = new FF()
  .use(isRegister) //是否注册
  .use(register) //注册
  .use(getUserInfo) //获取用户
  .use(agent) //成为下线
//启动的时候
master.start()
```
可读性变得更强，并且修改变动每条管道(use的回调函数之为管道)的时候，我们只需要关注ctx即可。

当然，如果你不想按步就班，你大可next(FocusFlow|Number|String|Boolean)来进行定点执行或跨管道
## Options
```
// 以下是默认配置
new FF({
  threadMax: 1, //最大线程数
  switch: true, //是否开放线程池
  life: -1, //清理线程的周期，毫秒单位, -1为永生
  hand: null, //函数this指向
  queue: false, // 是否开启队列
  queueMax: 10 // 队列上限
})
```
__threadMax__：用来限制threads的上限，当达到上限且其中线程都仍活跃，使用start就不会再创建成功，也就意味着该次的start无法成功执行

__switch__：threadMax如果是一个容器，那么switch则是这个容器的开关

__life__：规定线程的寿命，-1为永生，止至到线程执行完毕。每当回调函数使用next时，都会刷新线程的寿命。线程池会根据线程的寿命去清理掉那些过期的线程。

__hand__：回调函数的全局this指向

__queue__：线程池满了之后的任务都会储存到队列中，线程池有空闲位置时，按照队列先进先出进入线程池中

__queueMax__：队列任务的上限
## Concept
### 跨管道
何为跨管道？因为有些情景可能不止一条管道分支，宛如git上的一条条不同的分支，正常流程上线用到master分支，但当你要处理bug的时候，有可能就需要建一个bug_dev分支了。同理，当我们的master管道出现正常流程之外的事情，我们可以在回调函数里面是用next(ff2, [sign]),就像git checkout ff2那样，让一个专门处理非正常流程的分支去处理这些逻辑，这样整个业务都变得侧层级分明。
### 线程
```
getList(){
  if(close) return 
  close = true
  //异步逻辑，完成后把close设置成false
}
```
上拉加载的时候，用节流去限制请求接口次数。不知道你有没有写过类似代码，或者用闭包去实现。如上功能，FF也可以实现。
```
const ff = new FF()
  .use(getList(ctx, next){
    // 异步逻辑,完成后next
    // 还有一个状况，假设判断后台的所有列表数据已经返回完了，那么再触发这段管道就没有意义了。这时候我们就可以使用ctx.$info.ff.close关闭掉线程池。
  })
ff.start({接口参数})
```
每当ff使用start(成功使用)的时候，内部就会新建一条线程，该线程会负责该次start的请求。而ff中的threads是专门用来储存这些线程。
### 队列
线程池溢出的任务都会储存到队列中，而队列中除了任务上限，还有入口和出口。

__入口__：线程池溢出的任务去向。
- closeQueue 关闭队列入口
- openQueue 打开队列入口

__出口__：线程池空闲后任务进入的方向
- closeExit 关闭队列出口
- openExit 打开队列出口
### 事件
callback(ctx, FocusFlow)
- onFull：线程池溢满事件
- onQueueFull：队列溢满事件
## explain
### 基本管道
success,fail：callback(ctx, next, close)
end：callback(ctx)
error：callback(error, ctx, next, close)
__success__：next()到底的时候就会触发该管道。当然，你也可以next(true)直接执行成功管道

__fail__：next(false)的时候触发

__end__：success和fail的下一个next就是end，而error则是触发完自己的回调函数后，会自动触发end

__error__：捕获错误管道
### 回调函数接受的参数
__ctx__：管道传递的上下文，ff.start(参数)的参数会合并到ctx上。

$info：
* id: 线程的id
* index：当前管道坐标
* ff: 创建该线程的FF实例
* life: 线程的生命周期，-1知道线程执行完成
***
__next__：可传递2个参数。
* 第一个参数param是Number类型时，会跳转到第param条管道并执行（没有符合则相当于next()）
* 第一个参数param是String类型时，会跳转到标记为param的管道并执行（没有符合则相当于next()）
* 第一个参数param是Boolean类型时，会跳转到相应的基本管道并执行
* 第一个参数param是FocusFlow类型时，会进行跨管道（相当于FocusFlow的实例.start(当前ctx，第二个参数)），第二个参数重复以上行为
***
__close__：清理当前执行线程，但线程是还会执行完任务
### ff.start
有时候，当我们执行到某一段逻辑时，因为某些原因中断了流程。当我满足了该条件后，又不想重新由头到尾执行该管道分支，那应该怎么办？我想有同学大概能想到，start(ctx,sign)

用小程序举个例子：
```
const master = new FF()
export default master
  .use(userInfo) // 获取微信用户信息
  .use('code', code) // 获取code
  .use(openid) // 获取openid
  .use('myInfo', myInfo) //获取openid
  .use('register', register) //注册
```
当有用户没有授权微信用户信息的时候，我们从userInfo跳出，然后用某种方法（个人用发布订阅）触发出授权弹框让他们授权,点击授权后调master.start(用户数据,'code')，直接跳到code管道，然后进行接下来的逻辑。








