昨天认真看完了Promise 与 Generator,今天早上认真学了一下redux-saga.js。把笔记输出到这里
作用类似于dispatch,用来put 异步 的 generator function
我的理解是 调用api , 发起请求,也可以用来call 同步的 action
官方说明
与前面的例子不同的是,现在我们不立即执行异步调用,相反,
call
创建了一条描述结果的信息。 就像在 Redux 里你使用 action 创建器,创建一个将被 Store 执行的、描述 action 的纯文本对象。call
创建一个纯文本对象描述函数调用。redux-saga
middleware 确保执行函数调用并在响应被 resolve 时恢复 generator。
需要注意的,写异步流的时候
// 错误写法,effects 将按照顺序执行
const users = yield call(fetch, ‘/users‘),
repos = yield call(fetch, ‘/repos‘)
// 由于第二个 effect 将会在第一个 call 执行完毕才开始。所以我们需要这样写:
import { call } from ‘redux-saga/effects‘
// 正确写法, effects 将会同步执行
const [users, repos] = yield [
call(fetch, ‘/users‘),
call(fetch, ‘/repos‘)
]
非常常见,提供了类似 redux-thunk的行为
// 首先我们创建一个将执行异步 action 的任务:
import { call, put } from ‘redux-saga/effects‘
export function* fetchData(action) {
try {
const data = yield call(Api.fetchUser, action.payload.url);
yield put({type: "FETCH_SUCCEEDED", data});
} catch (error) {
yield put({type: "FETCH_FAILED", error});
}
}
// 然后在每次 FETCH_REQUESTED action 被发起时启动上面的任务。
import { takeEvery } from ‘redux-saga‘
function* watchFetchData() {
yield* takeEvery(‘FETCH_REQUESTED‘, fetchData)
}
在上面的例子中,takeEvery
允许多个 fetchData
实例同时启动。在某个特定时刻,尽管之前还有一个或多个 fetchData
尚未结束,我们还是可以启动一个新的 fetchData
任务,
如果我们只想得到最新那个请求的响应(例如,始终显示最新版本的数据)。我们可以使用 takeLatest
辅助函数。
import { takeLatest } from ‘redux-saga‘
function* watchFetchData() {
yield* takeLatest(‘FETCH_REQUESTED‘, fetchData)
}
和 takeEvery
不同,在任何时刻 takeLatest
只允许一个 fetchData
任务在执行。并且这个任务是最后被启动的那个。 如果已经有一个任务在执行的时候启动另一个 fetchData
,那之前的这个任务会被自动取消。
根据 在组件中dispatch一个action的例子中,如果要在effects中对于param数据和当前的state数据进行再出处理,这里怎么获取state呢?采用select,如下:
export default {
namespace: ‘example‘,
state: {num:1}, //表示当前的example中的state状态,这里可以给初始值,这里num初始为1
effects: { //这里是做异步处理的
*addByONe({ param}, { call, put,select }) { //这里使用select
const num = yield select(state => state.num) //这里就获取到了当前state中的数据num
//方式二: const num = yield select(({num}) =>num)
//方式三: const num = yield select(_ =>_.num)
let param1;
param1 = num + param; 这里就可以使用num进行操作了
yield put({
type: ‘save‘,
num:param1
});
}
},
//用来保存更新state值 上面的put方法调用这里的方法
reducers: {
save(state, action) { //这里的state是当前总的state,这里的action包含了上面传递的参数和type
return { ...state, ...action.num }; //这里用ES6语法来更新当前state中num的值
},
},
};
take
就像我们更早之前看到的 call
和 put
。它创建另一个命令对象,告诉 middleware 等待一个特定的 action。 正如在 call
Effect 的情况中,middleware 会暂停 Generator,直到返回的 Promise 被 resolve。 在 take
的情况中,它将会暂停 Generator 直到一个匹配的 action 被发起了。 在以上的例子中,watchAndLog
处于暂停状态,直到任意的一个 action 被发起。
import { select, take } from ‘redux-saga/effects‘
function* watchAndLog() {
while (true) {
const action = yield take(‘*‘)
const state = yield select()
console.log(‘action‘, action)
console.log(‘state after‘, state)
}
}
一个简单的例子,假设在我们的 Todo 应用中,我们希望监听用户的操作,并在用户初次创建完三条 Todo 信息时显示祝贺信息。
import { take, put } from ‘redux-saga/effects‘
function* watchFirstThreeTodosCreation() {
for (let i = 0; i < 3; i++) {
const action = yield take(‘TODO_CREATED‘)
}
yield put({type: ‘SHOW_CONGRATULATION‘})
}
使用拉取(pull)模式,我们可以在同一个地方写控制流,而不是重复处理相同的 action。
function* loginFlow() {
while (true) {
yield take(‘LOGIN‘)
// ... perform the login logic
yield take(‘LOGOUT‘)
// ... perform the logout logic
}
}
为了表示无阻塞调用,redux-saga 提供了另一个 Effect:fork
。 当我们 fork 一个 任务,任务会在后台启动,调用者也可以继续它自己的流程,而不用等待被 fork 的任务结束。
所以为了让 loginFlow
不错过一个并发的 LOGOUT
,我们不应该使用 call
调用 authorize
任务,而应该使用 fork
。
import { take, call, put, cancelled } from ‘redux-saga/effects‘
import Api from ‘...‘
function* authorize(user, password) {
try {
const token = yield call(Api.authorize, user, password)
yield put({type: ‘LOGIN_SUCCESS‘, token})
yield call(Api.storeItem, {token})
return token
} catch(error) {
yield put({type: ‘LOGIN_ERROR‘, error})
} finally {
if (yield cancelled()) {
// ... put special cancellation handling code here
}
}
}
如果我们收到一个 LOGOUT
action,我们将那个 task 传入给 cancel
Effect。 如果任务仍在运行,它会被中止。如果任务已完成,那什么也不会发生,取消操作将会是一个空操作(no-op)。最后,如果该任务完成了但是有错误, 那我们什么也没做,因为我们知道,任务已经完成了。
import { take, call, put, cancelled } from ‘redux-saga/effects‘
import Api from ‘...‘
function* authorize(user, password) {
try {
const token = yield call(Api.authorize, user, password)
yield put({type: ‘LOGIN_SUCCESS‘, token})
yield call(Api.storeItem, {token})
return token
} catch(error) {
yield put({type: ‘LOGIN_ERROR‘, error})
} finally {
if (yield cancelled()) {
// ... put special cancellation handling code here
}
}
}
有时候我们同时启动多个任务,但又不想等待所有任务完成,我们只希望拿到 胜利者:即第一个被 resolve(或 reject)的任务。 race
Effect 提供了一个方法,在多个 Effects 之间触发一个竞赛(race)。
// 下面的示例演示了触发一个远程的获取请求,并且限制了 1 秒内响应,否则作超时处理。
import { race, call, put } from ‘redux-saga/effects‘
import { delay } from ‘redux-saga‘
function* fetchPostsWithTimeout() {
const {posts, timeout} = yield race({
posts: call(fetchApi, ‘/posts‘),
timeout: call(delay, 1000)
})
if (posts)
put({type: ‘POSTS_RECEIVED‘, posts})
else
put({type: ‘TIMEOUT_ERROR‘})
}
race
的另一个有用的功能是,它会自动取消那些失败的 Effects。例如,假设我们有 2 个 UI 按钮:
while(true)
中(例如:每 x 秒钟从服务器上同步一些数据)import { race, take, call } from ‘redux-saga/effects‘
function* backgroundTask() {
while (true) { ... }
}
function* watchStartBackgroundTask() {
while (true) {
yield take(‘START_BACKGROUND_TASK‘)
yield race({
task: call(backgroundTask),
cancel: take(‘CANCEL_TASK‘)
})
}
}
原文:https://www.cnblogs.com/ssaylo/p/12808218.html