bai3/note

声明式Effects

Opened this issue · 0 comments

bai3 commented

声明式Effects

在 redux-saga的世界里,Sagas都是用Generator函数实现。我们从Generator里yield纯Javascript对象以表达Saga逻辑。我们称呼那些对象为Effect、Effect是一个简单的对象,这个对象包含了一些给middleware解释执行的信息。你可以把Effect看做是发送给middleware的指令以执行某些操作(调用某些异步函数,发起一个action到store)

介绍一些基础的Effect以及概念。

Sagas可以多种形式yield Effect。最简单的方式是yield一个Promise。

举个例子,假设我们有个监听 PRODUCTS_REQUESTED action 的Saga。每次匹配搭配action,它会启动一个从服务器上获取产品列表的任务。

import { takeEvery } from 'redux-saga/effects'
import Api from './path/to/api'

function* watchFetchProducts() {
	yield takeEvery('PROFUCTS_REQUESTED', fetchProducts)
}

function* fetchProducts() {
	const products = yield Api.fetch('/products')
    console.log(products)
}

在上面的例子中,我们在Generator中直接调用了Api.fetch(在Generator函数中,yield右边的任何表达式都会被求值,结果会被yield给调用者。)

Api.fetch('/products') 触发了一个AJAX请求并返回一个Promise,Promise会resolve请求的响应,这个AJAX请求将立即执行。

......

实际上我们需要的只是保证 fetchProducts 任务yield 一个调用正确的函数,并且函数有着正确的参数。

相比于在Generator中直接调用异步函数,我们可以仅仅yield一个描述函数调用的信息。也就是说,我们简单地yield 一个看起来像下面这样的对象:

{
    CALL: {
        fn: Api.fetch,
        args: ['./products']
    }
}

换句话说,Generator将会yield包含指令的文本对象。redux-saga middleware将确保执行这个指令并将指令的结果反馈给Generator。这样的话,在测试Generator时,所有我们需要做的就是,将yield后的对象作一个简单的deepEqual 来检查它是否yield了我们期望的指令。

出于这样的原因,redux-saga 提供了一个不一样的方式来执行异步调用

import { call } from 'redux-saga/effects'

function* fetchProducts() {
    const products = yield call(Api.fetch, '/products')
}

我们使用了call(fn, ...args) 这个函数。与前面的例子不同的是,现在我们不立即执行异步调用,相反,call创建了一条描述结果的信息。就像在Redux里你使用action创建器,创建一个将被Store执行的、描述action的纯文本对象。call创建一个纯文本对象描述函数调用。redux-saga middleware 确保执行函数调用并在响应被resolve时恢复generator。

这让你能容易地测试Generator,就算它在Redux环境之外。因为call只是一个返回纯文本对象的函数而已

call同样支持调用对象方法,你可以使用以下形式,为调用的函数提供一个this 上下文

yield call([obj, obj.method], args, arg2,...)

apply 提供另外一种调用的方式

yield apply(obj,obj.method,[arg1,arg2,...])