I have method which return Observable, like this:
public getCpuUsage(): Observable<string> {
return Observable.create((observer: Observer<string>) => {
let plugin = new Plugin();
plugin.open();
plugin.OnEvent = (info: string, ack: string, nak: string) => {
console.log("CPU [" + info + "].param1[" + ack + "].param2[" + nak + " ]");
observer.next(info);
plugin.close();
observer.complete();
};
plugin.executeCommand('getCPUUsage');
});
}
Object plugin has callback when will deliver info about cpu. It's work fine.
Other class can call this method like:
public executeInfo(): string {
this.getCpuUsage()
.subscribe({
next: (infoAboutCpu) => {
console.log(" Result: " + infoAboutCpu);
return infoAboutCpu;
}
});
console.log('Call this line');
return '-1';
}
In log a see:
first log: 'Call this line'
second log(after 1-2 seconds, because callback called): 'Result: ${value_cpu_info}'
How I can block current thread, until get result from Observable ? Method executeInfo() must return correct value.
Block current thread RXJS
There is only 1 thread in JavaScript. You can block it with
while(true) {
// insert break logic here
}
But I highly recommend you reconsider the requirement.
Related
I am new to spring webclient and i'm trying to write test case for failure case for onstatus method.
Logic here
private Function<ClientResponse, Mono<? extends Throwable>> errorStrategy() {
return response -> {
return response.bodyToMono(Errors.class).flatMap(errorResponse -> {
log.info("Track Error ----> {}", errorResponse.getErrorCode());
Errors errors = new Errors(errorResponse.getErrorMsg());
return Mono.error(errors);
});
};
}
public Mono<EnterpriseSearchResponse> getCustomerID(EnterpriseSearchRequest searchRequest) {
Mono<EnterpriseSearchResponse> response = this.client.method(HttpMethod.GET)
.uri(enterpriseSearchURI + enterpriseSearchContext)
.header("Authorization", "Bearer " + enterpriseSearchAuthToken)
.accept(new MediaType[] { MediaType.APPLICATION_JSON }).bodyValue(searchRequest).retrieve()
.onStatus(HttpStatus::is5xxServerError, errorStrategy())
.onStatus(HttpStatus::is4xxClientError, errorStrategy()).bodyToMono(EnterpriseSearchResponse.class);
return response;
}
I want to write test case for errorStategy method.
can someone suggest how to achieve that?
I have the following API call to retrieve page data
List<VillageNewsItem> newsList = pageRetriever.RetrieveAsync<VillageNewsItem>(
query => query
.Path("/Home/Village-News", PathTypeEnum.Children)
.Published(true)
.OnSite(SiteContext.CurrentSiteName)
.OrderByDescending(x => x.DocumentCreatedWhen)
)?.Result?.ToList();
It works fine and return 2 records if I run the query on page load. Inside Index action of the controller.
public VillageNewsListController(IPageDataContextRetriever dataRetriever, VillageNewsListRepository villageNewsListRepository,
IPageRetriever pagesRetriever, IPageDataContextRetriever pageDataContextRetriever, IPageUrlRetriever pageUrlRetriever)
{
this._dataRetriever = dataRetriever;
this._villageNewsListRepository = villageNewsListRepository;
this._pagesRetriever = pagesRetriever;
this.pageDataContextRetriever = pageDataContextRetriever;
this.pageUrlRetriever = pageUrlRetriever;
}
public async Task<ActionResult> Index(CancellationToken cancellationToken)
{
try
{
List<VillageNewsItem> newsList = pagesRetriever.RetrieveAsync<VillageNewsItem>(
query => query
.Path("/Home/Village-News", PathTypeEnum.Children)
.Published(true)
.OnSite(SiteContext.CurrentSiteName)
.OrderByDescending(x => x.DocumentCreatedWhen)
)?.Result?.ToList();
newsItems.VillageNewsItems = newsList;
return View(newsItems);
}
catch (Exception ex)
{
ErrorHandler.EventLog.LogError(ex.Source, ex.Message, ex.StackTrace);
return RedirectToAction("ErrorPage", "Error");
}
}
However, if I try to make the same API call via a client side AJAX call, it doesn't work and return 0 records. Why it's not working with Ajax calls?
Ajax call
function loadMoreNews() {
$.ajax({
url: '/VillageNewsList/VillageNewsItemList',
//data: { "term": request.term },
type: "POST",
success: function (data) {
response($.map(data,
function (item) {
console.log(data);
}));
},
error: function (response) {
//alert(response.responseText);
},
failure: function (response) {
// alert(response.responseText);
}
});
}
Server side method.
[HttpPost]
[Route("VillageNewsList/VillageNewsItemList")]
public VillageNewsListViewModel VillageNewsItemList(string NodeAliasPath = "", int villageId = 0, string state = "", int page = 1, int pageSize = 4)
{
try
{
List<VillageNewsItem> newsList = pagesRetriever.RetrieveAsync<VillageNewsItem>(
query => query
.Path("/Home/Village-News", PathTypeEnum.Children)
.Published(true)
.OnSite(SiteContext.CurrentSiteName)
.OrderByDescending(x => x.DocumentCreatedWhen)
)?.Result?.ToList();
var model = new VillageNewsListViewModel
{
VillageNewsItems = newsList, // returns 0 records
};
return model;
}
catch (Exception ex)
{
ErrorHandler.EventLog.LogError(ex.Source, ex.Message, ex.StackTrace);
//return RedirectToAction("ErrorPage", "Error");
}
return null;
}
Couple things I see.
You're calling IPageRetriever.RetrieveAsync, but you aren't putting an await before it. There may be some odd behavior due to this. Get rid of the ?.Result?.ToList() and instead just put await before it, it will return an IEnumerable of the specified type.
You don't need ".Published" nor "OnSite" with IPageRetriever, this API automatically uses the Current Site Context, the current culture, and either Published or not / Latest Version or not based on if it's in edit/preview mode or not.
See if those things fix the issue!
I also asume it is caused by async context here...
You can try to use a document query instead.
Would be something like this:
var items = new DocumentQuery<VillageNewsItem>(
.Path("/Home/Village-News", PathTypeEnum.Children)
.PublishedVersion()
.Published()
.OnCurrentSite()
.OrderByDescending(x => x.DocumentCreatedWhen))
?.Result
?.ToList();
If you have multiple cultures, add the culture to your query, too.
.Culture(LocalizationContext.CurrentCulture.CultureCode)
I'm re-writing an existing module which spawns a child process and executes a command.
I've re-written it as a class but when I run the code, I get an error that the Promise rejects and resolve are undefined.
I assume that I pass them incorrectly to the .call method but I did not find a different way I can pass them.
Here's the code:
import logger from './logger.utils';
import { spawn, ChildProcess } from 'child_process';
/**
* This function runs a spawn command and rejects the promise if timed out
* #param cmd - the command to execute
* #param params - the command's parameters
* #param timeoutMs - timeout in milliseconds
* #param taskDescription - a text description of the task for logging
*/
export class SpawnTimeout {
cmd: string;
params: string[];
finished: boolean;
childProcess: ChildProcess;
timeoutMs: number;
timeout: NodeJS.Timeout;
taskDescription: string;
handlers: Object;
constructor(
cmd: string,
params: string[],
timeoutMs: number,
taskDescription: string = 'no description specified'
) {
this.finished = false;
this.childProcess = spawn(cmd, params, {
stdio: [process.stdin, process.stdout, process.stderr],
});
this.timeoutMs = timeoutMs;
this.timeout = null;
this.taskDescription = taskDescription;
this.cmd = cmd;
this.params = params;
}
exec() {
return new Promise((resolve, reject) => {
const handlers = {
resolve,
reject,
};
this.handlers = handlers;
this.childProcess.once('error', this._onError.call(this.handlers));
this.childProcess.once('exit', this._onExit.call(this.handlers));
this.timeout = setTimeout(this._setTimeout, this.timeoutMs);
});
}
_onError(err: Error, handlers) {
clearTimeout(this.timeout);
const message = `spawn [${this.taskDescription}] ${this.cmd}, ${this.params} failed with error ${err}`;
logger.error(message);
handlers.reject(new Error(message));
}
_onExit(code: number, handlers) {
this.finished = true;
clearTimeout(this.timeout);
logger.debug(`spawn [${this.taskDescription}] finished.code ${code}`);
if (code == 0) {
handlers.resolve(true);
}
// case of error, code !== 0
const message = `spawn [${this.taskDescription}] cmd : ${this.cmd} ${this.params}. failed with code ${code}`;
logger.error(message);
handlers.reject(new Error(message));
}
_setTimeout() {
if (!this.finished) {
logger.warn(
`spawn [${this.taskDescription}] - timeout. cmd : ${this.cmd}, ${this.params}`
);
this.childProcess.kill();
}
}
}
The error is generated when handlers.resolve or handlers.reject are called.
Please advise how can I resolve this? or even if such an implementation good practice.
call immediately calls a function, the first parameter is this context with which a function is called, it doesn't return a function in this case and it's incorrect to provide the result as a listener for once.
Callback needs to be wrapped with a function to provide expected arguments:
this.childProcess.once('error', err => this._onError(err, this.handlers))
this.childProcess.once('exit', code => this._onExit(code, this.handlers));
Since callbacks are bound to correct this this way, it may be unnecessary to pass this.handlers to them as it's already available inside them.
I have the following readable stream in typescript:
import {Readable} from "stream";
enum InputState {
NOT_READABLE,
READABLE,
ENDED
}
export class Aggregator extends Readable {
private inputs: Array<NodeJS.ReadableStream>;
private states: Array<InputState>;
private records: Array<any>;
constructor(options, inputs: Array<NodeJS.ReadableStream>) {
// force object mode
options.objectMode = true;
super(options);
this.inputs = inputs;
// set initial state
this.states = this.inputs.map(() => InputState.NOT_READABLE);
this.records = this.inputs.map(() => null);
// register event handlers for input streams
this.inputs.forEach((input, i) => {
input.on("readable", () => {
console.log("input", i, "readable event fired");
this.states[i] = InputState.READABLE;
if (this._readable) { this.emit("_readable"); }
});
input.on("end", () => {
console.log("input", i, "end event fired");
this.states[i] = InputState.ENDED;
// if (this._end) { this.push(null); return; }
if (this._readable) { this.emit("_readable"); }
});
});
}
get _readable () {
return this.states.every(
state => state === InputState.READABLE ||
state === InputState.ENDED);
}
get _end () {
return this.states.every(state => state === InputState.ENDED);
}
_aggregate () {
console.log("calling _aggregate");
let timestamp = Infinity,
indexes = [];
console.log("initial record state", JSON.stringify(this.records));
this.records.forEach((record, i) => {
// try to read missing records
if (!this.records[i] && this.states[i] !== InputState.ENDED) {
this.records[i] = this.inputs[i].read();
if (!this.records[i]) {
this.states[i] = InputState.NOT_READABLE;
return;
}
}
// update timestamp if a better one is found
if (this.records[i] && timestamp > this.records[i].t) {
timestamp = this.records[i].t;
// clean the indexes array
indexes.length = 0;
}
// include the record index if has the required timestamp
if (this.records[i] && this.records[i].t === timestamp) {
indexes.push(i);
}
});
console.log("final record state", JSON.stringify(this.records), indexes, timestamp);
// end prematurely if after trying to read inputs the aggregator is
// not ready
if (!this._readable) {
console.log("end prematurely trying to read inputs", this.states);
this.push(null);
return;
}
// end prematurely if all inputs are ended and there is no remaining
// record values
if (this._end && indexes.length === 0) {
console.log("end on empty indexes", this.states);
this.push(null);
return;
}
// create the aggregated record
let record = {
t: timestamp,
v: this.records.map(
(r, i) => indexes.indexOf(i) !== -1 ? r.v : null
)
};
console.log("aggregated record", JSON.stringify(record));
if (this.push(record)) {
console.log("record pushed downstream");
// remove records already aggregated and pushed
indexes.forEach(i => { this.records[i] = null; });
this.records.forEach((record, i) => {
// try to read missing records
if (!this.records[i] && this.states[i] !== InputState.ENDED) {
this.records[i] = this.inputs[i].read();
if (!this.records[i]) {
this.states[i] = InputState.NOT_READABLE;
}
}
});
} else {
console.log("record failed to push downstream");
}
}
_read () {
console.log("calling _read", this._readable);
if (this._readable) { this._aggregate(); }
else {
this.once("_readable", this._aggregate.bind(this));
}
}
}
It is designed to aggregate multiple input streams in object mode. In the end it aggregate multiple time series data streams into a single one. The problem i'm facing is that when i test the feature i'm seeing repeatedly the message record failed to push downstream and immediately the message calling _read true and in between just the 3 messages related to the aggregation algorithm. So the Readable stream machinery is calling _read and every time it's failing the push() call. Any idea why is this happening? Did you know of a library that implement this kind of algorithm or a better way to implement this feature?
I will answer myself the question.
The problem was that i was misunderstanding the meaning of the this.push() return value call. I think a false return value mean that the current push operation fail but the real meaning is that the next push operation will fail.
A simple fix to the code shown above is to replace this:
if (this.push(record)) {
console.log("record pushed downstream");
// remove records already aggregated and pushed
indexes.forEach(i => { this.records[i] = null; });
this.records.forEach((record, i) => {
// try to read missing records
if (!this.records[i] && this.states[i] !== InputState.ENDED) {
this.records[i] = this.inputs[i].read();
if (!this.records[i]) {
this.states[i] = InputState.NOT_READABLE;
}
}
});
} else {
console.log("record failed to push downstream");
}
By this:
this.push(record);
console.log("record pushed downstream");
// remove records already aggregated and pushed
indexes.forEach(i => { this.records[i] = null; });
this.records.forEach((record, i) => {
// try to read missing records
if (!this.records[i] && this.states[i] !== InputState.ENDED) {
this.records[i] = this.inputs[i].read();
if (!this.records[i]) {
this.states[i] = InputState.NOT_READABLE;
}
}
});
You can notice that the only difference is avoid conditioning operations on the return value of the this.push() call. Given that the current implementation call this.push() only once per _read() call this simple change solve the issue.
It means feeding is faster than consuming. The official approach is enlarge its highWaterMark, Default: 16384 (16KB), or 16 for objectMode. As long as its inner buffer is big enough, the push function will always return true. It does not have to be single push() in single _read(). You may push as much as the highWaterMark indicates in a single _read().
I have a library that connects to a remote API:
class Client(access_token) {
void put(key, value, callback);
void get(key, callback);
}
I want to set up a Node.js REPL to make it easy to try things out:
var repl = require('repl');
var r = repl.start('> ');
r.context.client = new Client(...);
The problem is that an asynchronous API is not convenient for a REPL. I'd prefer a synchronous one that yields the result via the return value and signals an error with an exception. Something like:
class ReplClient(access_token) {
void put(key, value); // throws NetworkError
string get(key); // throws NetworkError
}
Is there a way to implement ReplClient using Client? I'd prefer to avoid any dependencies other than the standard Node.js packages.
You can synchronously wait for stuff with the magic of wait-for-stuff.
Based on your example specification:
const wait = require('wait-for-stuff')
class ReplClient {
constructor(access_token) {
this.client = new Client(access_token)
}
put(key, value) {
return checkErr(wait.for.promise(this.client.put(key, value)))
}
get(key) {
return checkErr(wait.for.promise(this.client.get(key)))
}
}
const checkErr = (maybeErr) => {
if (maybeErr instanceof Error) {
throw maybeErr
} else {
return maybeErr
}
}