I have two classes: Parent and Child. Parent class needs to initialize through a promise which I did during its construction:
// Parent.ts
class Parent {
constructor() {
this.init().then().catch();
}
private init(): Promise<boolean> {
return new Promise((resolve, reject)=>{
//do stuff.
resolve(true);
});
}
multiply(number): number {
return number * USER_VALUES_PICK
}
};
Now in child class I do:
class Child {
private parent = new Parent();
add(a: number, b:number): number {
return parent.multiply(a + b);
}
}
Now in my Child.test.ts:
it("add method", () => {
const child = new Child();
expect(child.add(1,1)).toStrictEqual(2 * USER_VALUES_PICK);
});
But the test fails stating a promise is not being caught:
node:internal/process/promises:246
triggerUncaughtException(err, true /* fromPromise */);
^
[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "TypeError: Cannot read properties of undefined (reading 'map')".] {
code: 'ERR_UNHANDLED_REJECTION'
}
It passes if I don't call the init() method in Parent constructor method.
How do I go about testing this or what change should I make?
Your stack trace points to an unhandled rejection but for me it was a syntax error in your add method, you must use this to call a class property, anyway, you can use the reject method and handle the promise in your constructor as well, try the following:
Parent.ts
const USER_VALUES_PICK: number = 2;
// export the class
export default class Parent {
constructor() {
this.init()
// handle the promises
.then((data) => console.log(data))
.catch((err) => console.log(err));
}
private init(): Promise<boolean> {
return new Promise((resolve, reject) => {
resolve(true);
// add reject to promise
reject(false);
});
}
// type the input
public multiply(number: number): number {
return number * USER_VALUES_PICK;
}
}
Child.ts
// don't forget to import the parent class
import Parent from "./Parent";
export class Child {
private parent = new Parent();
public add(a: number, b: number): number {
// use this to call the property
return this.parent.multiply(a + b);
}
}
Take a look at the working example in codesandbox.
Related
I have a typescript method that is trying to call a method, on(), that takes in a callback method. I'd like to have myConnect() wait until the callback method is executed. I assume this is a promise, but I'm not sure how to write this method so it waits until the callback is called.
myConnect(): void {
this.innerProducer.connect();
this.innerProducer.on("ready", () => {
Logger.info("producer is ready to produce for topic {}", this.topic);
this.isReadyToProduce = true;
});
}
I attempted to use util.promisify, but it's not quite right:
async connect2() {
this.innerProducer.connect();
const util = require("util");
const readyCallbackFunc = util.promisify(this.innerProducer.on);
await readyCallbackFunc("ready", () => {
Logger.info("producer is ready to produce for topic {}", this.topic);
this.isReadyToProduce = true;
});
}
error: (node:23144) UnhandledPromiseRejectionWarning: TypeError: Cannot read property '_events' of undefined
API is defined here:
https://github.com/Blizzard/node-rdkafka/blob/129cb733f5b3271523fb27cd38c08de0f20e0515/index.d.ts#L196
on<E extends Events>(event: E, listener: EventListener<E>): this;
You need to wrap on function into a Promise.
async myConnect(): Promise<void> {
this.innerProducer.connect();
await new Promise<void>((resolve, reject) => {
// TODO: Add reject call on some error.
this.innerProducer.on("ready", () => {
Logger.info("producer is ready to produce for topic {}", this.topic);
this.isReadyToProduce = true;
resolve();
});
});
}
Then when you need to use use use await myConnect() or myConnect().then(() => { some code here! });
I'd like to write a wrapper around Promise.catch() that performs some default actions, like logging, and then calls .catch() to reject the Promise.
I am trying to understand the difference in behavior I'm seeing between these 2 simple bits of sample code.
Why doesn't this work:
declare global {
interface Promise<T> {
catchWrapper(): Promise<T>;
}
}
export {}
Promise.prototype.catchWrapper = function(){
return Promise.prototype.catch.apply(this, e => { console.log(`An error occured: {e}`) });
};
async function f(): Promise<string> {
throw new Error('error');
}
f().catchWrapper()
but this does:
declare global {
interface Promise<T> {
catchWrapper(errHandler: (errObj: any) => void): Promise<T>;
}
}
export {}
Promise.prototype.catchWrapper = function(){
return Promise.prototype.catch.apply(this, arguments);
};
async function f(): Promise<string> {
throw new Error('An error');
}
f().catchWrapper(e => { console.log(`An error occured: ${e}`) });
The first example is creating the lambda function inside of catchWrapper, this example generates an unhandled promise rejection.
The second example creates the lambda outside of catchWrapper and passes it in. This works as expected.
Am I seeing some kind of incompatibility with this and the lambda?
(I'm using typescript 3.1.2 and node 8.11.3)
Edit:
The first example works after putting the arrow function inside of an array like so:
return Promise.prototype.catch.apply(this,
[e => { console.log(`An error occured: ${e}`) }]);
arguments is an array of arguments. So in your second example:
Promise.prototype.catchWrapper = function(){
return Promise.prototype.catch.apply(this, arguments);
};
You are passing in arguments as an array, not as a function as you are maybe expecting. You can try passing in arguments[0] to get that function.
Or you could use a spread operator:
Promise.prototype.catchWrapper = function(){
return Promise.prototype.catch.apply(this, ...arguments);
};
But the short answer is you are passing an array of arguments, instead of the arguments as separate parameters.
I have a constructor which fetches data from a DynamoDB using promisified dynogels to populate part of the object's properties.
So after instantiating an instance of that object, the property is not populated, here is an extract of the code:
export class QueryAuthoriser {
authPerms: [AuthPerms];
constructor (data: string) {
AuthPermsDDB.scan().execAsync().then ( (perms) => {
perms.Items.forEach(element => {
this.authPerms[element.name] = <AuthPerms> element.attrs
})
}).catch (err => {
console.log ('%%%%%%%%%%%%%% Err loading authPerms: ', err)
})
}
authFieldAccess (fieldName: string, args?:any): Promise<boolean> {
return new Promise ((resolve, reject) => {
console.log ('________________ authFieldAccess called for: ', fieldName)
console.log ('________________ this.authPerms entry: ', this.authPerms[fieldName])
resolve (true)
})
[...]
}
So when authFieldAccess method is called, the field this.authPerms is undefined. How can I fix this?
Thanks, I'm learning node and typescript the hard way :O
You generally don't want to carry out an asynchronous operation in a constructor because it complicates creating an object and then knowing when the async operation is done or if it had an error because you need to allow the constructor to return the object, not a promise that would tell you when the async operation was done.
There are several possible design options:
Option #1: Don't do any async operation in the constructor. Then, add a new method with an appropriate name that does the async operation and returns a promise.
In your case, you could make the new method be scan() that returns a promise. Then, you'd use your object by creating it and then calling scan and then using the returned promise to know when the data is valid.
I don't know TypeScript myself, so I'll give a modified version of your code, but the concept is the same either way whether it's TypeScript or plain Javascript:
export class QueryAuthoriser {
authPerms: [AuthPerms];
constructor (data: string) {
}
scan () {
return AuthPermsDDB.scan().execAsync().then ( (perms) => {
perms.Items.forEach(element => {
this.authPerms[element.name] = <AuthPerms> element.attrs
})
}).catch (err => {
console.log ('%%%%%%%%%%%%%% Err loading authPerms: ', err)
})
}
}
// usage
let obj = new QueryAuthoriser(...);
obj.scan(...).then(() => {
// the object is full initialized now and can be used here
}).catch(err => {
// error here
})
Option #2: Initiate the async operation in the constructor and use a promise in the instance data for the caller to know when everything is done.
export class QueryAuthoriser {
authPerms: [AuthPerms];
constructor (data: string) {
this.initialScan = AuthPermsDDB.scan().execAsync().then ( (perms) => {
perms.Items.forEach(element => {
this.authPerms[element.name] = <AuthPerms> element.attrs
})
}).catch (err => {
console.log ('%%%%%%%%%%%%%% Err loading authPerms: ', err)
})
}
}
// usage
let obj = new QueryAuthoriser(...);
obj.initialScan.then(() => {
// the object is full initialized now and can be used here
}).catch(err => {
// error here
});
Option #3: Use a factory function that returns a promise that resolves to the object itself.
export createQueryAuthorizer;
function createQueryAuthorizer(...) {
let obj = new QueryAuthorizer(...);
return obj._scan(...).then(() => {
// resolve with the object itself
return obj;
})
}
class QueryAuthoriser {
authPerms: [AuthPerms];
constructor (data: string) {
}
_scan () {
return AuthPermsDDB.scan().execAsync().then ( (perms) => {
perms.Items.forEach(element => {
this.authPerms[element.name] = <AuthPerms> element.attrs
})
}).catch (err => {
console.log ('%%%%%%%%%%%%%% Err loading authPerms: ', err)
})
}
}
// usage
createQueryAuthorizer(...).then(obj => {
// the object is fully initialized now and can be used here
}).catch(err => {
// error here
});
My preference is for option #3 for several reasons. It captures some shared code in the factory function that every caller has to do in the other schemes. It also prevents access to the object until it is properly initialized. The other two schemes just require documentation and programming discipline and can easily be misused.
I want to handle 10,000,000 rows
This is my code:
stmt.each('select * from very_big_table',function(err,rows,next){
console.log('This can take 2 seconds'
})
The question is: It will eat all my RAM, or it will read from hard-drive row after row?
I've just recently created this helper class to handle streaming:
import { ReadableTyped } from '#naturalcycles/nodejs-lib'
import { Database, Statement } from 'sqlite'
import { Readable } from 'stream'
/**
* Based on: https://gist.github.com/rmela/a3bed669ad6194fb2d9670789541b0c7
*/
export class SqliteReadable<T = any> extends Readable implements ReadableTyped<T> {
constructor(private stmt: Statement) {
super( { objectMode: true } );
// might be unnecessary
// this.on( 'end', () => {
// console.log(`SQLiteStream end`)
// void this.stmt.finalize()
// })
}
static async create<T = any>(db: Database, sql: string): Promise<SqliteReadable<T>> {
const stmt = await db.prepare(sql)
return new SqliteReadable<T>(stmt)
}
/**
* Necessary to call it, otherwise this error might occur on `db.close()`:
* SQLITE_BUSY: unable to close due to unfinalized statements or unfinished backups
*/
async close(): Promise<void> {
await this.stmt.finalize()
}
// count = 0 // use for debugging
override async _read(): Promise<void> {
// console.log(`read ${++this.count}`) // debugging
try {
const r = await this.stmt.get<T>()
this.push(r || null)
} catch(err) {
console.log(err) // todo: check if it's necessary
this.emit('error', err)
}
}
}
Both. statement.each will not use any extra memory if the function is fast and synchronous, however with asynchronous functions it will start all asynchronous processing as fast as it can load the data from disk, and this will eat up all your RAM.
As is also said in the issue you posted here https://github.com/mapbox/node-sqlite3/issues/686 it is possible to get your desired behavior using Statement.get(). Statement.get() without any parameters will fetch the next row. So you can implement your own async version like this:
function asyncEach(db, sql, parameters, eachCb, doneCb) {
let stmt;
let cleanupAndDone = err => {
stmt.finalize(doneCb.bind(null, err));
};
stmt = db.prepare(sql, parameters, err => {
if (err) {
return cleanupAndDone(err);
}
let next = err => {
if (err) {
return cleanupAndDone(err);
}
return stmt.get(recursiveGet);
};
// Setup recursion
let recursiveGet = (err, row) => {
if (err) {
return cleanupAndDone(err);
}
if (!row) {
return cleanupAndDone(null);
}
// Call the each callback which must invoke the next callback
return eachCb(row, next);
}
// Start recursion
stmt.get(recursiveGet);
});
}
Note, slightly different syntax than the built in Statement.each, errors are only sent to the last callback and no support for optional parameters.
Also note that this is slower than the normal Statement.each function, which can be improved by issuing multiple get calls so the next row is waiting when next is invoked, but it is much harder to follow that code.
Example for using the snippet:
let rowCount = 0;
asyncEach(db, 'SELECT * from testtable', [], (row, next) => {
assert.isObject(row);
rowCount++;
return next();
}, err => {
if (err) {
return done(err);
}
assert.equal(rowCount, TEST_ROW_COUNT);
done();
});
I have created a small class, which wrappers around a generic callback function signature, and returns a promise.
however then it calls the handle menthod the _resolve is undefined.
/// <reference path="typings/es6-promise/es6-promise.d.ts" />
import rsvp = require('es6-promise');
var Promise = rsvp.Promise;
/** * wrapper a callback style call into a promise */ class
Promiseify<T> {
private _promise : Promise<T>;
private _resolve : (result?: T) => void;
private _reject : (error: any) => void;
/**
* create wrapper, example: new Promiseify(call).promise
* #param call example (handle) => yourDb.update(document, handle)
*/
constructor(call:(handle:(error: any, result: T) => void) => void){
this._promise = new Promise<T>((resolve, reject) =>{
this._resolve = resolve;
this._reject = reject;
call(this.handle);
});
}
handle(error: any, result: T) {
if(error != null) this._reject(error);
else this._resolve(result);
}
/**
* get the es6 promise which can then be passed to external callers
* #returns {Promise<T>} the es6 promise which this class creates.
*/
get promise() : Promise<T>{
return this._promise;
}
}
//the test rig
new Promiseify<string>((handle)=> {
handle(null, 'hello world');
}).promise.then((greeting: string)=> {
console.log(greeting);
});
am i missing something, btw the JS under the covers looks ok too
also if it helps I am running this on Node.JS, inside Webstorm 9.03
You are passing this.handle into call(), so when call() calls it, this will be null and that is why your approach isn't working.
I'm not sure what would be most idiomatic in TypeScript, but you can do:
call(this.handle.bind(this));
or
call((error, result) => this.handle(error, result));
Alternatively, you could get rid of the handle method entirely, and do this:
this._promise = new Promise<T>((resolve, reject) => {
call(function (error, result) {
if (error) { reject(error); }
resolve(result);
});
});
but then that just leaves the question of why you need a class at all, when you could instead use an 8-line function that does the same thing:
function promisify<T>(call:(handle:(error: any, result :T) => void) => void) {
return new Promise<T>((resolve, reject) => {
call(function (error, result) {
if (error) { reject(error); }
resolve(result);
});
});
}
You have two options here:
The first one is to declare the handle method as a property like so:
handle = (error: any, result: T) => {
if(error != null) this._reject(error);
else this._resolve(result);
}
Or change your call method so it calls it on the instance like below:
var that = this;
call(function(e, r) { that.handle(e, r); });