Open onvno opened 4 years ago
首先,saga既然可以做到精细控制,能控制请求取消吗。
个人认为可以,也不可以。
如果是任务塞进异步队列的,那取消任务,没发出的自然可以取消请求。
但是如果已发出的,那这个取消,就只能通过请求库自己来处理了,如xhr的abort
,axios的cancel
P.S:fetch
没有原生的取消,这类可能需要动动脑筋了
最近看的几篇文章:
promise的发展历程和优缺点:介绍了promise,generator的symbol.interator方式,以及async的babel编译实现,可以了解。
JS 异步解决方案的发展历程以及优缺点:简单明了,面试专用
Promise缺点:无法取消 Promise ,错误需要通过回调函数来捕获 Generotar特点:可以控制函数的执行,可以配合 co 函数库使用 Async/await:优点是:代码清晰,不用像 Promise 写一大堆 then 链,处理了回调地狱的问题 缺点:await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用 await 会导致性能上的降低。
Redux-Saga 实用指北 这篇文章是满满的诚意,还有Github仓库:redux-saga-example 整篇没有废话,干货满满。
关于saga
为什么不用async,await
代替generator
:
A few issues have been raised asking whether Redux saga plans to use async/await syntax instead of generators. We will continue to use generators. The primary mechanism of async/await is Promises and it is very difficult to retain the scheduling simplicity and semantics of existing Saga concepts using Promises. async/await simply don't allow for certain things - like i.e. cancellation. With generators we have full power over how & when effects are executed.
关于几个常用的effect-helper API介绍:
takeLatest, // 短时间内(没有执行完函数)多次触发的情况下,指会触发相应的函数一次
takeEvery, // takeLatest 的同胞兄弟,不同点是每次都会触发相应的函数
put, // 作用跟 dispatch 一毛一样,可以就理解为dispatch
call // fork 的同胞兄弟,不过fork是非阻塞,call是阻塞,阻塞的意思就是到这块就停下来了
race 雷同promise.race,可用于接口timeout限定
在选择要系统地学习一个新的 框架/库 之前,首先至少得学会先去思考以下两点: • 它是什么? • 它解决了什么问题? 然后,才会带着更多的好奇心去了解:它的由来、它名字的含义、它引申的一些概念,以及它具体的使用方式...
以下是几个不常用的,在官网看到:
import { runSaga, stdChannel } from 'redux-saga'
const emitter = new EventEmitter() const channel = stdChannel() emitter.on("action", channel.put)
const myIO = { // this will be used to orchestrate take and put Effects channel, // this will be used to resolve put Effects dispatch(output) { emitter.emit("action", output) }, // this will be used to resolve select Effects getState() { return state } }
runSaga( myIO, function* saga() { ... }, )
* 使用`channel`控制任务顺序执行
区别于`fork`的并发,以及`call`的逐个执行,`channel`可以对任务实现顺序执行,一个执行完执行下一个,还可以设置同类任务的最大执行次数
import { take, actionChannel, call, ... } from 'redux-saga/effects'
function* watchRequests() { // 1- Create a channel for request actions const requestChan = yield actionChannel('REQUEST') while (true) { // 2- take from the channel const {payload} = yield take(requestChan) // 3- Note that we're using a blocking call yield call(handleRequest, payload) } }
function* handleRequest(payload) { ... }
理解本质上还是利用了`call`底层API的阻塞,`channel`不过是做了个缓存
* 接入`eventChannel `自定义事件
不使用saga提供的事件如`call`等,自定义实现。
import { eventChannel, END } from 'redux-saga'
function countdown(secs) { return eventChannel(emitter => { const iv = setInterval(() => { secs -= 1 if (secs > 0) { emitter(secs) } else { // this causes the channel to close emitter(END) } }, 1000); // The subscriber must return an unsubscribe function return () => { clearInterval(iv) } } ) }
调用
import { take, put, call } from 'redux-saga/effects' import { eventChannel, END } from 'redux-saga'
// creates an event Channel from an interval of seconds function countdown(seconds) { ... }
export function* saga() {
const chan = yield call(countdown, value)
try {
while (true) {
// take(END) will cause the saga to terminate by jumping to the finally block
let seconds = yield take(chan)
console.log(countdown: ${seconds}
)
}
} finally {
console.log('countdown terminated')
}
}
同时还支持cancel
* channel与`sagas`通信
大致理解是通过常见channel,可以手动触发任务,限制并发任务量,做限流。但是没有看明白完成后如何自动添加。
import { channel } from 'redux-saga' import { take, fork, ... } from 'redux-saga/effects'
function* watchRequests() { // create a channel to queue incoming requests const chan = yield call(channel)
// create 3 worker 'threads' for (var i = 0; i < 3; i++) { yield fork(handleRequest, chan) }
while (true) { const {payload} = yield take('REQUEST') yield put(chan, payload) } }
function* handleRequest(chan) { while (true) { const payload = yield take(chan) // process the request } }
> All the three workers run a typical while loop. On each iteration, a worker will take the next request, or will block until a message is available. Note that this mechanism provides an automatic load-balancing between the 3 workers. Rapid workers are not slowed down by slow workers.
redux-saga官方文档学习
支持截流,防抖,请求重试:Recipes
阻塞调用/非阻塞调用
import {call, cancel, join, take, put} from "redux-saga/effects"
function* saga() { yield take(ACTION) // Blocking: will wait for the action yield call(ApiFn, ...args) // Blocking: will wait for ApiFn (If ApiFn returns a Promise) yield call(otherSaga, ...args) // Blocking: will wait for otherSaga to terminate
yield put(...) // Non-Blocking: will dispatch within internal scheduler
const task = yield fork(otherSaga, ...args) // Non-blocking: will not wait for otherSaga yield cancel(task) // Non-blocking: will resume immediately // or yield join(task) // Blocking: will wait for the task to terminate }
这里`put`中英文文档不同,不过直接执行dispatch,也无所谓强制理解为阻塞或者不阻塞
* [API Reference](https://redux-saga.js.org/docs/api/):API参考
* 名词解释
Effect:返回的就是一个JS对象
Task:一个后台运行的进程任务
源码解析几篇,需要单独抽时间看下:
提到
saga
,就必然会说到generator
.而说到generator
,就要讲下promise
的区别。最简单的换来说:
promise
: 好比机关枪没办法一发一发发射。generator
:一枪一下,随时开关控制。所以从精细化控制,一些库用到了
generator
,如saga
.整体过了遍saga,在这里自我理解作者追求几个点:
关于他的一些思路,这里介绍了他的好处,链接中有流程图,可以作为以后深入了解查看:
Saga Pattern | How to implement business transactions using Microservices
Avoid cyclic dependencies between services, as the saga orchestrator invokes the saga participants but the participants do not invoke the orchestrator
Centralize the orchestration of the distributed transaction
Reduce participants complexity as they only need to execute/reply commands.
Easier to be implemented and tested
The transaction complexity remains linear when new steps are added
Rollbacks are easier to manage
If you have a second transaction willing to change the same target object, you can easily put it on hold on the orchestrator until the first transaction ends.