24 SEPTEMBER 2016
原文在这里
(这个文章是有关Redux-saga三部曲的第一部分,第二部分,第三部分.我们会构建一个抽象的时钟)
第一部分译文请见,
第二部分译文请见,
第三部分译文请见.
我的观点是:React/Redux组合是目前最好的前端技术栈.这两个库是相互依存的,因为React可以是纯函数,功能是:state=>view
,React仅仅关注怎么根据state来渲染内容,Redux也是纯函数,功能是:action=>state
,Redux作为控制app state的单一资源(译者观点:这个地方的state,需要更加开阔眼界,并不是仅仅是UI的状态,组件需要的远程数据也可以是state的一部分,如此以来,Redux的意义就更加凸显出来).即便是拥有如此先进的技术,你最终也会发现,app的控制流代码被一位闲逛的流浪汉搞得好像是一团烂泥.在实际的应用中,最复杂的控制流代码通常包含异步代码,异步代码和其他时序的代码总是协同工作.
- “当POST请求返回时,转到下一页或者显示错误信息.”
- “当移动设备的电力降到5%一下的时候,显示一条⚠️信息.”
- “十秒钟以后,隐藏模态框.”
- “但我的直属领导在附近晃荡的时候,从FB切换到JIRA.”(译者:这肯定是个幽默)
在一些app中,这些“无家可归”的代码开始骚骚扰有礼貌的邻居.(译者:后面这段没有翻译出来,意思是异步代码会把同步代码搞得一团糟.)
与无家可归者战斗
javascript库Redux-saga是为Redux设计的一个中间件,为异步操作提供了解决办法:把数据流中的异步代码使用生成器代码块(generator)隔离起来,generator的yield描述一些代码块:effects
,这些effects
火灾控制流的某个给定点发生作用.
Saga作为异步代码的收容所,接纳app的异步代码块.saga严重依赖生成器函数,因此我们在正式学习Redux-saga之前,需要了解一下生成器函数.在这个文章中,我们会涉及到内在的合理原因和使用Redux-saga的好处,接着我会教你一些必要的背景知识.好了,到底什么是generator,我们为什么需要他?
wiki关于generators的解释下面这个内容
生成器非常类似于返回一个数组的函数,在生成器内有可以被调用的参数,生成器是一系列的值.然而与返回一个数组的函数一次返回整个数组不同,生成器函数一次只返回一个值.一句话,生成器函数看起来就是一个函数,但是行为像是一个遍历器.
我觉得从上面这段话根本就没有学到任何东西,那么看看下面这段
生成器概念首次提出是在1975年(译者:题目就是来源于此),作为CLU语言的字符串操作特征,现在Python,C#,Ruby中都可以使用,在javascript的ES6版本中也会加入,在CLU和C#中生成器叫做iterators,在Ruby中叫做enumerators.
所以生成器函数的概念是比较老也是广为所知的概念,但是直到最近才加入ECMAScript.但是为什么现在在javascript中要关注它呢?
javascript的运行环境让我们成为了非常不幸的程序员,在浏览器内部仅仅只有一个单线程事件循环模型.我们所有的代码都在同一个线程中执行,因此在javascript程序员的脑袋里一定要牢固的植入一个概念,永远永远不要阻塞代码执行,因为阻塞的代码块将会终端其他所有的其他代码的执行.在非阻塞情况下,我们有各种办法来处理异步代码执行,其实都是各种形式的回调函数的注册方法.回调函数是每一个javascript开发者必须熟练掌握的内容,如果你还不了解回调函数和promises对象,在开始下面内容之前要了解一下.否则的话你看到生成器的时候会疯掉的.
时刻记住,不要暂停代码执行,让我们看看ES6生成器的实现.你也可以读读收到高度评价的2ality Blog,
生成器是ECMAScript6的新特征,生成器可以暂停和恢复执行.这个特点对于遍历器和异步编程都大有帮助.
这都是什么鬼话.他只说生成器是“可以暂停和恢复的函数!!”我们被允许,鼓励在生成器内暂停和恢复代码执行.这一点完全和不要阻塞代码执行不一样.现在我们已经有了一个编程构架允许我们编写可以暂停和恢复执行的代码.这就是为什么生成器函数成为一个js异步编程进化的里程碑.
ES6生成器摘要
你不需要具有博士学位才可以使用Redux-saga,但是基础知识还是需要的.下面的内容是ES6生成器绝对的精华部分:
- 一个generator函数以
function *
(“function star”)开始,看起来像下面的样子:
function* genFunc () {
// code goes here
}
- generator函数返回一个generator
- generator不是一个函数,该死的.看起来像是generator函数返回的一个特殊对象:
let genObj=genFunc()
- 在generator中可以使用
yield
终止一个generator的执行. - 不要忘了那个✨符号
function* example(){yield 'scheibe'}
- 不能使用
new
关键字来调用一个generator函数.我们可以一直调用generator函数,他总是返回一个新的generator对象,函数体执行会中断,直到我们调用next
:
function* genFunc () { yield 'hi' }
let genObj1 = genFunc()
let genObj2 = genFunc()
genObj1.next()
{value: "hi", done: false }
genObj1.next()
{ value: undefined, done: true }
genObj2.next()
{ value: "hi", done: false }
- 从上面的代码可以看到,当我们调用
next
方法像这样genObj.next()
,直到下一个yield
关键字的时候才可以执行,接着返回yield
的表达式形式,格式为{value:any,done:boolean}
,“any”是yield的值,”done”告诉我们generator还有没有剩下的代码需要yield. - 下一次返回的时候,generator从这个点继续执行.但是你是监听不到这个过程的.
- 如果整个generator完成,如果再执行
next()
调用,将返回{value:undefined,}
. - 也可以通过参数传值到generator:
function* test () {
while (true) {
console.log('FIRST STEP')
var x = yield 1
console.log('SECOND STEP')
yield x * 2
}
}
> let gen = test()
> gen.next()
// executes until the first yield, sends out its value and waits
FIRST STEP
{ value: 1, done: false }
gen.next(29)
// sends in 29 to the first yield, which is waiting, assigns it to x, then executes until the next yield and sends out its value
SECOND STEP
{ value: 58, done: false }
gen.next(2)
// paused at the second yield, we execute up until the first yield again, the value passed to next gets thrown away since we didn't assign it to anything
FIRST STEP
{ value: 1, done: false }
gen.next()
// sends in no value to the paused first yield (undefined), and executes until the second yield, sending out the result of its operand expression
SECOND STEP
{ value: NaN, done: false }
- 所以
next
可以被分为三个步骤:1.可以传递一个值到目前挂起的yield 2.返回下一个yield的操作符 3.在yield中挂起
好了,确实有很多的东西要消化,但是现在你已经知道怎么传递一个值到generator.你或许会想着慢慢研究这些代码直到搞清楚每件事.确保调用next
的时候,记住三个步骤.最好能记住这些步骤.这些步骤不仅仅对于理解generator,Redux和Saga很关键,如果我们写测试saga代码的时候,这些步骤也变得非常有用.最终的两个结论:
- 只能在generator函数中yield,在回调函数中是不行的.例如下面的代码是错误的:
function* genFunc () { [1, 2].map(x => yield x) }
- 可以使用yield*把控制权传递到另一个generator:
function* foo () {
yield* bar()
yield 3
}
function* bar () {
yield 1
yield 2
}
> let gen = foo()
> gen.next()
{ value: 1, done: false }
> gen.next()
{ value: 2, done: false }
> gen.next()
{ value: 3, done: false }
> gen.next()
{ value: undefined, done: true }
我期望你能重新读读上面的列表项的内容,在浏览器的console中练习一下generators代码.如果你对这些代码熟悉了以后,就可以准备开始Redux Saga app了.