I guess I need some type of promise chain, but the syntax eludes me...
Within the same component:
I'm calling:
this.somethingService.getSomethings().then(somethings => this.somethings = somethings);
Then I need to call:
this.otherService.getOthers(this.somethings).then(others => this.others = others);
In the second service call I'm using the result of the first to perform aggregate functions on its content, but its empty when the second call is made, thus the second service returns empty.
How can I get the second service to wait until the first promise has been resolved.
Thanx
Steve
You can chain promises this way:
this.somethingService.getSomethings().then(somethings => {
this.somethings = somethings;
return this.otherService.getOthers(somethings);
}).then(others => {
this.others = others;
});
The second callback will receive the result of the promise returns by the first callback.
Related
I'm trying to abort any/all previous Axios requests, using AbortController():
https://axios-http.com/docs/cancellation
FAILS: In my testing, previous queries do not get aborted.
The search experience still works as expected, but every request gets fully digested when user slams away on filters. Instead I want all previous requests to just be aborted.
I want to avoid building logic that uses storing/tracking requests, tokens and/or promises. I'm familiar with this stuff and could build it, but just want to avoid all that.
Is the Axios' AbortController intended for this purpose?
UPDATE (WORKS): Thx to #Oluwafemi, my setup is working.
Two things had to be changed:
Set a new instance of AbortController() directly after the abort.
The signal needs to be a third parameter going into the Axios
function, and not part of the payload (unlike what you see in
material online).
Side note: In addition, not included here is a debouncer wrapping my query function (in my app), which alongside this AbortController, makes for a good multi-layer management of outgoing/incoming comms with the API server.
(I redacted a bunch of methods/lines that aren't relevant)
export default class MySearch {
constructor() {
// ONE-TIME SETUP
this.payload = null
this.active = {
q: "", // (Query) string e.g. "apples"
facets: {}, // Objects, each with array of options e.g. { 'size': [ '2 x 2 in', '3 x 3 in' ]}, { 'artists': [ 'mike', 'john', 'jane' ] }
page: null, // number e.g. 3
sortBy: null // string, one of: "default" | "newest" | "price_asc" | "price_desc"
}
// Declaring this here. Good/bad?
this.AxiosSearchController = new AbortController()
}
async query() {
return new Promise( async (resolve, reject) => {
// Abort any previous Axios request
this.AxiosSearchController.abort()
// Reinstantiate another instance of AbortController()
this.AxiosSearchController = new AbortController()
this.transformURL()
let requestParams = {
"page": this.active.page,
"sortBy": this.active.sortBy,
"filter": this.active.facets,
}
// Here we tell Axios to associate the request with the controller.
let AxiosSignal = {
signal: this.AxiosSearchController.signal
}
axios.post('/api/search/' + this.active.q, requestParams, AxiosSignal)
.then( response => {
this.payload = response.data
return resolve(response)
})
.catch( error => {
console.error(error)
return reject(error)
})
})
}
}
Where AxiosSearchController is initialized for MySearch depends on if you want multiple instances of the MySearch to keep the same state of search or to maintain their own state of search.
When initialized in the constructor, each instance of MySearch has its own state of search like you have in your snippet.
1. Instance 1 initialized
2. Instance 2 initialized
3. Instance 3 initialized
4. Instance 1 performs request
5. Instance 2 performs request
6. Instance 3 performs request
7. Instance 1 aborts request
8. Instance 2 continues request till fulfillment
9. Instance 3 continues request till fulfillment
When initialized outside of the constructor, all instances of MySearch keep the same state of search.
1. Instance 1 initialized
2. Instance 2 initialized
3. Instance 3 initialized
4. Instance 1 performs request
5. Instance 2 performs request
6. Instance 1 has request aborted
7. Instance 3 performs request
8. Instance 2 has request aborted
Providing the signal property in the params argument is the proper format to set signal for the request for the axios library.
However, when aborting any previous request, AxiosSearchController.signal.aborted gets set to true.
Without resetting this state of the abort controller, you shouldn't be able to make any further requests after the signal is aborted the first time.
You need to initialize AxiosSearchController after aborting request for the previous search.
this.AxiosSearchController.abort();
this.AxiosSearchController = new AbortController();
I have a search input field in a ScalaJS app that fires off requests to a backend server whilst the user types in a city name. However, I need to implement a delay so that the request is not fired until after a certain delay (say 1000ms). Without such a delay, there is the chance that I'll get back false positives on the search (E.G. If the user wants to search for "paris", then there will be a false hit on "par" - a small town in Cornwall, England - when the third character is entered)
I've tried transcribing the JavaScript equivalent into Scala, but the setTimeout part doesn't seem to work.
import scala.scalajs.js.timers.{SetTimeoutHandle, clearTimeout, setTimeout}
private def delay = () => {
// Set initial timeout to do nothing after 0 ms
var handle: SetTimeoutHandle = setTimeout(0)(() => {})
(fn: Function0[Unit], ms: Double) => {
clearTimeout(handle)
handle = setTimeout(ms)(fn)
}
}
Then I'm handling the user input event using an Akka Actor
def receive = {
/************************************************
* Client event
* The user has typed something into the search field
*/
case evt: Event =>
delay()(handleInput, 1000.0)
}
Where handleInput is the zero parameter function that obtains the user's input and then fires off a request to the backend.
The anonymous inner function that clears and then resets the timeout is executed, but the handleInput function never gets called
Thanks
Chris W
The problem in your code is that you are giving a function of type () => Unit to setTimeout, but setTimeout takes a by-name parameter. In other words, the argument to setTimeout should be a statement to execute when the timeout expires. If you give it a function, then after the timeout that function value will be evaluated, but the function will not be called!
It is similar to mistakenly trying to do
val result = fn // result is the *function* itself, but does not call it
instead of
val result = fn() // fn is called, result is what it returns
You can fix your call to setTimeout by replacing fn by fn(). Also, it is typically more idiomatic, in those circumstances, to use {} instead of () for the parameter to setTimeout, which also gives a visual clue that it is a by-name parameter:
(fn: Function0[Unit], ms: Double) => {
clearTimeout(handle)
handle = setTimeout(ms) {
fn()
}
}
You should also adapt your first dummy setTimeout for consistency, although since it is a dummy anyway, that will not change the behavior:
// Set initial timeout to do nothing after 0 ms
var handle: SetTimeoutHandle = setTimeout(0) {}
I tried to validate 2 conditions inside eventually block... something like this
eventually(timeout(Span(26, Seconds)), interval(Span(2, Seconds))) {
response = executeSomeFunction
response should be = (true)
if (response) {
something = responseResult.get
something should be >= (10)
}
}
What am looking for is eventually should satisfy both the conditions. That is first it should check if response is true and then when response is true, it should validate the condition inside if loop.
I tried executing this but am getting error message
ambiguous reference to overloaded definition" referencing to line
"response should be = (true)"
Am not sure what I am trying to do is even possible inside eventually or not.
The problem is that you write
response should be = (true)
But actually you want to write:
response shouldBe true
In your case you make assignment of response should be: ResultOfBeWordForAny[Boolean] to the value true. Not clear what conversion here you expect.
P.S. Also write response = executeSomeFunction outside of eventually block, otherwise it could be executed multiple times.
P.P.S Moreover you don't need eventual call if you test result of your function, it's anyway in the scope. eventually isn't the best practice and used when function have some async side-effects you would like to test.
I have paged interface. Given a starting point a request will produce a list of results and a continuation indicator.
I've created an observable that is built by constructing and flat mapping an observable that reads the page. The result of this observable contains both the data for the page and a value to continue with. I pluck the data and flat map it to the subscriber. Producing a stream of values.
To handle the paging I've created a subject for the next page values. It's seeded with an initial value then each time I receive a response with a valid next page I push to the pages subject and trigger another read until such time as there is no more to read.
Is there a more idiomatic way of doing this?
function records(start = 'LATEST', limit = 1000) {
let pages = new rx.Subject();
this.connect(start)
.subscribe(page => pages.onNext(page));
let records = pages
.flatMap(page => {
return this.read(page, limit)
.doOnNext(result => {
let next = result.next;
if (next === undefined) {
pages.onCompleted();
} else {
pages.onNext(next);
}
});
})
.pluck('data')
.flatMap(data => data);
return records;
}
That's a reasonable way to do it. It has a couple of potential flaws in it (that may or may not impact you depending upon your use case):
You provide no way to observe any errors that occur in this.connect(start)
Your observable is effectively hot. If the caller does not immediately subscribe to the observable (perhaps they store it and subscribe later), then they'll miss the completion of this.connect(start) and the observable will appear to never produce anything.
You provide no way to unsubscribe from the initial connect call if the caller changes its mind and unsubscribes early. Not a real big deal, but usually when one constructs an observable, one should try to chain the disposables together so it call cleans up properly if the caller unsubscribes.
Here's a modified version:
It passes errors from this.connect to the observer.
It uses Observable.create to create a cold observable that only starts is business when the caller actually subscribes so there is no chance of missing the initial page value and stalling the stream.
It combines the this.connect subscription disposable with the overall subscription disposable
Code:
function records(start = 'LATEST', limit = 1000) {
return Rx.Observable.create(observer => {
let pages = new Rx.Subject();
let connectSub = new Rx.SingleAssignmentDisposable();
let resultsSub = new Rx.SingleAssignmentDisposable();
let sub = new Rx.CompositeDisposable(connectSub, resultsSub);
// Make sure we subscribe to pages before we issue this.connect()
// just in case this.connect() finishes synchronously (possible if it caches values or something?)
let results = pages
.flatMap(page => this.read(page, limit))
.doOnNext(r => this.next !== undefined ? pages.onNext(this.next) : pages.onCompleted())
.flatMap(r => r.data);
resultsSub.setDisposable(results.subscribe(observer));
// now query the first page
connectSub.setDisposable(this.connect(start)
.subscribe(p => pages.onNext(p), e => observer.onError(e)));
return sub;
});
}
Note: I've not used the ES6 syntax before, so hopefully I didn't mess anything up here.
I have two methods that both return an IObservable
IObservable<Something[]> QueryLocal();
and
IObservable<Something[]> QueryWeb();
QueryLocal is always successful. QueryWeb is susceptible to both a timeout and possible web errors.
I wish to implement a QueryLocalAndWeb() that calls both and combines their results.
So far I have:
IObservable<Something[]> QueryLocalAndWeb()
{
var a = QueryLocal();
var b = QueryWeb();
var plan = a.And(b).Then((x, y) => x.Concat(y).ToArray());
return Observable.When(plan).Timeout(TimeSpan.FromSeconds(10), a);
}
However, I'm not sure that it handles the case where QueryWeb yields an error.
In the future I might have a QueryWeb2() that also needs to be taken into account.
So, how do I combine the results from a number of IObservables ignoring the ones that throw errors (or time out)?
I guess OnErrorResumeNext should be able to handle this scenario:
From MSDN:
Continues an observable sequence that is terminated normally or by an
exception with the next observable sequence.
IObservable<Something[]> QueryLocalAndWeb()
{
var a = QueryLocal();
var b = QueryWeb().Timeout(TimeSpan.FromSeconds(10));
return Observable.OnErrorResumeNext(b, a);
}
You can do concat of array by using Aggregation on the returned observable.
I am assuming that both local and web are cold observable i.e they start producing values only when someone subscribes to them.
How about:
var plan = a.And(b).Then((x, y) => x.Concat(y.Catch(Observable.Empty<Something[]>()).ToArray());