Run parallel saga effects without cancelling any of them - redux

I'd like to run parallel effects with redux-saga, without throwing if an error happens.
Using the all effect from redux-saga, if:
One of the Effects was rejected before all the effects complete: throws the rejection error inside the Generator.
Basically, I want to wait for all effects to finish to trigger an action. I want to do something like this, but replacing all by something else:
export function* getSaga() {
yield put(request());
try {
yield all([fetchItems1, fetchItems2, fetchItems3]);
// Wait for all to resolve or get rejected, then dispatch succeed.
yield put(actions.succeeded());
} catch (e) {
// This should never happen.
}
}
I tried to use fork but it does cancel all other tasks if one failed. And I tried to use spawn but it doesn't wait for the tasks to finish to dispatch succeeded.
Using regular JS, there is a pattern called reflect which I'd like to apply with saga.
How can we achieve that?
Thanks

Following the answer from the linked stackoverflow issue you can just as easily create a reflect saga and use it in the same way:
function* reflect(saga) {
try {
return { v: yield call(saga), status: 'fulfilled' }
} catch (err) {
return { e: err, status: 'rejected' }
}
}
...
yield all([fetchItems1, fetchItems2, fetchItems3].map(reflect));
Working example: https://codesandbox.io/s/y2vx74jzqv

Related

Synchronize MeteorJS promise based migration

The notification always appears "Migrations: Finished migrating." before the real migration seed is finished. I chained all. I've checked all.
During migration, we use the async mechanism.
const { db } = MongoInternals.defaultRemoteCollectionDriver().mongo;
I've just found that MeteorMigration does not support promise-based functional. As a solution we use Meteor.wrapAsync. But it does not help.
const wrapIntoMongoTransaction = Meteor.wrapAsync(function (func, callback) {
wrapIntoMongoTransactionAsync(func).then(callback);
});
And then simply call.
Promise.await from the package meteor/promise does it well. It makes asynchronous to synchronous code.
My code becomes looking the following way:
import { Promise } from 'meteor/promise';
function wrapIntoMongoTransaction(func) {
Promise.await(wrapIntoMongoTransactionAsync(func));
}

Is it Ok to call cy.server() twice in same test in Cypress

it('Some Test', () => {
cy.server();
cy.route('POST', 'my/api1').as('myApi');
...
cy.wait('#api1');
cy.server();
cy.route('POST', 'my/api2').as('myApi');
...
cy.wait('#api2');
}
Is this code ok, like is there any problem if we are calling cy.server() twice in the same test?in Cypress
I did code as above to see if there is any repercussion, however, couldn't get any!
Basically I wanted to abstract it away in a function like this
Cypress.Commands.add('listenRoute', (type, url, alias) => {
cy.server();
cy.route({
method: type,
url,
}).as(alias);
});
So I got tests successfully passing without any errors or warning, and thus am concluding, that it is OK
The second cy.server(); call is not required as you are waiting cy.wait('#myApi'); for the previous call to complete. Also, the second route is the same as the first, therefore no need to set again if server() is not re-instantiated.
Doco:
Outstanding requests are automatically aborted between tests
Therefore you need to either wait for previous to complete, or initiate a second server to run concurrently.
it('Some Test', () => {
cy.server();
cy.route('POST', 'my/api').as('myApi');
...
cy.wait('#myApi');
cy.wait('#myApi');
cy.visit('#myApi');
cy.wait('#myApi');
cy.visit('#myApi');
}

redux-saga and firebase - Can't log the user out in a clean way

As a preface, let me mention that I have never used redux-saga or Firebase before today. I'm currently playing around to get a feel for both technologies.
I'm probably just missing a small concept, but I can't seem to get signout to work on my app. I figured that I should probably use call() to manage side effects within a saga, but nothing does the trick.
Here's the saga in question:
export function* deauthenticateUser() {
yield takeLatest(DEAUTHENTICATE_REQUEST, function* () {
try {
yield call(firebase.auth().signOut)
yield put({ type: DEAUTHENTICATE })
}
catch (error) {
yield put({
type: DEAUTHENTICATE_FAILURE,
payload: error,
error: true,
})
}
})
}
I confirmed that calling firebase.auth().signout() directly works, it's only when using call() that I get the error action. Note that there's also no payload when the error gets dispatched.
I checked in Firebase's documentation, and apparently firebase.auth().signout() returns a promise with nothing as it's content. I'm starting to wonder if that wouldn't be the problem, maybe redux-saga does not like having no result in it's promise when using call()?
How would one handle authentication and especially logging out with Firebase and redux-saga?
From a comment from NULL SWEΔT, I had to call yield call([firebase.auth(), firebase.auth().signOut]).
The reason for this is because of JS' context and how this works. More details by reading this and this (read documentation for call([context, fn], ...args)).

Intercepting HTTP Requests in Redux

Many redux examples show making HTTP requests directly in an async action; however this will result in an exterme amount of duplicate code common to all requests, for example:
Retrying failed requests based on status code
Appending common HTTP Headers to each request.
Invoking additional http requests based on response (ie: oauth token refresh)
Aborting in-flight requests on route transitions.
My gut feeling is that a middleware could be used to make http requests (in the same vein as redux-api-middleware) which can keep the in-flight requests in the store - however I am also wondering if I'm leaning on bad habbits - is a little duplication a small price to pay for immutability?
Your action creators are just JavaScript functions.
If you have duplication between several functions, you extract the common code into another function. This is no different. Instead of duplicating the code, extract the common code into a function and call it from your action creator.
Finally, this pattern can be abstracted away with a custom middleware. Check out the “real world” example in Redux repo to see how it can be done.
All but the aborting could be accomplished while staying immutable by using a request factory which attaches .then and .catch (or equivalent, depending on promise flavor) to the request before returning it.
you can have a action which executes its operation in addition it calls another action, to achieve this you need to have a redux-async-transitions, the example code is given below
function action1() {
return {
type: types.SOMEFUNCTION,
payload: {
data: somedata
},
meta: {
transition: () => ({
func: () => {
return action2;
},
path : '/somePath',
query: {
someKey: 'someQuery'
},
state: {
stateObject: stateData
}
})
}
}
}
and here is for asynchronous call
function asynccall() {
return {
types: [types.PENDINGFUNCTION, types.SUCCESSFUNCTION, types.FAILUREFUNCTION],
payload: {
response: someapi.asyncall() // only return promise
}
meta: {
transition: (state, action) => ({
onPending: () => {
},
onSuccess: (successdata) => {
//gets response data can trigger result based on data
},
onFail: (promiseError) => {
//gets error information used to display messages
}
})
}
}
}
Calling http request in middleware is the same bad ideas as calling it in action creators. You should focus on making our reducers powerful enough to handle asynchronous effects as well as synchronous state transitions. The best way i found while working with redux is describe effects in the reducer(http request is the effect) and then handle it with library like redux-loop or redux-saga
For avoiding code duplication you can extract common code to request function and use it for handling http effects

Meteor [Error: Can't wait without a fiber] after a call to Email.send

I've created a very simple server using Meteor, to send an email after a timeout. When I use a timeout, the message is successfully sent but an error is thrown: [Error: Can't wait without a fiber].
Here's my code:
if (Meteor.isServer) {
Meteor.startup(function () {
// <DUMMY VALUES: PLEASE CHANGE>
process.env.MAIL_URL = 'smtp://me%40example.com:PASSWORD#smtp.example.com:25';
var to = 'you#example.com'
var from = 'me#example.com'
// </DUMMY>
//
var subject = 'Message'
var message = "Hello Meteor"
var eta_ms = 10000
var timeout = setTimeout(sendMail, eta_ms);
console.log(eta_ms)
function sendMail() {
console.log("Sending...")
try {
Email.send({
to: to,
from: from,
subject: subject,
text: message
})
} catch (error) {
console.log("Email.send error:", error)
}
}
})
}
I understand that I could use Meteor.wrapAsync to create a fiber. But wrapAsync expects there to be a callback to call, and Email.send doesn't use a callback.
What should I do to get rid of the error?
This happens because while your Meteor.startup function runs inside a Fiber (like almost all other Meteor callbacks), the setTimeout you use does not! Due to the nature of setTimeout it will run on the top scope, outside the fiber in which you defined and/or called the function.
To solve, you could use something like Meteor.bindEnvironment:
setTimeout(Meteor.bindEnvironment(sendMail), eta_ms);
And then do so for every single call to setTimeout, which is a painfully hard fact.
Good thing it's not actually true. Simply use Meteor.setTimeout instead of the native one:
Meteor.setTimeout(sendMail, eta_ms);
From the docs:
These functions work just like their native JavaScript equivalents. If you call the native function, you'll get an error stating that Meteor code must always run within a Fiber, and advising to use Meteor.bindEnvironment
Meteor timers just bindEnvironment then delay the call as you wanted.

Resources