I have this function:
export const changeNotes = (userId, note, isStay)
I want to use to change a note, and sometimes I want to use it to change the value of isStay.
const handleOnChange = async e => {
await changeNotes(user.id,null ,true) // await changeNotes(user.id,10 ,null)
}
How do I send different cases?
You can pass an object as an argument to your changeNote function, and then use function argument destructuring to use properties from it. This way, order doesn't matter and only the values you send will be used (anything you don't pass will be undefined).
export const changeNotes = ({ userId, note, isStay }) => {
if (note) { // undefined
}
if (isStay) { // true
// do something
}
}
const handleOnChange = async e => {
await changeNotes({
userId: user.id,
isStay: true
})
}
Related
const productIds = ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqr', ...]
const generateBearerToken = async () => {
await //api calling
} // will return bearerToken
const getSubmissions = async () => {
await // api calling
}
let sellerId = null
const getPublisherId = async (productId) => {
//generating bearer token using generateBearerToken()
await GenerateBearerToken()
//calling API here and setting the value of sellerId
const response = await axios.get(url, { header })
//From this response, I am setting the value of sellerId, then calling getSubmission()
sellerId = response.data.sellerId
await getSubmission()
}
productsIds.map((productId) => {
await getPublisherId(productId)
})
The sellerId which I am getting from getPublisherId, I am using that value in the header for calling getSubmissions. This value (sellerId) is different for different product Ids. But when I am calling this above map function, the sellerId of one is getting passed in other calling getSubmissions, that should be not the case. The sellerId should be passed to that particular getSubmissions only. How to avoid this collision?
This shouldn't be global:
let sellerId = null
Every asynchronous operation is modifying that value, and any other operation will use whatever the value is at that time, not whatever it was when that operation may have been invoked.
Don't use global variables to pass values from one function to another. Just pass the value to the function:
const getSubmissions = async (sellerId) => {
//...
};
and:
const getPublisherId = async (productId) => {
await GenerateBearerToken();
const response = await axios.get(url, { header });
await getSubmissions(response.data.sellerId);
};
As an aside, the code suggests that you're making the same mistake elsewhere and this same thing may need to be corrected in other places.
avoid globals
you are using .map as a "for each". Map is made to transform data.
At a minimum you want:
/* let sellerId = null // < Remove global */
const getPublisherId = async (productId) => {
[...]
const sellerId = response.data.sellerId
await getSubmission()
return sellerId
}
const allSellerIds = productsIds.map((productId) => {
await getPublisherId(productId)
})
However you will probably want to look up Promise.allSettled and others to avoid each publisher from being fetched one at a time sequentially.
is there someway to pass an argument to readable store in svelte ? I have the next code:
export const worker = readable(
[], async (set) => {
const response = await fetchAPI()
set(response)
const interval = setInterval(async () => {
const response = await fetchAPI()
set(response)
}, 10000)
})
I want pass an argumeto to that readable function with the response of the api to set the result of it via set().
Thanks
You can access any variables that are in scope, so you can "pass" a variable from outside the readable's function.
export function worker(arg) {
return readable([], async set => {
const response = await fetchAPI(arg); // arg is in scope here
// ...
});
}
How can I achieve dynamic callback arguments in JavaScript
I have a three functions I want to compose. Why am I doing this is because I want to encapsulate the details of the initDB so I can write less code. Here's what it looks like below:
const initDB = (schemas: any[]) =>
Realm.open({ path: 'CircleYY.realm', schema: schemas })
.then((realm: Realm) => ({ realm }))
.catch((error: Error) => ({ error }));
So basically this function just initialize a DB and it will return a DB instance or an Error.
I also have some specific database write functions like this below:
// Delete a message
const deleteOrder = (orderID: string, realm: Realm) => {
realm.write(() => {
const order = realm.objects('Orders').filtered(`primaryKey = ${id}`);
realm.delete(order);
});
};
and I have this three functions below:
makeDBTransaction(deleteOrder(id));
and
makeDBTransaction(writeCommentInOrder(orderId, comment))
and
const makeDBTransaction = async (callback: Function) => {
const { error, realm } = (await initDB([
OrderSchema,
ProductSchema,
])) as InitRealm;
if (error) return { error };
callback(realm); // Pass realm while maintaining the arguments specified in the callback which is dynamic
return realm.close();
};
I also want to pass the realm into the callback that can have more than 2 arguments.
How can I achieve that?
I think you can keep adding arguments in an array in a required order and then apply the arguments to the function and call that function.
//for example
function foo1(x, y) {
console.log(x,y);
}
function foo2(cb, y) {
const x = 3;
cb.apply(null, [x,y]);
}
foo2(foo1, 5);
//So your code will be like this
makeDBTransaction(deleteOrder, [id]);
const makeDBTransaction = async (callback: Function, arg: any[]) => {
const { error, realm } = (await initDB([
OrderSchema,
ProductSchema,
])) as InitRealm;
if (error) return { error };
arg.push(realm);
callback.apply(null, arg);
return realm.close();
};
I have an object of convos with userIDs that I need to loop through and, inside the loop, I need to make a call to Firebase to get the corresponding userName and then return an object with the convos, userNames, and userIDs.
I have tried using the async/await and the result I get from console.log is correct but my return statement directly after that, is undefined. Why is this happening? They are receiving the same object.
store.js getter snippet
getConvosObj: state => {
var convoObj = {};
var userConvos = state.userProfile.convos;
async function asyncFunction() {
for (const key in userConvos) {
if (userConvos.hasOwnProperty(key)) {
const userID = userConvos[key];
var userName;
await fire.database().ref('/users/' + userID + '/userName').once('value', async (snapshot) => {
userName = await snapshot.val();
convoObj[key] = {userName, userID}
})
}
}
console.log(convoObj); //result: correct object
return convoObj; //result: undefined
}
asyncFunction();
}
Why is this happening ?
Because you called async function synchronously.
lets make your code simpler.
getConvosObj: state => {
async function asyncFunction() {
// ...
}
asyncFunction();
}
at this point, your getConvosObj() will return nothing, because getConvosObj() ends before asyncFunction() ends.
you need to wait until your asyncFunction() ends, then your code should be like this:
getConvosObj: async state => { // <- changed here
async function asyncFunction() {
// ...
}
await asyncFunction(); // <- changed here too
}
but you should not do like this, because getters are not meant to be asynchronous by design.
this may work, but you should try a different approach.
So what should you do ?
Use actions before using getters
this is a basic approach.
async functions should be in actions.
so your store should be like this:
export default () =>
new Vuex.Store({
state: {
convoObj: null
},
mutations: {
updateConvoObj(state, payload) {
state.convoObj = payload;
}
},
actions: {
async fetchAndUpdateConvoObj({ state, commit }) {
const fetchUserData = async userId => {
const snapShot = await fire.database().ref('/users/' + userID + '/userName').once('value');
const userName = snapShot.val();
return {
userName: userName,
userID: userId
}
}
const userConvos = state.userProfile.convos;
let convoObj = {};
for (const key in userConvos) {
if (userConvos.hasOwnProperty(key)) {
const userId = userConvos[key];
const result = await fetchUserData(userId);
convoObj[key] = {
userName: result.userName,
userId: result.userId
}
}
}
commit('updateConvoObj', convoObj);
}
}
});
then call your actions before using getter in your sample.vue:
await this.$store.dispatch('fetchAndUpdateConvoObj');
convoObj = this.$store.getters('getConvoObj');
wait for db and update store, then get its state.
doesnt make sense ?
Use vuexfire to connect your store directly to Realtime Database
another approach is this.
use vuexfire, then the state of the store is always up-to-date to realtime database, so you can call getters without calling actions.
i got tired to refactor / write a code, so google some sample if you wanna use that plugin :)
i refactored the original code a lot, so there should be some typo or mistake.
plz revise is if you find one.
I'm trying to pass a variable into a page.evaluate() function in Puppeteer, but when I use the following very simplified example, the variable evalVar is undefined.
I can't find any examples to build on, so I need help passing that variable into the page.evaluate() function so I can use it inside.
const puppeteer = require('puppeteer');
(async() => {
const browser = await puppeteer.launch({headless: false});
const page = await browser.newPage();
const evalVar = 'WHUT??';
try {
await page.goto('https://www.google.com.au');
await page.waitForSelector('#fbar');
const links = await page.evaluate((evalVar) => {
console.log('evalVar:', evalVar); // appears undefined
const urls = [];
hrefs = document.querySelectorAll('#fbar #fsl a');
hrefs.forEach(function(el) {
urls.push(el.href);
});
return urls;
})
console.log('links:', links);
} catch (err) {
console.log('ERR:', err.message);
} finally {
// browser.close();
}
})();
You have to pass the variable as an argument to the pageFunction like this:
const links = await page.evaluate((evalVar) => {
console.log(evalVar); // 2. should be defined now
…
}, evalVar); // 1. pass variable as an argument
You can pass in multiple variables by passing more arguments to page.evaluate():
await page.evaluate((a, b c) => { console.log(a, b, c) }, a, b, c)
The arguments must either be serializable as JSON or JSHandles of in-browser objects: https://pptr.dev/#?show=api-pageevaluatepagefunction-args
I encourage you to stick on this style, because it's more convenient and readable.
let name = 'jack';
let age = 33;
let location = 'Berlin/Germany';
await page.evaluate(({name, age, location}) => {
console.log(name);
console.log(age);
console.log(location);
},{name, age, location});
Single Variable:
You can pass one variable to page.evaluate() using the following syntax:
await page.evaluate(example => { /* ... */ }, example);
Note: You do not need to enclose the variable in (), unless you are going to be passing multiple variables.
Multiple Variables:
You can pass multiple variables to page.evaluate() using the following syntax:
await page.evaluate((example_1, example_2) => { /* ... */ }, example_1, example_2);
Note: Enclosing your variables within {} is not necessary.
It took me quite a while to figure out that console.log() in evaluate() can't show in node console.
Ref: https://github.com/GoogleChrome/puppeteer/issues/1944
everything that is run inside the page.evaluate function is done in the context of the browser page. The script is running in the browser not in node.js so if you log it will show in the browsers console which if you are running headless you will not see. You also can't set a node breakpoint inside the function.
Hope this can help.
For pass a function, there are two ways you can do it.
// 1. Defined in evaluationContext
await page.evaluate(() => {
window.yourFunc = function() {...};
});
const links = await page.evaluate(() => {
const func = window.yourFunc;
func();
});
// 2. Transform function to serializable(string). (Function can not be serialized)
const yourFunc = function() {...};
const obj = {
func: yourFunc.toString()
};
const otherObj = {
foo: 'bar'
};
const links = await page.evaluate((obj, aObj) => {
const funStr = obj.func;
const func = new Function(`return ${funStr}.apply(null, arguments)`)
func();
const foo = aObj.foo; // bar, for object
window.foo = foo;
debugger;
}, obj, otherObj);
You can add devtools: true to the launch options for test
I have a typescript example that could help someone new in typescript.
const hyperlinks: string [] = await page.evaluate((url: string, regex: RegExp, querySelect: string) => {
.........
}, url, regex, querySelect);
Slightly different version from #wolf answer above. Make code much more reusable between different context.
// util functions
export const pipe = (...fns) => initialVal => fns.reduce((acc, fn) => fn(acc), initialVal)
export const pluck = key => obj => obj[key] || null
export const map = fn => item => fn(item)
// these variables will be cast to string, look below at fn.toString()
const updatedAt = await page.evaluate(
([selector, util]) => {
let { pipe, map, pluck } = util
pipe = new Function(`return ${pipe}`)()
map = new Function(`return ${map}`)()
pluck = new Function(`return ${pluck}`)()
return pipe(
s => document.querySelector(s),
pluck('textContent'),
map(text => text.trim()),
map(date => Date.parse(date)),
map(timeStamp => Promise.resolve(timeStamp))
)(selector)
},
[
'#table-announcements tbody td:nth-child(2) .d-none',
{ pipe: pipe.toString(), map: map.toString(), pluck: pluck.toString() },
]
)
Also not that functions inside pipe cant used something like this
// incorrect, which is i don't know why
pipe(document.querySelector)
// should be
pipe(s => document.querySelector(s))