What's the difference between join and all in Redux Saga?
function* fetchUserData(userId) {
const postsTask = yield fork(fetchAlbums, userId)
const albumsTask = yield fork(fetchPosts, userId)
yield join([postsTask, albumsTask])
}
function* fetchUserData(userId) {
const postsTask = yield fork(fetchAlbums, userId)
const albumsTask = yield fork(fetchPosts, userId)
yield all([postsTask, albumsTask])
}
Seems that both of them just syncing several tasks.
in short, one is "synchronous"/join and the other "asynchronous"/all,
looking at the documentation:
join([...tasks]) = It wraps the array of tasks in join effects, roughly becoming the equivalent of yield tasks.map(t => join(t)) - https://redux-saga.js.org/docs/api/#jointask
join will resolve to the same outcome of the joined task (success or error). If the joined task is cancelled, the cancellation will also propagate to the Saga executing the join effect. Similarly, any potential callers of those joiners will be cancelled as well.
and:
all([...effects]) - parallel effects = Creates an Effect description that instructs the middleware to run multiple Effects in parallel and wait for all of them to complete. It's quite the corresponding API to standard Promise#all - https://redux-saga.js.org/docs/api/#alleffects---parallel-effects
Related
I am working on a project with Firestore + Cloud Function where a user's "credits" balance will be updated on many occasions.
For each update to the main balance we are maintaining a separate ledger in a separate collection with the details of all the movements (date, amounts etc.).
There are several areas of the codebase that interact with the balance.
I am contemplating using a Firestore background function with retries on to take care of updating my ledger on every update to the balance mainly to avoid duplication of logic throughout the codebase.
The tradeoff however is that what could be done in one transaction now needs two:
A) Transaction to update the balance (from wherever in the codebase)
B) Another transaction from the CF to log the details to the ledger
Is this a bad idea (if the CF is configured to retry)?
Thanks,
If I understood correctly your question, you don't need to use two transactions. You can very well execute the two writes (one update and one creation) within the same Transaction, as an atomic operation.
Something along the following lines with the JS SDK:
const balanceDocRef1 = doc(db, 'balances', '....');
const historyDocRef = doc(db, 'history', '....');
try {
await runTransaction(db, async (transaction) => {
const balanceDoc = await transaction.get(balanceDocRef1);
const newBalance = balanceDoc.data().balance + ....;
transaction
.update(balanceDocRef1, { balance: newBalance })
.set(historyDocRef, { amount: '...', date: '...' });
});
console.log('Transaction successfully committed!');
} catch (e) {
console.log('Transaction failed: ', e);
}
my code looks like below:
exports.XXX = functions.database.ref("/global/tokenEarned")
.onUpdate(async (change, context) => {
let newValue = change.after.val();
const oldValue = change.before.val()
if(oldValue !== newValue){
const diff = newValue - oldValue
const resp = await db.ref("/global").once("value")
const respData = resp.val()
await db.ref("global").update({"totalDivi" : respData.totalDivi + divi})
}
return null
})
As you can see that i am updating the attribute "totalDivi" whenever another attribute "tokenEarned" gets updated with the difference between new and old value.
this works as expected. However, how this will work in a situation when multiple people are updating the value of tokenEarned? will firebase queue the above function calls in the order value gets updated by multiple users?
Will firebase queue the above function calls in the order value gets
updated by multiple users?
No, there is no guarantee that the executions will follow any ordering, like ordering of triggering of events. The Cloud Function could even be executed simultaneously, in parallel’ if several server instances are spun up.
Depending on your exact use case you may need to go for another solution.
In our django-channels async consumer, i'm trying to retrieve a group result. This result may or may not be complete, and therefor i would like to add a callback to pipe results as they become ready. The problem is, the websocket is async, the callback must be sync, and self.send method must to async. Initially i assumed being able to just wrap the async send in async_to_sync, though it gives the error You cannot use AsyncToSync in the same thread as an async event loop - just await the async function directly., and therefor it seems that going async->sync->async isn't allowed.
class ImageConsumer(AsyncWebsocketConsumer):
async def celery_retrieve_group_result(self, batch_id):
group = group = celery.GroupResult().restore(batch_id)
res = group.get(callback=self.sync_callback)
def sync_callback(self, task_id, value):
async_to_sync(self.send_group_result)(task_id, value)
async def send_group_result(self, task_id , value):
await self.send(json.dumps({"type": "result.redraw_border", "result": value}))
I have tried different combinations, though my limiting factor is that the callback must be sync, and everything else is Async.
Does anyone have experience mixing celery with async django in this manner?
Any help would be appreciated!
Decided to go with a different approach of iterating over the enclosed AsyncResults.
class ImageConsumer(AsyncWebsocketConsumer):
name = "Image Consumer"
timeout = 20
sleep_duration = 5e-2
async def celery_retrieve_group_result(self, batch_id):
group = celery.GroupResult().restore(batch_id)
if group is not None:
ordered_list_of_async_results = group.results
for result in ordered_list_of_async_results:
start = time.time()
try:
while not result.ready():
await asyncio.sleep(self.sleep_duration)
if self.timeout and time.time() - start > self.timeout:
logger.exception(str(TimeoutError))
raise TimeoutError
value = result.get()
await self.send(json.dumps({'type': 'result.processing', 'result': value}))
except:
await self.send(json.dumps({'type': 'result.processing', 'error': str(TimeoutError)}))
group.forget()
else:
await self.send(json.dumps({'type': 'response.nonexistant_group', 'error': 'No result batch exists. re-upload'}))
This is working really well. The timeout is because Celery 4.3 currently has an issue with Redis backends, which if removed will cause a gridlock. So until that is fixed, this is working perfectly.
I'm still struggeling with the async/await pattern so I'm here to ask you some precisions.
I saw this page explaining the async/await pattern pretty well. I'm posting here the example that bother me :
import 'dart:async';
Future<String> firstAsync() async {
await Future<String>.delayed(const Duration(seconds: 2));
return "First!";
}
Future<String> secondAsync() async {
await Future<String>.delayed(const Duration(seconds: 2));
return "Second!";
}
Future<String> thirdAsync() async {
await Future<String>.delayed(const Duration(seconds: 2));
return "Third!";
}
void main() async {
var f = await firstAsync();
print(f);
var s = await secondAsync();
print(s);
var t = await thirdAsync();
print(t);
print('done');
}
In this example, each async method is called one after another, so the execution time for the main function is 6 seconds (3 x 2 seconds). However, I don't understand what's the point of asynchronous function if they are executed one after another.
Are async functions not supposed to execute in the background ? Is it not the point of multiple async functions to fastens the process with parrallel execution ?
I think I'm missing something about asynchronous functions and async/await pattern in flutter so if you could explain me that, it would be very appreciated.
Best
Waiting on multiple Futures to complete using Future.wait()
If the order of execution of the functions is not important, you can use Future.wait().
The functions get triggered in quick succession; when all of them complete with a value, Future.wait() returns a new Future. This Future completes with a list containing the values produced by each function.
Future
.wait([firstAsync(), secondAsync(), thirdAsyncC()])
.then((List responses) => chooseBestResponse(responses))
.catchError((e) => handleError(e));
or with async/await
try {
List responses = await Future.wait([firstAsync(), secondAsync(), thirdAsyncC()]);
} catch (e) {
handleError(e)
}
If any of the invoked functions completes with an error, the Future returned by Future.wait() also completes with an error. Use catchError() to handle the error.
Resource:https://v1-dartlang-org.firebaseapp.com/tutorials/language/futures#waiting-on-multiple-futures-to-complete-using-futurewait
The example is designed to show how you can wait for a long-running process without actually blocking the thread. In practice, if you have several of those that you want to run in parallel (for example: independent network calls), you could optimize things.
Calling await stops the execution of the method until the future completes, so the call to secondAsync will not happen until firstAsync finishes, and so on. If you do this instead:
void main() async {
var f = firstAsync();
var s = secondAsync();
var t = thirdAsync();
print(await f);
print(await s);
print(await t);
print('done');
}
then all three futures are started right away, and then you wait for them to finish in a specific order.
It is worth highlighting that now f, s, and t have type Future<String>. You can experiment with different durations for each future, or changing the order of the statements.
If anyone new in this problem use the async . Dart has a function called FutureGroup. You can use it to run futures in parallel.
Sample:
final futureGroup = FutureGroup();//instantiate it
void runAllFutures() {
/// add all the futures , this is not the best way u can create an extension method to add all at the same time
futureGroup.add(hello());
futureGroup.add(checkLocalAuth());
futureGroup.add(hello1());
futureGroup.add(hello2());
futureGroup.add(hello3());
// call the `.close` of the group to fire all the futures,
// once u call `.close` this group cant be used again
futureGroup.close();
// await for future group to finish (all futures inside it to finish)
await futureGroup.future;
}
This futureGroup has some useful methods which can help you ie. .future etc.. check the documentation to get more info.
Here's a sample usage Example One using await/async and Example Two using Future.then.
you can always use them in a single future
final results = await Future.wait([
firstAsync();
secondAsync();
thirdAsync();
]);
results will be an array of you return type. in this case array of strings.
cheers.
Try this resolve.
final List<Future<dynamic>> featureList = <Future<dynamic>>[];
for (final Partner partner in partnerList) {
featureList.add(repository.fetchAvatar(partner.uid));
}
await Future.wait<dynamic>(featureList);
If want parallel execution you should switch to multi thread concept called Isolates
mix this with async/await concepts . You can also check this website for more
https://buildflutter.com/flutter-threading-isolates-future-async-and-await/
Using async / await like that is useful when you need a resource before executing the next task.
In your example you don't do really useful things, but imagine you call firstAsync, that gives you a stored authorization token in your phone, then you call secondAsync giving this token get asynchronously and execute an HTTP request and then checking the result of this request.
In this case you don't block the UI thread (user can interact with your app) and other tasks (get token, HTTP request...) are done in background.
i think you miss understood how flutter works first flutter is not multi threaded.....!
second if it isn't multi threaded how can it executes parallel tasks, which doesnt happen....! here is some links that will help you understand more https://webdev.dartlang.org/articles/performance/event-loop
https://www.dartlang.org/tutorials/language/futures
flutter doesn't put futures on another thread but what happens that they are added to a queue the links that i added are for event loop and how future works. hope you get it , feel free to ask me :)
I'm using Saga's takeLatest to abort all requests except the latest. This works fine, but now I want to only abort requests which don't have identical url, params, and method.
I know Saga uses the type attribute to compare actions (just like vanilla Redux does), but I've also added url, params, and method to my actions because I was hoping there there was some way to do something like
yield takeLatestIf((action, latestAction) => {
const sameType = action.type === latestAction.type;
const sameUrl = action.url === latestAction.type;
const sameParams = areEqual(action.params, lastAction.params);
const sameMethod = action.method === lastAction.method;
return sameType && sameUrl && sameParams && sameMethod;
});
which should only abort requests if all 4 of those attribute comparisons are false.
How can I accomplish this?
If I get it right from your question, you want this:
Like standard takeLatest().
But when a duplicate request is made, ignore it and wait for the one already executing (a reasonable use case).
So I took takeLatest() implementation provided in the docs and adapted it to your scenario:
const takeLatestDeduped = (patternOrChannel, compareActions, saga, ...args) => fork(function*() {
let lastTask
let lastAction
while (true) {
const action = yield take(patternOrChannel)
// New logic: ignore duplicate request
if (lastTask && lastTask.isRunning() && !compareActions(lastAction, action)) {
continue
}
if (lastTask) {
yield cancel(lastTask)
}
lastTask = yield fork(saga, ...args.concat(action))
// New logic: save last action
lastAction = action
}
})
We have three cases:
No running task: start the new one - standard behavior
Running task, got non-duplicate: cancel old one, start new one - standard behavior
Running task, got duplicate: ignore - new custom hehavior
So I added case #3 logic:
Ignoring duplicate request (nothing should be done in this case, so I continue to handling next action).
Saving last action for future duplicate check.