超级易懂的redux-saga原理解析

栏目: 服务器 · 发布时间: 5年前

内容简介:笔者最近在做一些后台项目,使用的是在redux-saga中,saga是指一些长时操作,用generator函数表示。generator函数的强大之处在于其可以手动的暂停、恢复执行,且可以与函数体外进行数据交互,看如下例子:可以看出来genrator函数何时进行下一步操作完全取决于外部的调度时机,且其内部执行状态也由外部的输入决定,这使得generator函数可以很方便的做异步流程控制。举个例子,我们首先读取一个文件的内容作为查询参数,然后请求一个查询接口并把返回的内容打印出来:

笔者最近在做一些后台项目,使用的是 Ant Design Pro ,其使用了 redux-saga 处理异步数据流,本文将对redux-saga的原理做一个简单的解读,并将实现一个 简易版的redux-saga

Generator函数的自动流程控制

在redux-saga中,saga是指一些长时操作,用generator函数表示。generator函数的强大之处在于其可以手动的暂停、恢复执行,且可以与函数体外进行数据交互,看如下例子:

function *gen() {
  const a = yield 'hello';
  console.log(a);
}

cont g = gen();
g.next(); // { value: 'hello', done: false }
setTimeout(() => g.next('hi'), 1000)  // 此时 a => 'hi'   一秒后打印‘hi'
复制代码

可以看出来genrator函数何时进行下一步操作完全取决于外部的调度时机,且其内部执行状态也由外部的输入决定,这使得generator函数可以很方便的做异步流程控制。举个例子,我们首先读取一个文件的内容作为查询参数,然后请求一个查询接口并把返回的内容打印出来:

function getParams(file) {
  return new Promise(resolve => {
    fs.readFile(file, (err, data) => {
      resolve(data)
    })
  })
}

function getContent(params) {
  //  request返回promise
  return request(params)
}

function *gen() {
  const params = yield getParams('config.json');
  const content = yield getContent(params);
  console.log(content);
}
复制代码

我们可以手动控制gen函数的执行:

const g = gen();
g.next().value.then(params => {
  g.next(params).value.then(content => {
    g.next(content);
  })
})
复制代码

以上可以达到我们的目的,但是过于繁琐,我们想要的是generator函数可以自动的执行,可以写一个简易的自动执行函数如下:

function genRun(gen) {
  const g = gen();
  
  next();

  function next(err, pre) {
    let temp;
    (err === null) && (temp = g.next(pre));
    (err !== null) && (temp = g.throw(pre));

    if(!temp.done) {
      nextWithYieldType(temp.value, next);
    }
  }
}

function nextWithYieldType(value, next) {
  if(isPromise(value)) {
    value
      .then(success => next(null, success))
      .catch(error => next(error))
  } 
}

genRun(gen);
复制代码

此时generator函数便可以自动执行,事实上我们可以发现,generator的内部状态完全是由 nextWithYieldType 决定的,我们可以根据yield的类型执行不同的处理逻辑。

Effect

事实上 sagaMiddleware.run(saga) 可以类似看做 genRun(saga) ,而saga是由一个个的effect组成的,那么effect是什么?redux-saga官网的解释:一个 effect 就是一个 Plain Object JavaScript 对象,包含一些将被 saga middleware 执行的指令。redux-saga提供了很多Effect创建器,如 callputtake 等,已 call 为例:

function saga*() {
  const result = yield call(genPromise);
  console.log(result);
}
复制代码

call(genPromise) 生成的就是一个effect,它可能类似如下:

{
  isEffect: true,
  type: 'CALL',
  fn: genPromise
}
复制代码

事实上effect只表明了意图,而实际的行为由类似于上文的nextWithYieldType完成,例如:

function nextWithYieldType(value, next) {
  ...
  if(isCallEffect(value)) {
    value.fn(). then(success => next(null, success)).catch(error => next(error))  
  } 
}
复制代码

当genPromise函数返回的promise被resolve后便会打印出结果。

生产者与消费者

观察下面的例子

function *saga() {
  yield take('TEST');
  console.log('test...');
}

sagaMiddleware.run(test);
复制代码

saga会在 take('TEST') 处阻塞,只有执行了 dispatch({type: 'TEST'}) 后saga才能继续运行(注意:此时的 dispatch 方法是经过sagaMiddleware包装过的)。这给我们的感觉似乎很像是 take 是一个生产者,在等待 disaptch 的消费,事实上 take 只是一个Effect生成器,具体的处理逻辑依然是在nextWithYieldType完成的,类似于:

function nextWithYieldType(value, next) {
  ...
  // take('TEST')生成的effect简单的认为是  {isEffect: true, type: 'TAKE', name: 'TEST'}
  if(isTakeEffect(value)) {
    channel.take({pattern: value.name, cb: params => next(null, params)})  
  } 
}
复制代码

channel是一个任务生成器,它有两个方法:take生成任务,put消费任务:

function channel() {
  /*
    task = {
      pattern,
      cb
    }
  */
  let _task = null;

  function take(task) {
    _task = task;
  }

  function put(pattern, args) {
    if(!_task) return;
    if(pattern == _task.pattern) _task.cb.call(null, args);
  }

  return {
    take,
    put
  }
}
复制代码

显然任务是在执行 dispatch 的时候被消费掉的,这个工作是在sagaMiddleware中做的,类似于如下:

const sagaMiddleware = store => {
  return next => action => {
    next(action);
    
    const { type, ...payload } = action;
    channel.put(type, payload);
  }
} 
复制代码

看到这里我们可以发现,需要我们做的就是不断的完善nextWithYieldType这个函数,当完成了 putforktakeEvery 对应的逻辑后,一个具备基本功能的redux-saga就诞生啦,笔者就不在赘述这些功能的实现了。最后,你可以查看这里: tiny-redux-saga ,这是笔者实现的一个简易版的redux-saga,希望对你有所帮助。

全文完。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

An Introduction to Genetic Algorithms

An Introduction to Genetic Algorithms

Melanie Mitchell / MIT Press / 1998-2-6 / USD 45.00

Genetic algorithms have been used in science and engineering as adaptive algorithms for solving practical problems and as computational models of natural evolutionary systems. This brief, accessible i......一起来看看 《An Introduction to Genetic Algorithms》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具