yield与dva流程控制 dva.js dva 首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch ,所以也可以理解为一个轻量级的应用框架。
yield
yield是ES6的新关键字 ,使生成器函数执行暂停,yield关键字后面的表达式的值返回给生成器的调用者。它可以被认为是一个基于生成器的版本的return关键字。
yield关键字实际返回一个IteratorResult(迭代器)对象,它有两个属性,value和done,分别代表返回值和是否完成。
yield无法单独工作,需要配合generator(生成器)的其他函数,如next,懒汉式操作,展现强大的主动控制特性。
迭代器(Iterator)和生成器(Generator) 迭代器 :迭代器是一种特殊对象,它具有一些专门为迭代过程设计的专有接口,所有的迭代器对象都有一个next()方法,每次调用都返回一个结果对象。
下面用ES5的语法创建一个迭代器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function createIterator (items ) { var i = 0 ; return { next: function ( ) { var done = (i >= items.length); var value = !done ? items[i++] : undefined ; return { done: done, value: value }; } }; }
生成器: 生成器是一种返回迭代器的函数,通过function关键字后的星号(*)来表示,函数中会用到新的关键字yield。
星号可以紧挨着function关键字,也可以在中间添加一个空格。
1 2 3 4 5 6 7 8 9 10 11 function *createIterator ( ) { yield 1 ; yield 2 ; yield 3 ; } let iterator = createIterator();console .log(iterator.next().value); console .log(iterator.next().value); console .log(iterator.next().value);
在这个示例中,createlterator()前的星号表明它是一个生成器;yield关键字也是ES6的新特性,可以通过它来指定调用迭代器的next()方法时的返回值及返回顺序 。生成迭代器后,连续3次调用它的next()方法返回3个不同的值,分别是1、2和3。生成器的调用过程与其他函数一样,最终返回的是创建好的迭代器。
1 2 3 4 5 6 7 8 9 10 11 12 13 function *createIterator (items ) { for (let i = 0 ; i < items.length; i++) { yield items[i]; } } let iterator = createIterator([1 , 2 , 3 ]);console .log(iterator.next()); console .log(iterator.next()); console .log(iterator.next()); console .log(iterator.next()); console .log(iterator.next());
生成器函数最有趣的部分是,每当执行完一条yield语句后函数就会自动停止执行。
【使用限制】yield关键字只可在生成器内部使用,在其他地方使用会导致程序抛出错误
1 2 3 4 5 6 function *createIterator *(items ) { items.forEach(function (item ) { yield item + 1 ; }); }
从字面上看,yield关键字确实在createlterator()函数内部,但是它与return关键字一样,二者都不能穿透函数边界。嵌套函数中的return语句不能用作外部函数的返回语句,而此处嵌套函数中的yield语句会导致程序抛出语法错误。
也可以通过函数表达式来创建生成器,只需在function关键字和小括号中间添加一个星号(*)即可
可迭代对象 可迭代对象具有Symbol.iterator属性,是一种与迭代器密切相关的对象。
Symbol.iterator通过指定的函数可以返回一个作用于附属对象的迭代器。在ES6中,所有的集合对象(数组、Set集合及Map集合)和字符串都是可迭代对象 ,这些对象中都有默认的迭代器。ES6中新加入的特性for-of循环需要用到可迭代对象的这些功能。
……
简单模拟dva处理effects函数 经过以上对于yield与迭代器的分析,我们可以以call为例模拟dva的处理流程。
遍历model下的effects
遍历effects下的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 function * requestCom ( ) { let comRes = yield getCommit let repRes = yield getReply.bind(this , comRes.id) console .log(repRes); } const getCommit = () => { return { id : "1" , value : "评论" } } const getReply = (id ) => { if (!id) return null return [ { id : 101 , content : "hahah" }, { id : 102 , content : "heheh" }, { id : 103 , content : "huhuh" }, { id : 104 , content : "xixix" }, ] } let resCommIterator = requestCom() let comValue = resCommIterator.next().value;let repValue = resCommIterator.next(comValue()).valuelet result = repValue()console .log(comValue());console .log(repValue());
业务代码中的写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 * fetch_add_topic ({}, { call, put, select } ) { const commResponse = yield call(get_topic); if (ValidResponse(commResponse)) { let id = commResponse.data.id const replyResponse = yield call(get_reply, {id}); if (ValidResponse(commResponse)) { message.success("获取成功!" ) yield put({ type: 'saveReply' , payload: { data : replyResponse.data, topic_id : payload.topic_id}, }); } } }, export async function get_topic (params ) { return request('/dev/site/topic' , { method: 'GET' , params, }); } export async function get_reply (params ) { return request('/dev/site/reply' , { method: 'GET' , params, }); }
源码分析 dva/ createPromiseMiddleware.js
.umi/plugin-dva/dva.ts