onvno / pokerface

日常技术文章阅读整理
3 stars 0 forks source link

20200409 - generator & saga #91

Open onvno opened 4 years ago

onvno commented 4 years ago

提到saga,就必然会说到generator.而说到generator,就要讲下promise的区别。

最简单的换来说: promise: 好比机关枪没办法一发一发发射。 generator:一枪一下,随时开关控制。

所以从精细化控制,一些库用到了generator,如saga.

整体过了遍saga,在这里自我理解作者追求几个点:

关于他的一些思路,这里介绍了他的好处,链接中有流程图,可以作为以后深入了解查看:

onvno commented 4 years ago

首先,saga既然可以做到精细控制,能控制请求取消吗。 个人认为可以,也不可以。 如果是任务塞进异步队列的,那取消任务,没发出的自然可以取消请求。 但是如果已发出的,那这个取消,就只能通过请求库自己来处理了,如xhr的abort,axios的cancel

P.S:fetch没有原生的取消,这类可能需要动动脑筋了

onvno commented 4 years ago

最近看的几篇文章:

onvno commented 4 years ago

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限定
onvno commented 4 years ago
onvno commented 4 years ago

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.
onvno commented 4 years ago

redux-saga官方文档学习

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:一个后台运行的进程任务
onvno commented 4 years ago

源码解析几篇,需要单独抽时间看下: