I have made a for loop who call a web worker to highlight code inside code tag
I have made this loop, this call my worker and do the job.
highlightBase: ->
codes = document.getElementsByClassName('hljs')
if (codes)
for code in codes
HighlightWorker = new Worker('/js/highlight_worker.js')
HighlightWorker.onmessage = (event) ->
code.innerHTML = event.data
HighlightWorker.postMessage(code.textContent)
This is working well but I need to stop this loop until the worker is done.
Is this possible to stop this loop and continue it after or I need to add some timeOut ?
I am not very familiar very coffeeScript but this might give you an idea.
Stoping a for loop is not possible. There are generator functions for that lazy evaluation purposes. However although you could use, i don't think you need generators here. Instead i guess promises could be very handy. You might use Promise.all(). But first you have to create a utility function to return a promise when you instantiate a worker.
function postMessageToWorker(worker,message){
var p = new Promise((resolve,reject) => worker.onmessage = resolve);
worker.postMessage(message);
return p;
}
var proms = [];
if (codes)
for (var code of codes)
proms.push(postMessageToWorker(new Worker('/js/highlight_worker.js'), code.textContent));
Promise.all(proms).then(a => a.forEach((e,i) => codes[i].innerHTML = e.data));
Here is a plunker code for you to play.
Why do you need to stop the loop?
It looks like you are trying to do synchronous tasks, meaning the workers do their job one after the other.
But the whole thing about workers is that they are asynchronous...
I guess if you want your code to work synchronously, you need to do something like that:
highlightBase: ->
codes = document.getElementsByClassName('hljs')
if (codes)
for code in codes
code.innerHTML = function_that_was_before_made_by_worker()
And that it is...
Related
Trying to learn RxJs, and I found what looks like a nice tutorial on that topic at https://www.learnrxjs.io.
I'm going through their primer section, and I am not clear on what the return statement in the pipe() function actually does or what it means. These are a couple of screen shots from their tutorial:
In traditional programming, I've always understood a return statement to be an exit - if function A calls function B, and function B has the line return 1, then control goes back to function A.
Is that what happens here? If so, in either of these two examples, where am I returning to???
Or what if I don't want to return anywhere but act on the data immediately? For example, in the error handling example, instead of return makeRequest..., I want to do something like const result = makeRequest.... Can I do that?
In general I'm having some conceptual difficulties around all the returns I've seen used with observables, and any help in explaining what they do/are would be appreciated. So would any other tutorial sites on RxJs.
These are all very similar constructs in javascript
function adder0(a,b){
return a + b
}
const adder1 = (a,b) => {
return a + b
}
const adder2 = (a,b) => a + b
console.log(adder0(3,7)) // 10
console.log(adder1(3,7)) // 10
console.log(adder2(3,7)) // 10
lets re-write the code from that example but with explicit function definitions (instead of arrow syntax).
function maker(value){
return makeRequest(value).pipe(
catchError(handleError)
);
}
function actioner(value){
// take action
}
source.pipe(
mergeMap(maker)
).subscribe(actioner)
The thing to notice is that you never actually call these functions. RxJS does that for you. You give them a function and they'll call it when they're good and ready based on the spec for a given operator.
How is it possible with RXJS to make a cascaded forEach loop? Currently, I have 4 observables containing simple string lists, called x1 - x4. What I want to achieve now is to run over all variation and to call a REST-Api with an object of variation data. Usually, I would do something like that with a forEach, but how to do with RXJS? Please see the abstracted code:
let x1$ = of([1,2]);
let x2$ = of([a,b,c,d,e,f]);
let x3$ = of([A,B,C,D,E,F]);
let x4$ = of([M,N,O,P]);
x1$.forEach(x1 => {
x2$.forEach(x2 => {
x3$.forEach(x3 => {
x4$.forEach(x4 => {
let data = {
a: x1,
b: x2,
c: x3,
d: x4
}
return this.restService.post('/xxxx', data)
})
})
})
})
Is something like that possible with RXJS in an elegant way?
Let's assume you have a function combineLists which represent the plain-array version of the logic to turn static lists into an array of request observables:
function combineLists(lists: unknown[][]) {
const [x1s, x2s, x3s, x4s] = lists;
// Calculate combinations, you can also use your forEach instead
const combinations = x1s
.flatMap(a => x2s
.flatMap(b => x3s
.flatMap(c => x4s
.flatMap(d => ({a, b, c, d})))));
return combinations.map(combination => this.restService.post('/xxxx', combination));
}
Since your input observables are one-offs as well, we can use e.g. forkJoin. This waits for all of them to complete and then runs with their respective plain values. At this point you're back to computing the combinations with your preferred method.
forkJoin([x1$, x2$, x3$, x4$]).pipe(
map(combineLists),
);
Assuming your REST call is typed to return T, the above produces Observable<Observable<T>[]>. How you proceed from here depends on what data structure you're looking for / how you want to continue working with this. This didn't seem to be part of your question anymore, but I'll give a couple hints nonetheless:
If you want a Observable<T>, you can just add e.g. a mergeAll() operator. This observable will just emit the results of all individual requests after another in whichever order they arrive.
forkJoin([x1$, x2$, x3$, x4$]).pipe(
map(combineLists),
mergeAll(),
);
If you want an Observable<T[]> instead, which collects the results into a single emission, you could once again forkJoin the produced array of requests. This also preserves the order.
forkJoin([x1$, x2$, x3$, x4$]).pipe(
map(combineLists),
switchMap(forkJoin),
);
Some words of caution:
Don't forget to subscribe to make it actually do something.
You should make sure to handle errors on all your REST calls. This must happen right at the call itself, not after this entire pipeline, unless you want one single failed request to break the entire pipe.
Keep in mind that forkJoin([]) over an empty array doesn't emit anything.
Triggering a lot of requests like this probably means the API should be changed (if possible) as the number of requests grows exponentially.
I have tried to unsubscribe within the subscribe method. It seems like it works, I haven't found an example on the internet that you can do it this way.
I know that there are many other possibilities to unsubscribe the method or to limit it with pipes. Please do not suggest any other solution, but answer why you shouldn't do that or is it a possible way ?
example:
let localSubscription = someObservable.subscribe(result => {
this.result = result;
if (localSubscription && someStatement) {
localSubscription.unsubscribe();
}
});
The problem
Sometimes the pattern you used above will work and sometimes it won't. Here are two examples, you can try to run them yourself. One will throw an error and the other will not.
const subscription = of(1,2,3,4,5).pipe(
tap(console.log)
).subscribe(v => {
if(v === 4) subscription.unsubscribe();
});
The output:
1
2
3
4
Error: Cannot access 'subscription' before initialization
Something similar:
const subscription = of(1,2,3,4,5).pipe(
tap(console.log),
delay(0)
).subscribe(v => {
if (v === 4) subscription.unsubscribe();
});
The output:
1
2
3
4
This time you don't get an error, but you also unsubscribed before the 5 was emitted from the source observable of(1,2,3,4,5)
Hidden Constraints
If you're familiar with Schedulers in RxJS, you might immediately be able to spot the extra hidden information that allows one example to work while the other doesn't.
delay (Even a delay of 0 milliseconds) returns an Observable that uses an asynchronous scheduler. This means, in effect, that the current block of code will finish execution before the delayed observable has a chance to emit.
This guarantees that in a single-threaded environment (like the Javascript runtime found in browsers currently) your subscription has been initialized.
The Solutions
1. Keep a fragile codebase
One possible solution is to just ignore common wisdom and continue to use this pattern for unsubscribing. To do so, you and anyone on your team that might use your code for reference or might someday need to maintain your code must take on the extra cognitive load of remembering which observable use the correct scheduler.
Changing how an observable transforms data in one part of your application may cause unexpected errors in every part of the application that relies on this data being supplied by an asynchronous scheduler.
For example: code that runs fine when querying a server may break when synchronously returned a cashed result. What seems like an optimization, now wreaks havoc in your codebase. When this sort of error appears, the source can be rather difficult to track down.
Finally, if ever browsers (or you're running code in Node.js) start to support multi-threaded environments, your code will either have to make do without that enhancement or be re-written.
2. Making "unsubscribe inside subscription callback" a safe pattern
Idiomatic RxJS code tries to be schedular agnostic wherever possible.
Here is how you might use the pattern above without worrying about which scheduler an observable is using. This is effectively scheduler agnostic, though it likely complicates a rather simple task much more than it needs to.
const stream = publish()(of(1,2,3,4,5));
const subscription = stream.pipe(
tap(console.log)
).subscribe(x => {
if(x === 4) subscription.unsubscribe();
});
stream.connect();
This lets you use a "unsubscribe inside a subscription" pattern safely. This will always work regardless of the scheduler and would continue to work if (for example) you put your code in a multi-threaded environment (The delay example above may break, but this will not).
3. RxJS Operators
The best solutions will be those that use operators that handle subscription/unsubscription on your behalf. They require no extra cognitive load in the best circumstances and manage to contain/manage errors relatively well (less spooky action at a distance) in the more exotic circumstances.
Most higher-order operators do this (concat, merge, concatMap, switchMap, mergeMap, ect). Other operators like take, takeUntil, takeWhile, ect let you use a more declarative style to manage subscriptions.
Where possible, these are preferable as they're all less likely to cause strange errors or confusion within a team that is using them.
The examples above re-written:
of(1,2,3,4,5).pipe(
tap(console.log)
first(v => v === 4)
).subscribe();
It's working method, but RxJS mainly recommend use async pipe in Angular. That's the perfect solution. In your example you assign result to the object property and that's not a good practice.
If you use your variable in the template, then just use async pipe. If you don't, just make it observable in that way:
private readonly result$ = someObservable.pipe(/...get exactly what you need here.../)
And then you can use your result$ in cases when you need it: in other observable or template.
Also you can use pipe(take(1)) or pipe(first()) for unsubscribing. There are also some other pipe methods allowing you unsubscribe without additional code.
There are various ways of unsubscribing data:
Method 1: Unsubscribe after subscription; (Not preferred)
let localSubscription = someObservable.subscribe(result => {
this.result = result;
}).unsubscribe();
---------------------
Method 2: If you want only first one or 2 values, use take operator or first operator
a) let localSubscription =
someObservable.pipe(take(1)).subscribe(result => {
this.result = result;
});
b) let localSubscription =
someObservable.pipe(first()).subscribe(result => {
this.result = result;
});
---------------------
Method 3: Use Subscription and unsubscribe in your ngOnDestroy();
let localSubscription =
someObservable.subscribe(result => {
this.result = result;
});
ngOnDestroy() { this.localSubscription.unsubscribe() }
----------------------
Method 4: Use Subject and takeUntil Operator and destroy in ngOnDestroy
let destroySubject: Subject<any> = new Subject();
let localSubscription =
someObservable.pipe(takeUntil(this.destroySubject)).subscribe(result => {
this.result = result;
});
ngOnDestroy() {
this.destroySubject.next();
this.destroySubject.complete();
}
I would personally prefer method 4, because you can use the same destroy subject for multiple subscriptions if you have in a single page.
I am trying to understand the logic of promises here. But I can't wrap my head around the code.
Can someone help me understand?
What does (r)=> fn = r, means in this code.
function promise(a,b) {
return new Promise((resolve,reject) => {
if(a%2 !== 0) {
reject('ODD ');
return;
}
resolve('even');
});
}
let list_of_promise = [ promise(1),promise(2)
,promise(2),promise(2),promise(2),promise(2)
,promise(2),promise(2),promise(2),promise(2)
,promise(2),promise(2),promise(2),promise(2),promise(2)
,promise(2),promise(2),promise(2),promise(2)
,promise(2)];
function listOfPromise(list_of_promise) {
let arr = [];
let fn;
let count = 0;
list_of_promise.map( (p) => {
p.then((res)=>{
arr.push(res);
arr.length === 10 && fn(arr);
});
})
return new Promise((r,rj) => fn= r);
}
listOfPromise(list_of_promise).then((res)=>{
console.log("result set ", rest);
});
This is horrible code.
fn = r is just assigning the resolve() function from the new Promise() executor to a higher scope so it can be called from outside the executor, causing the promise to resolve. This is a very obtuse way to write the code. A design pattern for resolving a promise from the outside is typically referred to as a Deferred. 99.999% of the time, there is no need for the Deferred pattern and there's a bunch of good reasons why it was not built into the promise architecture. If you want to see a simple Deferred object, you can see here and here.
To rewrite this in a less obtuse way, I would need to understand what the objective of the code is (in terms of a real-world problem) so I could suggest the best way to solve the actual problem. Right now, it looks like demo code trying to demonstrate something, not trying to solve a real world problem. I prefer to focus coding on real world problems rather than theoretical discussions as the real world problem provides priorities for the actual coding strategy.
I was going through the link https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Your_second_WebExtension.
I could not understand the keyword then() in the choose_beast.js script. I know it is something related to promises in javascript. Can you explain in simple language promises and use of then in this context?
Let us compare synchronous and asynchronous code.
Looking at a normal synchronous code:
let a = Date.now();
let b = a * 3;
a is set before b is set and it is available for the next line to be used
Looking at an asynchronous code:
let a = someAsyncFuntion();
let b = a * 3; // runs into error
a is NOT set before b is set and it is NOT available for the next line to be used, therefore it results in error.
The someAsyncFuntion() is queued to run when the next process is available. The parser moves to let b = a * 3; but here the a is not set yet, so there would be an error.
I simple words, in Promise the function is queued to run asynchronously. Therefore, then() is when it has done it.
Looking at the example on above page:
var gettingActiveTab = browser.tabs.query({active: true, currentWindow: true});
gettingActiveTab.then((tabs) => { browser.tabs.sendMessage(tabs[0].id, {beastURL: chosenBeastURL}); });
browser.tabs.query() does not run immediately and does not get results immediately. Therefore, we write the code so that when it gets the result then() do something.
// query tabs asynchronously
var gettingActiveTab = browser.tabs.query({.....});
// once got the result THEN do something
gettingActiveTab.then( /* do something */ );
I hope that helps.