Error: wait.for can only be called inside a fiber - node.js

I have 2 scipts almost identical with a cascade of function calls nested in a fiber.
This one (parsing Tx in a blockchain) with three calls works perfectly
wait.launchFiber(blockchain)
function blockchain() {
foreach block {
parseBlock (blockIndex)
}
}
function parseBlock(blockIndex) {
foreach Tx in block {
parseTx(txHash)
}
}
function parseTx (txHash) {
if ( txHashInDB(txHash) ) {
do something
}
}
function txHashInDB (txHash) {
var theTx = wait.forMethod(Tx, 'findOne', {'hash': txHash});
return (theTx) ? true : false;
}
Then I have to do something similar with the mempool. In this case I don't have blocks, only transactions, so I have only 2 calls and I get this error message:
Error: wait.for can only be called inside a fiber
wait.launchFiber(watchMempool);
function watchMempool() {
web3.eth.filter('pending', function (error, txHash) {
parseTx(txHash);
});
}
function parseTx (txHash) {
if ( txHashInDB(txHash) ) {
do something
}
}
function txHashInDB (txHash) {
var theTx = wait.forMethod(Tx, 'findOne', {'hash': txHash});
return (theTx) ? true : false;
}
I don't understand what the problem is. Those two scripts have the same structure !

I think for array functions like map or filter you need to use the wait.parallel extensions, i.e. in your case something like:
function watchMempool() {
wait.parallel.filter(web3.eth, parseTx);
}
(Note: I'm just assuming web3.eth is an array; if not, you should probably add a bit more context to your question, or try to boil down the problem to a more generic example).

Related

How can I call a function of an exported function of a module in another script? NodeJS

I want to use a function, which is inside another function (function2) in another js file (2nd.js).
My current code looks something like this:
1st.js
module.exports = {
function1(){
function2(){
//...
}
}
}
2nd.js
const { function2 } = require("1st.js")
function2()
This sadly doesn't work and I have no idea how to solve this problem.
You can work with class approach or some older syntax. Any of these approaches results in the same thing, after all, the class syntax in JavaScript is just a thing for programmers, and does not affect the code at all. It is called syntax sugar.
See in the example below how to solve your problem without classes:
Export a object "container" with your functions:
Just as you referred in your example, 1st.js
export default {
functionOne: () => {
console.log("one");
},
functionTwo: (num) => {
console.log(num);
},
functionThree: ({number}) => {
console.log(number);
},
}
And import it with any name you want
...and so on, 2nd.js
import myFunctions from "../myFunctions";
const { functionOne, functionTwo, functionThree} = myFunctions;
functionOne();
functionTwo("two");
functionThree({number: "three"});

Best way to navigate throught a JSON in Node while validating the path

I'm trying to get some info out of a API call in Nodejs, structured something like a JSON:
{
"generated":"2019-11-04T09:34:11+00:00",
"event":{
"id":"19040956",
"start_":"2019-11-16T11:30:00+00:00",
"event_context":{
"sport":{
"id":"1",
"name":"Soccer"
}
}
}
}
I'm not sure about the presence of none of these fields(Json could be incomplete).
Is there a better way to get the value of "name" in JSON.event.event_context.sport.name without an ugly if to not get errors like "cannot get field 'sport' of undefined"?
Currently, I'm doing
if(json.event && json.event.event_context && json.event.event_context.sport) {
return json.event.event_context.sport.name;
}
Is there a better way?
Thank you!
what do you mean by saying "I'm not sure about the presence of none of these fields"?
i don't understand what your'e trying to achieve.
Looks like there is also an interesting package that will allow more conditions on searching json :
https://www.npmjs.com/package/jspath
let getNested = (path, obj) => {
return path.split(".").reduce( getPath, obj);
}
let getPath = (path, key) => {
return (path && path[key]) ? path[key] : null
}
let test = {
"foo": "bar",
"baz": { "one": 1, "two": ["to", "too", "two"] },
"event": { "event_context": { "sport": { "name": "soccer" } } }
}
console.log(getNested("none", test))
console.log(getNested("baz.one", test))
console.log(getNested("baz.two", test))
console.log(getNested("event.event_context.sport.name", test))
You can use lodash get to get a potentially deeply-nested value, and also specify a default in case it doesnt exist.
Example
const _ = require('lodash');
const my_object = {
"generated":"2019-11-04T09:34:11+00:00",
"event":{
"id":"19040956",
"start_":"2019-11-16T11:30:00+00:00",
"event_context":{
"sport":{
"id":"1",
"name":"Soccer"
}
}
};
_.get(my_object, 'event.event_context.sport.name'); // "Soccer"
_.get(my_object, 'event.event_context.sport.nonExistentField', 'default val'); // "default val"
Article: https://medium.com/#appi2393/lodash-get-or-result-f409e73e018b
You can check by using a function to check object keys like :
function checkProperty(checkObject, checkstring){
if(!checkstring)
return false;
var propertiesKeys = checkstring.split('.');
propertiesKeys.forEach(element => {
if(!checkObject|| !checkObject.hasOwnProperty(element)){
return false;
} else {
checkObject= checkObject[element];
}
})
return true;
};
var objectToCheck = {
"generated":"2019-11-04T09:34:11+00:00",
"event":{
"id":"19040956",
"start_":"2019-11-16T11:30:00+00:00",
"event_context":{
"sport":{
"id":"1",
"name":"Soccer"
}
}
}
}
if (checkProperty(objectToCheck ,'event.event_context.sport.name'))
console.log('object to find is : ', objectToCheck .event.event_context.sport.name;)
Yeah there are better ways!
For example, you could use lodash's get() method to reach a nested value.
var object = { 'a': [{ 'b': { 'c': 3 } }] };
_.get(object, 'a[0].b.c');
// => 3
But there is also a native solution.
Currently (11.2019) only Babel can handle this.
I am speaking of Optional chaining. It's new in the Ecmascript world.
Why I like it? Look here!
// Still checks for errors and is much more readable.
const nameLength = db?.user?.name?.length;
What happens when db, user, or name is undefined or null? With the optional chaining operator, JavaScript initializes nameLength to undefined instead of throwing an error.
If you are using Babel as a compiler then you could use it now.
Related link: https://v8.dev/features/optional-chaining

Initialize a Blockly Mutator within JavaScript

Hi,
as far as I know, custom blocks in Blockly can be defined wether in JSON or in JavaScript, but how can a mutator be initialized in JavaScript?
with JSON:
Blockly.defineBlocksWithJSONArray([
{....
"mutator": "myMutatorName"
});
Then the Mutator_MIXIN must be defined and with Blockly.Extension.registerMutator('myMutatorName', Blockly.myMutator_MIXIN, null, null) the mutator is added to the Block.
with JavaScript:
Blockly.Blocks['blockName'] = {
init: function() = {
....
??? this.setMutator(???)???
};
}
So how can this be done in JavaScript?
Kind regards
a new one
I might be just a little bit late here, but I'll leave the answer anyway for those who need a bit more concrete example.
In JavaScript, you don't actually need to bind a mutator to your block, you just need to define mutationToDom() and domToMutation(xmlElement) functions, like so:
Blockly.Blocks['my_custom_block'] = {
init() {
// Define your basic block stuff here
},
// Mutator functions
mutationToDom() {
let container = document.createElement('mutation');
// Bind some values to container e.g. container.setAttribute('foo', 3.14);
return container;
},
domToMutation(xmlElement) {
// Retrieve all attributes from 'xmlElement' and reshape your block
// e.g. let foo = xmlElement.getAttribute('foo');
// this.reshape(foo);
},
// Aux functions
reshape(param){
// Reshape your block...
}
}
Blockly will automagically take care of the rest and allow you to treat your block as dynamic one.
And if you need to used Mutator Editor UI, you must define decompose(workspace) and compose(containerBlock) functions and call this.setMutator(...) to set which blocks are used in the Mutator Editor UI, like so:
Blockly.Blocks['my_custom_block'] = {
init() {
// Define your basic block stuff here
// Set all block that will be used in Mutator Editor UI, in this
// case only 'my_block_A' and
this.setMutator(new Blockly.Mutator(['my_block_A', 'my_block_B']));
},
// Mutator functions
mutationToDom() {
// Same as previous example
},
domToMutation(xmlElement) {
// Same as previous example
},
decompose(workspace) {
// Decomposeyour block here
},
compose(containerBlock) {
// Compose your block here
},
// Aux functions
reshape(param){
// Same as previous example
}
}
Hope that these short examples help someone :)
You have to declare how the xml is loaded to dom, and how it is saved to xml and redrawn. Also notice how it attaches a mutator to a block element in case that is the only part you need to reference a mutator already present.
init: initFunction (Like you have declared.)
mutationToDom: MutationToDom,
domToMutation: DomToMutation,
updateShape_: UpdateShape`
If all you require is to create a reference to a mutator then what you need is an element of this kind, which we will programatically create in a bit:
<mutation mutator_name="true"></mutation>
The following snippet is an example of the extra functions mutationToDom, DomtoMutation UpdateShape which attaches extra input conditionally. I have a block with a checkbox that when enabled, adds an extra input.
function MutationToDom() {
var container = document.createElement('mutation');
var continueOnError = (this.getFieldValue('HasCONTINUE') == 'TRUE');
container.setAttribute('continueOnError', continueOnError);
return container;
}
function DomToMutation(xmlElement) {
var continueOnError = (xmlElement.getAttribute('continueOnError') == 'true');
this.updateShape_(continueOnError);
}
function UpdateShape(continueOnError) {
// Add or remove a Value Input.
if (continueOnError) {
this.appendValueInput("CONTINUE_ON_ERROR")
.setCheck('CONTINUE_ON_ERROR');
} else {
if (this.childBlocks_.length > 0) {
for (var i = 0; i < this.childBlocks_.length; i++) {
if (this.childBlocks_[i].type == 'continue_on_error') {
this.childBlocks_[i].unplug();
break;
}
}
}
this.removeInput('CONTINUE_ON_ERROR');
}
}

YUI, instantiable module that is not a widget?

If I want a module that is instantiable, let say, a module that handles storing preferences in a subcookies, and i want the main cookie to be configurable, but i don't want it to be a widget... what patterns should i use with YUI?
the end code should be something:
Y.use('my-pref-manager', function(Y){
var A = Y.my-pref-manager.prefStore('A"),
B = Y.my-pref-manager.prefStore('B");
// A and B are now loaded with the contents of cookies A and B, if they exist
A.set('xy', 123 );
});
So far i either found patterns that create widgets within my-module or i have to use methods directly in my-method which will be globals and lack initializers, etc.
There is a bunch of ways of doing this. You could do it using Y.Base.create, like below. The code might not be production ready, or even working properly, but hopefully it answers how you can create a module without it being a Widget.
The code below creates a module that extends Y.Base. This let us use Attributes and other cool things. Check the doc for Y.Base.
YUI.add('my-pref-manager', function (Y) {
var PrefManager = Y.Base.create('myPrefManager', Y.Base, [], {
initializer: function () {
this.after('prefsChange', this.changePref);
},
changePref: function (e) {
Y.Cookie.setSub(this.get('prefStore'), e.subAttrName, this.get(e.subAttrName));
},
setPref: function (name, val) {
this.set('prefs.'+name, val);
},
getPref: function (name) {
return this.get('prefs.'+name);
}
}, {
ATTRS: {
prefStore: {
value: null,
setter: function (val) {
return Y.Cookie.set(val, val);
}
},
prefs: {
value: {}
}
}
});
Y.namespace('My').PrefManager = PrefManager;
}, '0.0.1', {
requires: ['base', 'cookie']
});
YUI().use('my-pref-manager', function (Y) {
var A = new Y.My.PrefManager({prefStore: 'userPrefs'}),
B = new Y.My.PrefManager({prefStore: 'displayPrefs'});
A.setPref('x', 3);
A.setPref('y', 54);
B.setPref('tty', 7);
console.log(A.getPref('x')); // 3
});
Try it out: http://jsfiddle.net/B62nu/

Yii search with criteria and add parameters

I have one form and i want to search in my database. I create the object with all parameters but i have one problem. When write in one textfield search works fine and the query run correctly . When write two or more textfields params doesn't work and i have a fail execution of query :
WHERE
((((id_reservation=:id_reservation) AND (start=:start)) AND (end=:end)) AND
(fkCustomer.first_name=:first_name))
params doesn't replace.
$criteria=new CDbCriteria;
$criteria->with =array('fkCustomer');
if(!empty($start))
{
$criteria->addCondition('start=:start');
$criteria->params=array(':start'=>$start);
}
if(!empty($end))
{
$criteria->addCondition('end=:end');
$criteria->params=array(':end'=>$end);
}
if(!empty($merge->customer_name))
{
$criteria->addCondition('fkCustomer.first_name=:first_name');
$criteria->params=array(':first_name'=>$merge->customer_name);
}
if(!empty($merge->customer_surname))
{
$criteria->addCondition('fkCustomer.last_name=:last_name');
$criteria->params=array(':last_name'=>$merge->customer_surname);
}
if(!empty($merge->customer_email))
{
$criteria->addCondition('fkCustomer.email=:email');
$criteria->params=array(':email'=>$merge->customer_email);
}
$criteria->limit = 100;
It's because in every if block you replace the params array. Build an array in the if blocks then add it to $criteria->params on the last line, outside the blocks.
For instance:
$criteria=new CDbCriteria;
$criteria->with =array('fkCustomer');
$my_params = array();
if(!empty($end))
{
$criteria->addCondition('end=:end');
$my_params['end'] = $end;
}
if(!empty($merge->customer_name))
{
$criteria->addCondition('fkCustomer.first_name=:first_name');
$my_params['first_name'] = $merge->customer_name;
}
// other ifs ..
//then
$criteria->limit = 100;
$criteria->params = $my_params;
Also, if I remember correctly, you don't need to write ':end' and ':first_name' in the params array, it will work without the colon.
You also have the following alternative
$criteria=new CDbCriteria;
$criteria->with = 'fkCustomer';
if(!empty($end))
{
$criteria->compare('end', $end);
}
if(!empty($merge->customer_name))
{
$criteria->compare('fkCustomer.first_name', $merge->customer_name);
}
// The following conditions ..
// Limit:
$criteria->limit = 100;
Hi i think this is the same problem i have before with the params variable, for avoid this problem i use the CMap::mergeArray function
this happens because you overwrite the variable each time that the condition passed over it.
This is the syntax for avoid it, its an example
$criteria=new CDBCriteria;
$criteria->addBetweenCondition("Date",$datestart,$dateend);
$criteria->addCondition("Type=:type");
//$criteria->params=array(":type"=>"1"); //This is wrong, overwrites addBetweenCondition params
$criteria->params=CMap::mergeArray($criteria->params,array(
":type"=>"1",
)); //This is ok, mantain all parameters in the params var
$query=Model::findAll($criteria);
можно обратиться напрямую
$criteria=new CDbCriteria;
$criteria->with =array('fkCustomer');
if(!empty($start))
{
$criteria->addCondition('start=:start');
$criteria->params['start']=$start;
}
if(!empty($end))
{
$criteria->addCondition('end=:end');
$criteria->params['end']=$end;
}
if(!empty($merge->customer_name))
{
$criteria->addCondition('fkCustomer.first_name=:first_name');
$criteria->params['first_name']=$merge->customer_name;
}
if(!empty($merge->customer_surname))
{
$criteria->addCondition('fkCustomer.last_name=:last_name');
$criteria->params['last_name']=$merge->customer_surname;
}
if(!empty($merge->customer_email))
{
$criteria->addCondition('fkCustomer.email=:email');
$criteria->params['email']=$merge->customer_email;
}
$criteria->limit = 100;

Resources