Can a series of promises and then handlers be written in waitOneCycle.then(waitOneCycle().then(...)) but without deep nesting? - promise

If we already have
const waitOneCycle = () => {
return new Promise(resolve => setTimeout(resolve));
};
Then our code (this is currently used in Jest and SinonJS although it doesn't have to be:)
waitOneCycle()
.then(() => {
// do something
});
and it reads really elegantly: the waitOneCycle and then do something.
However, if we do a series of them, we have to:
waitOneCycle()
.then(() => {
// do something
return waitOneCycle();
})
.then(() => {
// do something
return waitOneCycle();
})
.then(() => {
// do something
return waitOneCycle();
});
and it reads in a strange way, because why would we return something to act as "waitOneCycle"? This is the way it works, but the code just read in a strange way.
We could do something like:
waitOneCycle()
.then(() => {
// not return anything here
})
.then(() => {
waitOneCycle()
.then(() => {
});
})
but this would go back to the nesting hell issue in the past. Is there a way to refactor it so it reads coherently but at the same time all actions are serialized?
P.S. in the case of Ruby, if the promises are chained this way, it actually can read out quite well, as we don't need to use return for the last value evaluated. So it'd read like waitOneCycle, then, waitOneCycle, then... and it appears quite elegantly.

You don't need to nest (and it would only work if you were to return the inner promise anyway), you can also chain it:
waitOneCycle()
.then(() => {
// do something
})
.then(waitOneCycle)
.then(() => {
// do something
})
.then(waitOneCycle)
.then(() => {
// do something
})
.then(waitOneCycle);
This might work even better if you make waitOneCycle pass through the value:
const waitOneCycle = (v) => {
return new Promise(resolve => setTimeout(resolve, v));
};
Of course, if you want promise code that reads coherently and has no nesting (not even that of then callbacks), you should just go for async/await syntax.

Related

How to stop Prettier from weird-formatting chunks of code

I use Prettier as my default code formatter in VSCode.
Everything is nice, and I love it, but sometimes it does some weird formatting that I don't really dig. Here's an example:
useEffect(() => {
taskAPI
.getUserTasks(session_user, auth.config())
.then((resp) => {
setTasks(resp.data);
})
.then(() => {
setLangs(getLanguages(tasks));
});
}, []);
Notice how instead of showing the code such as
taskAPI.getUserTasks(...)
.then(()=>{
...
}
It does that weird new line from taskAPI to getUserTasks. Is there a way to modify this?

How to fail cypress test from inside the Promise.prototype.catch() block?

I'm using a node library to execute api calls for test data setup and teardown. The library works as follows:
someApiServiceObject
.executeApiCall({... parameters})
.then(doSomethingWithResults())
.catch(() => {
// Here I would like to fail the test as something has gone wrong
})
If the request fails for some reason, I only learn about it by the Promise returning from executeApiCall function being rejected - hence the catch block.
But if I put throw new Error(); into the catch block or remove the catch block, I can see the (uncaught exception) Error: in the cypress console, but the test still passes.
Can someone advise me on how this case should be handled correctly?
The test:
it('List projects', () => {
projectsApi.projectsList({})
.then(() => {
cy.log('Success');
}).catch(() => {
throw new Error();
});
});
If you call someApiServiceObject.executeApiCall({...parameters}) in a task (since it's a node library), you should just be able to return the promise and Cypress handles failing the test. Don't catch() within the task.
module.exports = (on, config) => {
on('task', {
api(parameters) {
return someApiServiceObject
.executeApiCall({... parameters})
.then(doSomethingWithResults())
},
})
}
If that fails, follow this pattern Return number of files in the folder
module.exports = (on, config) => {
on('task', {
countFiles(folderName) {
return new Promise((resolve, reject) => {
someApiServiceObject
.executeApiCall({... parameters})
.then(doSomethingWithResults())
.then((results) => resolve(results))
.catch((err) => reject(err))
})
})
},
})
}
From comments, I think there's a assumption being made that .executeApiCall() must be returning a promise, but that may not be the case.
For example cy.get(...) has a .then(...) method, but it does not return a promise, it just has a .then() method.
If .executeApiCall() does actually return a promise, the first example is all you need. If it does not, you need to wrap the code.
Cypress will recognise a promise returned from a task, and use resolve or reject accordingly.

Why use immer for this function?

ToggleAttached function inside CauseSpecificPage.js. This function toggles the follow/following button on the cause specific page: st-bnv.
What is the benefit of using Immer in this situation? Do we even need to use Immer?
const ToggleAttached = () => {
if (state.isDisabled) {
return
}
const oldValue = state.isAttached
setState(produce((draftState) => {
draftState.isDisabled = true
draftState.isAttached = !oldValue
}))
ToggleFollow({ causeId })
.then(response => {
setState(produce((draftState) => {
draftState.isAttached = response.data.isAttached
}))
})
.catch(error => {
setState(produce((draftState) => {
draftState.isAttached = oldValue
}))
HandleError(error)
})
.finally(() => setState(produce((draftState) => {
draftState.isDisabled = false
})))}
Immer is a little more verbose but is more maintainable in the long run. Immer was created to help us to have an immutable state, it’s a library created based on the “copy-on-write” mechanism — a technique used to implement a copy operation in on modifiable resources.
We can see that the concept of immutability is getting used more and becoming more common in the React community. But to make sure that we’re doing it the right way, we can use Immer for the job. Immer adds more benefits on redux states.

Is It good to subscribe to an observable within another subscription handler?

I'm using Angular2 and I have a question about what is the best way to do if I have many observables.
Can I put subscriptions inside each other or put each one in a different method and put the results in class properties?
Example :
ngOnInit() {
this.route.params**.subscribe**(params => {
if (params['id']) {
this.load = true;
this.batchService.getPagesOfCurrentObject(params['id'], "10", "0")
**.subscribe**(result => {
this.stream = result;
if (this.stream.length > 0) {
this.stream.forEach(page => { this.batchService.getPageStreamById
(page.pageId)**.subscribe**(pageStream => {
let base64 = btoa(new Uint8Array(pageStream.data)
.reduce((data, byte)
=> data + String.fromCharCode(byte), ''));
this.pages.push(base64 );
})
return;
});
}
},
error => this.errorService.setError(<any>error),
() => this.load = false
);
}
});
try {
this.customer = this.sharedService.processSelect.subscription.customer;
} catch (err) {
return;
}
}
Having multiple observables is totally fine, this is what reactive programming is about :)
But here your problem is having too much subscribe. Keep in mind that subscribe is a way to create side effect. To have an easy to read code, you should try to use the least possible subscribe.
Your use case is the perfect use case for the mergeMap operator, that allows you to flatten nested observables.
Here what your code would look like
const response$ = this.route.params
.mergeMap(params => {
return this.batchService.getPagesOfCurrentObject(params['id'])
})
.mergeMap(stream => {
return Rx.Observable.merge(stream.map(page => this.batchService.getPageStreamById(page.pageId))
})
.map(pageStream => /* do your stuff with pageStream, base64 ... */)
response$.subscribe(pageStreamData => pages.push(pageStreamData))
See how there is a single subscription that triggers the side-effect that will modify your app's state
Note that I voluntarily simplified the code (removed error handling and checks) for you to get the idea of how to do that.
I hope it will help you thinking in reactive programming :)

In RXJS, is there a way to omit the res and err calls on .subscribe()?

Given an observable where you subscribe:
Observable.subscribe(
(res) => {},
(err) => {},
() => {
// do some thing on complete
}
);
Is it a requirement to write (res) and (err)?
Reason being, I'm using Observable.forkJoin([Observable1, Observable2]).subscribe, and I only care about when they are done, and maybe if they yield an error.
To omit the next and error functions, pass undefined for those parameters:
Rx.Observable
.from([0, 1, 2])
.subscribe(
undefined,
undefined,
() => console.log("completed")
);
I would opt to use the .finally() operator instead of omitting the res and err callbacks. This to better convey what it is that you want to happen.
Rx.Observable.forkJoin([obs1, obs2])
.finally(() => console.log('done'))
.subscribe();
Note that it is not required to pass the callbacks to subscribe().
If we are talking of RxJS5 at least, you can also define the specific subscriber methods you want.
Observable.subscribe(
(res) => {},
(err) => {},
() => {
// do some thing on complete
}
);
becomes
Observable.subscribe(
complete: () => {
// do some thing on complete
}
);
As a side note, the other methods that can be explicitly defined are next and error.

Resources