-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathsagaToolkit.js
152 lines (116 loc) · 3.54 KB
/
sagaToolkit.js
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import { createAsyncThunk, unwrapResult } from '@reduxjs/toolkit'
import { put, take, fork, takeEvery, cancel } from '@redux-saga/core/effects'
import createDeferred from '@redux-saga/deferred'
const requests = {}
const addRequest = requestId => {
const deferred = createDeferred()
const request = {
...requests[requestId],
requestId,
deferred,
}
if (requests[requestId]) {
requests[requestId].deferred = deferred
requests[requestId].onAdd(request)
} else {
requests[requestId] = request
}
return deferred.promise
}
export const createSagaAction = type => {
const thunk = createAsyncThunk(type, (_, { requestId }) => addRequest(requestId))
function actionCreator(...args) {
const originalActionCreator = thunk(...args)
return (...args) => {
const promise = originalActionCreator(...args)
requests[promise.requestId].abort = promise.abort
return promise
}
}
actionCreator.pending = thunk.pending
actionCreator.rejected = thunk.rejected
actionCreator.fulfilled = thunk.fulfilled
actionCreator.typePrefix = thunk.typePrefix
actionCreator.type = thunk.pending
return actionCreator
}
const cleanup = requestId => {
delete requests[requestId]
}
function* getRequest(requestId) {
const request = requests[requestId]
if (!request) {
return yield (new Promise(onAdd => {
requests[requestId] = { onAdd }
}))
}
return request
}
const wrap = saga => function* (action, ...rest) {
const { requestId } = action.meta
const request = yield getRequest(requestId)
const deferred = request.deferred
try {
deferred.resolve(yield saga(action, ...rest))
} catch (error) {
deferred.reject(error)
} finally {
cleanup(requestId)
}
}
export function takeEveryAsync(pattern, saga, ...args) {
return takeEvery(pattern, wrap(saga), ...args)
}
export function takeLatestAsync(pattern, saga, ...args) {
const tasks = {}
let deferred
function* wrapper(action, ...rest) {
if (deferred) {
const lastRequestId = yield deferred.promise
const request = yield getRequest(lastRequestId)
request.abort()
const task = yield tasks[lastRequestId].promise
yield cancel(task)
}
deferred = createDeferred()
const { requestId } = yield getRequest(action.meta.requestId)
deferred.resolve(requestId)
yield wrap(saga)(action, ...rest)
deferred = null
}
const takeEvery = (patternOrChannel, saga, ...args) => fork(function* () {
while (true) {
const action = yield take(patternOrChannel)
const { requestId } = action.meta
tasks[requestId] = createDeferred()
tasks[requestId].resolve(yield fork(saga, ...args.concat(action)))
}
})
return takeEvery(pattern, wrapper, ...args)
}
export function takeAggregateAsync(pattern, saga, ...args) {
let deferred
function* wrapper(action, ...rest) {
const { requestId } = action.meta
if (deferred) {
const request = yield getRequest(requestId)
const { resolve, reject } = request.deferred
const { promise } = yield deferred.promise
promise
.then(resolve, reject)
.finally(() => cleanup(requestId))
.catch(() => { })
} else {
deferred = createDeferred()
const request = yield getRequest(requestId)
const { promise } = request.deferred
yield wrap(saga)(action, ...rest)
deferred.resolve({ promise })
deferred = null
}
}
return takeEvery(pattern, wrapper, ...args)
}
export function* putAsync(action) {
return unwrapResult(yield (yield put(action)))
}