Ava unbinds this for t.throws - ava

AVA seems to unbind the instance methods 'this'.
class Person {
constructor(name) {
this.name = name;
}
sayMyName() {
const name = this.name;
return new Promise(function (resolve, reject) {
reject(new Error(name));
});
}
}
test('test', async (t) => {
const person1 = new Person('Bob');
const error = await t.throws(person1.sayMyName);
t.is(error.message, 'Bob');
});
For the above code, I get this:
85: const error = await t.throws(person1.sayMyName);
86: t.is(error.message, 'Bob');
87: });
Difference:
"CannBot read property \'name\' of undefinedb"
I've tried manually binding this promise like this:
person1.sayMyName.bind(person1), but this doesn't seem to work either.

t.throws() accepts a promise, so you just need to call your function:
const error = await t.throws(person1.sayMyName());
Bonus tip:
If you only need to check the error message, your assertion could be simplified to the following:
await t.throws(person1.sayMyName, 'Bob');
AVA seems to unbind the instance methods 'this'.
No, that's how this works in JavaScript. If you pass a class method, you need to bind it to its instance to preserve this. You might find my auto-bind module handy for this.

Related

Save filereader result to variable for later use

I can't find simple answer, but my code is simple.
I tried something like that, but always when i try to console.log my testResult, then i always recieving null. How to save data from file correctly?
public getFile(
sourceFile: File
): string {
let testResult;
const file = sourceFile[0]
const fileReader = new FileReader();
fileReader.readAsText(file, "UTF-8")
fileReader.onloadend = (e) => {
testResult = fileReader.result.toString()
}
console.log(testResult)
return testResult
}
This problem is related to my other topics, main reason is i can't handle load json file, translate them and upload to user. If i can save this file outside onloadend, then i hope i can handle rest of them (other attempts failed, this one blocking me at beginning)
Your issue is quite classical and is related to the asynchronous operations. Function which you assign to the onloadend request is called only when loadend event fires, but the rest of code will not wait for that to happen and will continue execution. So console.log will be executed immediately and then return will actually return testResult while it is still empty.
Firstly, in order to understand what I just said, put the console.log(testResult) line inside of your onloadend handler:
fileReader.onloadend = (e) => {
testResult = fileReader.result.toString();
console.log(testResult);
}
At this point testResult is not empty and you may continue handling it inside this function. However, if you want your getFile method to be really reusable and want it to return the testResult and process it somewhere else, you need to wrap this method into a Promise, like this:
public getFile(
sourceFile: File
): Promise<string> {
return new Promise((resolve) => {
const file = sourceFile[0]
const fileReader = new FileReader();
fileReader.onloadend = (e) => {
const testResult = fileReader.result.toString();
resolve(testResult);
}
fileReader.readAsText(file, "UTF-8");
});
}
Now whereever you need a file you can use the yourInstance.getFile method as follows:
yourInstance.getFile().then(testResult => {
// do whatever you need here
console.log(testResult);
});
Or in the async/await way:
async function processResult() {
const testResult = await yourInstance.getFile();
// do whatever you need
console.log(testResult);
}
If you are now familiar with promises and/or async/await, please read more about here and here.

Access the common method from other component

Here I am using some method for frequently used methods in common method ts file. If I am going to access these method I got null values Please help me out.
CommonMethod.ts:
GetCategoryList(){
let Mylist = [];
this.auth.Get("Master/Category").then((user) => {
Mylist= user.json();
});
return Mylist;
}
My Another component:
I am trying to access common method ts file here. by below way.
import {CommonMethod} from './CommonMethod';
...
...
construtor(private com:CommonMethod){}
ngOninit(){
console.log(this.com.GetCategoryList());
}
this.auth.Get is going to be async in nature due to which the return MyList line will get called even before the callback to the then method is called and the data is arrived and set in MyList.
You can use the async await syntax to fix it:
async GetCategoryList() {
let Mylist = [];
const user = await this.auth.Get("Master/Category");
Mylist = user.json();
return Mylist;
}
You can then use it like this in your Component:
import {CommonMethod} from './CommonMethod';
...
...
construtor(private com: CommonMethod) {}
async ngOninit() {
const myList = await this.com.GetCategoryList();
console.log(myList);
}
PS: Make sure that CommonMethod is a service and is added to the providers array of your #NgModule
Should update your common method:
GetCategoryList(): Promise<any>{
let Mylist = [];
return this.auth.Get("Master/Category").then((user) => {
Mylist= user.json();
Promise.resolve(Mylist);
});
}
And
ngOninit(){
this.com.GetCategoryList().then(results=>{
console.log(results);
});
}

DialogflowSDK middleware return after resolving a promise

I'm currently playing around with the actions-on-google node sdk and I'm struggling to work out how to wait for a promise to resolve in my middleware before it then executes my intent. I've tried using async/await and returning a promise from my middleware function but neither method appears to work. I know typically you wouldn't override the intent like i'm doing here but this is to test what's going on.
const {dialogflow} = require('actions-on-google');
const functions = require('firebase-functions');
const app = dialogflow({debug: true});
function promiseTest() {
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve('Resolved');
}, 2000)
})
}
app.middleware(async (conv) => {
let r = await promiseTest();
conv.intent = r
})
app.fallback(conv => {
const intent = conv.intent;
conv.ask("hello, you're intent was " + intent );
});
It looks like I should at least be able to return a promise https://actions-on-google.github.io/actions-on-google-nodejs/interfaces/dialogflow.dialogflowmiddleware.html
but I'm not familiar with typescript so I'm not sure if I'm reading these docs correctly.
anyone able to advise how to do this correctly? For instance a real life sample might be I need to make a DB call and wait for that to return in my middleware before proceeding to the next step.
My function is using the NodeJS V8 beta in google cloud functions.
The output of this code is whatever the actual intent was e.g the default welcome intent, rather than "resolved" but there are no errors. So the middleware fires, but then moves onto the fallback intent before the promise resolves. e.g before setting conv.intent = r
Async stuff is really fiddly with the V2 API. And for me only properly worked with NodeJS 8. The reason is that from V2 onwards, unless you return the promise, the action returns empty as it has finished before the rest of the function is evaluated. There is a lot to work through to figure it out, here's some sample boilerplate I have that should get you going:
'use strict';
const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const {BasicCard, MediaObject, Card, Suggestion, Image, Button} = require('actions-on-google');
var http_request = require('request-promise-native');
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
function welcome(agent) {
agent.add(`Welcome to my agent!`);
}
function fallback(agent) {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
function handleMyIntent(agent) {
let conv = agent.conv();
let key = request.body.queryResult.parameters['MyParam'];
var myAgent = agent;
return new Promise((resolve, reject) => {
http_request('http://someurl.com').then(async function(apiData) {
if (key === 'Hey') {
conv.close('Howdy');
} else {
conv.close('Bye');
}
myAgent.add(conv);
return resolve();
}).catch(function(err) {
conv.close(' \nUh, oh. There was an error, please try again later');
myAgent.add(conv);
return resolve();
})})
}
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
intentMap.set('Default Fallback Intent', fallback);
intentMap.set('myCustomIntent', handleMyIntent);
agent.handleRequest(intentMap);
});
A brief overview of what you need:
you have to return the promise resolution.
you have to use the 'request-promise-native' package for HTTP requests
you have to upgrade your plan to allow for outbound HTTP requests (https://firebase.google.com/pricing/)
So it turns out my issue was to do with an outdated version of the actions-on-google sdk. The dialogflow firebase example was using v2.0.0, changing this to 2.2.0 in the package.json resolved the issue

How do I use promises in a Chrome extension?

What I am trying to do is create a chrome extension that creates new, nested, bookmark folders, using promises.
The function to do this is chrome.bookmarks.create(). However I cannot just
loop this function, because chrome.bookmarks.create is asynchronous. I need to wait until the folder is created, and get its new ID, before going on to its children.
Promises seem to be the way to go. Unfortunately I cannot find a minimal working example using an asynchronous call with its own callback like chrome.bookmarks.create.
I have read some tutorials 1, 2, 3, 4. I have searched stackOverflow but all the questions do not seem to be about plain vanilla promises with the chrome extension library.
I do not want to use a plugin or library: no node.js or jquery or Q or whatever.
I have tried following the examples in the tutorials but many things do not make sense. For example, the tutorial states:
The promise constructor takes one argument—a callback with two
parameters: resolve and reject.
But then I see examples like this:
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
How this works is a mystery to me.
Also, how can you call resolve() when its never been defined? No example in the tutorials seem to match real life code. Another example is:
function isUserTooYoung(id) {
return openDatabase() // returns a promise
.then(function(col) {return find(col, {'id': id});})
How do I pass in col, or get any results!
So if anyone can give me a minimal working example of promises with an asynchronous function with its own callback, it would be greatly appreciated.
SO wants code, so here is my non-working attempt:
//loop through all
function createBookmarks(nodes, parentid){
var jlen = nodes.length;
var i;
var node;
for(var i = 0; i < nodes.length; i++){
var node = nodes[i];
createBookmark(node, parentid);
}
}
//singular create
function createBookmark(node, parentid){
var bookmark = {
parentId : parentid,
index : node['index'],
title : node['title'],
url : node['url']
}
var callback = function(result){
console.log("creation callback happened.");
return result.id; //pass ID to the callback, too
}
var promise = new Promise(function(resolve, reject) {
var newid = chrome.bookmarks.create(bookmark, callback)
if (newid){
console.log("Creating children with new id: " + newid);
resolve( createBookmarks(bookmark.children, newid));
}
});
}
//allnodes already exists
createBookmarks(allnodes[0],"0");
Just doesn't work. The result from the callback is always undefined, which it should be, and I do not see how a promise object changes anything. I am equally mystified when I try to use promise.then().
var newid = promise.then( //wait for a response?
function(result){
return chrome.bookmarks.create(bookmark, callback);
}
).catch(function(error){
console.log("error " + error);
});
if (node.children) createBookmarks(node.children, newid);
Again, newid is always undefined, because of course bookmarks.create() is asynchronous.
Thank you for any help you can offer.
Honestly, you should just use the web extension polyfill. Manually promisifying the chrome APIs is a waste of time and error prone.
If you're absolutely insistent, this is an example of how you'd promisify chrome.bookmarks.create. For other chrome.* APIs, you also have to reject the callback's error argument.
function createBookmark(bookmark) {
return new Promise(function(resolve, reject) {
try {
chrome.bookmarks.create(bookmark, function (result) {
if (chrome.runtime.lastError) reject(chrome.runtime.lastError)
else resolve(result)
})
} catch (error) {
reject(error)
}
})
}
createBookmark({})
.then(function (result) {
console.log(result)
}).catch(function (error) {
console.log(error)
})
To create multiple bookmarks, you could then:
function createBookmarks(bookmarks) {
return Promise.all(
bookmarks.map(function (bookmark) {
return createBookmark(bookmark)
})
)
}
createBookmarks([{}, {}, {}, {}])
.catch(function (error) {
console.log(error)
})
Take the advantage of the convention that the callback function always be the last argument, I use a simple helper function to promisify the chrome API:
function toPromise(api) {
return (...args) => {
return new Promise((resolve) => {
api(...args, resolve);
});
};
}
and use it like:
toPromise(chrome.bookmarks.create)(bookmark).then(...);
In my use case, it just works most of the time.

await doesn't seem to wait till the async call is finished

I have the async function which uses await calls and I thought that when you use await, it should pause the function execution until the value it received. For some reason, it doesn't work with me.
Here's my function (it is inside a class):
async userExistsInDB(email) {
let userExists;
await MongoClient.connect('mongodb://127.0.0.1:27017/notificator', async(err, db) => {
if (err) throw err;
let collection = db.collection('users');
userExists = await collection.find({email: email}).limit(1).count() > 0;
console.log("INSIDE:\n", userExists);
db.close();
});
console.log("OUTSIDE:\n", userExists);
return userExists;
}
And here's how I call it in another function inside the same class:
async getValidationErrors(formData) {
let userExists = await this.userExistsInDB(formData.email);
console.log("ANOTHER FUNC:\n", userExists);
}
So, I get the following output:
OUTSIDE:
undefined
ANOTHER FUNC:
undefined
INSIDE:
true
although the value INSIDE: true I expect to get printed the first.
Basically, what I need is to get the boolean value userExists from the userExistsInDB function and use it in other code.
What am I doing wrong here?
await only works with promises, so MongoClient.connect(…) would need to return a promise. Yet, you are using it as a callback API, and even with an async (promise-returning) callback function, which is not gonna work. Assuming mongo returns promises if you don't pass a callback, your code should look like
async function userExistsInDB(email) {
let db = await MongoClient.connect('mongodb://127.0.0.1:27017/notificator');
let collection = db.collection('users');
let userExists = (await collection.find({email: email}).limit(1).count()) > 0;
db.close();
return userExists;
}
though ideally you'd rather do
async function userExistsInDB(email) {
let db = await MongoClient.connect('mongodb://127.0.0.1:27017/notificator');
try {
let collection = db.collection('users');
let userCount = (await collection.find({email: email}).limit(1).count();
return userCount > 0;
} finally {
db.close();
}
}