I'm having quite a bit of trouble with the scope of the Meteor.call procedure. It won't set my scope variable to the result.length
'takeaways': function (userId) {
var len = 0;
Meteor.call('userTakeaways', userId, function (error, result) {
if (error) {
console.log('there was an error finding the number of messages that were takeaways')
} else {
len = result.length; // result.length is 2
}
});
console.log(len); // still 0
return len;
}
Please help!
Thank you :)
len is not a reactive variable. So if the len value changes, it won't update the spacebar value.
So here two approach to solving this problem:
1. using reactive var/session.
//Make sure you have install reactive var package
var len = new ReactiveVar(0);
Template['name'].helpers({
'takeaways': function (userId) {
Meteor.call('userTakeaways', userId, function (error, result) {
if (error) {
console.log('there was an error finding the number of messages that were takeaways')
} else {
len.set(result.length); // result.length is 2
}
});
console.log(len.get()); // You will get 2 when response come from you method call.
return len.get();
}
});
2. Using 'simple:reactive-method' package
takeaways : function(userId){
return ReactiveMethod.call('userTakeaways', userId).length;
}
try adding in the else statement
return len = result.length;
as you can see bellow.
'takeaways': function (userId) {
var len = 0;
Meteor.call('userTakeaways', userId, function (error, result) {
if (error) {
console.log('there was an error finding the number of messages that were takeaways')
} else {
return len = result.length; // result.length is 2
}
});
console.log(len); // still 0
return len;
}
Related
I want to get the last deviceId.
Please try the following code on a smartphone.
https://www.ofima.ch/file1.html
Inside the function "getConnectedDevices" the variable deviceId ok.
But outside is returned a promise and not the variable deviceId.
How can I get the variable deviceId ?
Thanks
Miche
Explanation:
You need to wrap your alert in an async function and use await. Also to take the value from the promise you needed .then(). Hope the below helps.
Original code:
async function getConnectedDevices() {
var index;
const devices = await navigator.mediaDevices.enumerateDevices();
for (var i=0; i<devices.length; i++) {
if (devices[i].kind == "videoinput") {
index = i;
}
}
var deviceId = devices[index].deviceId;
alert('deviceId is ok: ' + deviceId);
return (deviceId);
}
const deviceId = getConnectedDevices();
alert('deviceId is not defined, why ?: ' + deviceId);
New code:
async function getConnectedDevices() {
let index;
const devices = await navigator.mediaDevices.enumerateDevices();
for (let i=0; i < devices.length; i++) {
console.debug(devices[i]);
if (devices[i].kind == "videoinput") {
index = i;
}
}
console.log('deviceId is ok: ', devices[index]);
return devices[index].deviceId;
}
(async() => {
const deviceId = await getConnectedDevices().then();
alert(`deviceId: ${deviceId}`);
})();
And a quick hack for storing the deviceId in the window
console.log('globalDeviceId should be undefined', window.globalDeviceObj);
async function getConnectedDevices() {
let index;
const devices = await navigator.mediaDevices.enumerateDevices();
for (let i = 0; i < devices.length; i++) {
console.debug(devices[i]);
if (devices[i].kind == "videoinput") {
index = i;
}
}
console.log('deviceId is ok', devices[index]);
return devices[index];
}
function getDeviceId() {
(async() => {
window.globalDeviceObj = await getConnectedDevices().then();
console.log(`globalDeviceId set: ${JSON.stringify(window.globalDeviceObj)}`);
})();
}
function tick() {
if(typeof window.globalDeviceObj === 'undefined'){
requestAnimationFrame(tick);
}else {
alert(`globalDeviceId get: ${JSON.stringify(window.globalDeviceObj)}, with deviceId: ${(window.globalDeviceObj.deviceId)}`)
}
}
function init() {
tick();
getDeviceId();
}
init();
I wonder if it is good enough to test if the reference exists
BEFORE I start a transaction on this reference?
e.g: by using .once('value') and snapshot.exists()
I mean if the check is outside the transaction isn't there a risk another user to delete the reference just after the check and before the transacton executor function?
==== edited to include the minimal complete code =====
here is my data in realtime database:
activeOffers
-LKohyZ58cnzn0vCnt9p
details
direction: "city"
seatsCount: 2
timeToGo: 5
uid: "-ABSIFJ0vCnt9p8387a" ---- offering user
And here is my code flow:
===== index.js =====
entries = require('./entries');
/// cloud function
exports.TEST_askOfferSeats = functions.https.onCall((data, context) => {
console.log('data: ' + JSON.stringify(data));
return entries.askSeats(data);
});
here is my test data sent by Postman:
{
"data":
{
"uid": "-FGKKSDFGK12387sddd", ---- the requesting/asking user
"id": "-LKpCACQlL25XTWJ0OV_",
"details":
{
"direction": "city",
"seatsCount": 1,
"timeToGo": 5
}
}
}
===== entries.js =======
exports.askSeats = function(data) {
const TAG = '[askSeats]: ';
var entryRef = db.ref('activeOffers/' + data.id);
return globals.exists(entryRef)
.then((found)=>{
if (found) {
return dealSeats(entryRef, data);
} else {
return 'Offer not found [' + data.id + ']';
}
});
}
===== globals.js ======
exports.exists = (ref)=>{
return ref.once('value')
.then((snapshot)=>{
return (snapshot.exists());
});
}
===== entries.js =====
dealSeats = function(entryRef, data) {
const TAG = '[dealSeats]: ';
return entryRef.transaction((entry)=>{
if (entry) {
if ((entry.deals) && (entry.deals[data.uid])) {
throw new Error('You've already made a deal.');
} else if (entry.details.seatsCount >= data.details.seatsCount) {
entry.details.seatsCount -= data.details.seatsCount;
var deal = [];
deal.status = 'asked';
deal.details = data.details;
if (!entry.deals) {
entry.deals = {};
}
entry.deals[data.uid] = deal;
} else {
throw new Error('Not enought seats.');
}
}
return entry;
})
.then((success)=>{
return success.snapshot.val();
})
.catch((error)=>{
return Promise.reject(error);
});
}
Btw: is this 'throw new Error(......)' is the correct way to break the transaction ?
========= updated with final source ===
Thanks to Doug Stevenson.
So here is my final source that is working fine. If someone sees a potential problem please let me know. Thanks.
dealSeats = function(entryRef, data) {
const TAG = '[dealSeats]: ';
var abortReason;
return entryRef.transaction((entry)=>{
if (entry) {
if ((entry.deals) && (entry.deals[data.uid])) {
abortReason = 'You already made a reservation';
return; // abort transaction
} else if (entry.details.seatsCount >= data.details.seatsCount) {
entry.details.seatsCount -= data.details.seatsCount;
var deal = [];
deal.status = 'asked';
deal.details = data.details;
if (!entry.deals) {
entry.deals = {};
}
entry.deals[data.uid] = deal;
// Reservation is made
} else {
abortReason = 'Not enought seats';
return; // abort transaction
}
}
return entry;
})
.then((result)=>{ // resolved
if (!result.committed) { // aborted
return abortReason;
} else {
let value = result.snapshot.val();
if (value) {
return value;
} else {
return 'Offer does not exists';
}
}
})
.catch((reason)=>{ // rejected
return Promise.reject(reason);
});
}
If you read a value before a transaction, then read it again inside the transaction, you have absolutely no guarantee that the second read inside the transaction will yield the same result as the initial read outside before the transaction. It could be modified by the time the transaction is performed.
If you want a truly atomic update, only check value that participate in the transaction within the transaction itself, and make a decision about what to do in the transaction handler.
I'm using Node/Puppeteer in the code below, passing in a large list of URL's for traversal and scraping. It has been difficult to do it asynchronously, though I find that I am getting closer and closer to the answer. I am currently stuck on an issue related to the following error.
UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 17): Error: Protocol error (Target.createTarget): Target closed.
This error occurs once upon every iteration of the while loop. Though I'm not sure what I may be doing incorrectly.
Could someone help me do the following:
1) Diagnose the source of the error.
2) Potentially find a more effective way to traverse a large list of URLs asynchronously.
async function subProc(list, batchSize) {
let subList = null;
let i = 0;
while (list.length > 0) {
let browser = await puppeteer.launch();
subList = list.splice(0, batchSize);
console.log("Master List Size :: " + list.length);
console.log("SubList Size :: " + subList.length);
for (let j = 0; j < subList.length; j++) {
promiseArray.push(new Promise((resolve, reject) => {
resolve(pageScrape(subList[j], browser));
}));
}
Promise.all(promiseArray)
.then(response => {
procArray.concat(response);
});
promiseArray = new Array();
try {
await browser.close();
} catch(ex){
console.log(ex);
}
};
}
async function pageScrape(url, browser) {
let page = await browser.newPage();
await page.goto(url, {
timeout: 0
});
await page.waitFor(1000);
return await page.evaluate(() => {
let appTitle = document.querySelector('').innerText;
let companyName = document.querySelector('').innerText;
let dateListed = document.evaluate("", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.innerText;
let category = document.evaluate("']//a//strong", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.innerText;
/* */
return {
appTitle,
companyName,
dateListed,
category
}
}).then(response => {
let urlData = {
id: subList[j],
appName: response.appTitle,
companyName: response.companyName,
dateListed: response.dateListed,
category: response.category
}
return urlData;
});
};
I figured out the solution to the problem I was having.
Every computer is limited in its processing ability, so instead of iterating through 1000 urls simultaneously you have to break it down into smaller pieces.
By using a PromiseAll, and iterating and scraping 10 urls at a time and storing these values in an array, I was able to throttle the processing required to iterate through all 1000 urls.
processBatch(subData, 10, procArray).then((processed)=>{
for(let i = 0; i < procArray.length; i++){
for(let j = 0; j < procArray[i].length; j++){
results.push(procArray[i][j]);
}
}
function processBatch(masterList, batchSize, procArray){
return Promise.all(masterList.splice(0, batchSize).map(async url =>
{
return singleScrape(url)
})).then((results) => {
if (masterList.length < batchSize) {
console.log('done');
procArray.push(results);
return procArray;
} else {
console.log('MasterList Size :: ' + masterList.length);
procArray.push(results);
return processBatch(masterList, batchSize, procArray);
}
})
}
I would like to count how many entreprise are in some category but I'm stuck with the asynchrone concept.
Here's what I already have:
Category.getall(function(err, cat){
if(err) return res.negotiate(err);
catIds = []
for( var iCat in cat){
catIds.push(cat[iCat].id)
// and here I would like do something like
Entreprise.count({category_id: cat[iCat].id}, function(err, nbr){
categoriesOUT.push({categorie: cat, entreprise_number: nbr })
// I know that i can not do it but it's just to help to understand the logic I would like to have.
if(cat.length==iCat){
return res.json({categories: categoriesOUT})
}
})
}
})
There are a couple of ways to handle this. One would be to bring in a promise library like Q. Another would be a single database call that can count up enterprise objects grouped by category_id... however, I think that would go beyond Waterline's normal queries, you would have to use .query or .native or something.
The easiest quick fix for you is to just keep a counter of how many results you have handled. You may get tired of this approach after using it a couple of times, but it would look something like this:
Category.getall(function(err, cat){
if(err) { return res.negotiate(err); }
var catIds = [], categoriesOut = [], processedCategories = 0;
for( var iCat in cat){
catIds.push(cat[iCat].id)
Entreprise.count({category_id: cat[iCat].id}, function(err, nbr) {
if (err) {
categoriesOUT.push({categorie: cat, entreprise_number: 0});
} else {
categoriesOUT.push({categorie: cat, entreprise_number: nbr });
}
processedCategories += 1;
if (processedCategories >= cat.length) {
return res.json({categories: categoriesOUT});
}
});
}
});
Here's how I finaly get it only with MySQL request as suggered by #arbuthnott
(The category field is call domaine here)
Domaine.getall(function(err, domaines){
if(err){return res.negotiate(err)}
var domNames = {}, domContain = {}, domOut = [];
Entreprise.query('SELECT domaine_id, COUNT(*) FROM entreprise GROUP BY domaine_id', function(err, entreprises){
if(err){return res.negotiate(err)}
entreprises = JSON.parse(JSON.stringify(entreprises));
for(var ent of entreprises){
domContain[ent['domaine_id']] = ent['COUNT(*)'];
}
for(var iDom in domaines){
var countAdded = false;
for(var dc in domContain){
if(dc==domaines[iDom].id) {
domaines[iDom].entreprises_count = domContain[dc];
countAdded = true;
}
}
if(!countAdded) domaines[iDom].entreprises_count = 0;
}
res.json({domaines:domaines})
})
})
I copy code from: How does the messages-count example in Meteor docs work? it does not work. client call Counts.find().count() method, I expect it to output 1 but the result is 0 ,can you tell me why?
//server code
if (Meteor.is_server)
{
Meteor.startup(function (){
console.log("server is startup...");
Messages = new Meteor.Collection("messages");
if(Messages.find().count() == 0){
for(var i=0;i<7;i++){
Messages.insert({room_id:"00"+i,text:"message "+i});
}
}
console.log("room_id:001 messages count="+Messages.find({room_id:"001"}).count());
//print--->room_id:001 messages count=1 (it's ok)
Meteor.publish("counts-by-room", function (roomId) {
var self = this;
var uuid = Meteor.uuid();
var count = 0;
var handle = Messages.find({room_id: roomId}).observe({
added: function (doc, idx) {
count++;
self.set("counts", uuid, {roomId: roomId, count: count});
self.flush();
},
removed: function (doc, idx) {
count--;
self.set("counts", uuid, {roomId: roomId, count: count});
self.flush();
}
// don't care about moved or changed
});
// remove data and turn off observe when client unsubs
self.onStop(function () {
handle.stop();
self.unset("counts", uuid, ["roomId", "count"]);
self.flush();
});
});
});
}
//client code
if (Meteor.is_client)
{
Meteor.startup(function () {
Counts = new Meteor.Collection("counts");
Session.set("roomId","001");
Meteor.autosubscribe(function () {
Meteor.subscribe("counts-by-room", Session.get("roomId"));
});
console.log("I client,Current room "+Session.get("roomId")+" has "
+ Counts.find().count() + " messages.");
//print--->I client,Current room 001 has 0 messages.(trouble:I expect it to output "...has 1 messages" here)
});
}
I try many times and I find the bug.
change the client code to like below,it will print the correct result.
//client code
Meteor.startup(function () {
Counts = new Meteor.Collection("counts");
Session.set("roomId","001");
Meteor.autosubscribe(function () {
Meteor.subscribe("counts-by-room", Session.get("roomId"));
data = Counts.findOne();
if(data){
console.log("I client,Current room "+Session.get("roomId")+" has "
+ data.count + " messages.");
//print--->I client,Current room 001 has 1 messages.(now it's ok!)
}
})
});
Try it like that
Counts = new Meteor.Collection("counts-by-room"); /* correct name of the collection */
/*... subscribing stuff */
Counts.findOne('counts') /* this is the count you published */