why the es5 transpiled code for generator has a while(1) - generator

As with babel repl, if we have es6 code like :
function* test() {
let a = yield 1
let b = yield 2
}
it will be transpiled into
"use strict";
var _marked =
/*#__PURE__*/
regeneratorRuntime.mark(test);
function test() {
var a, b;
return regeneratorRuntime.wrap(function test$(_context) {
while (1) {
switch ((_context.prev = _context.next)) {
case 0:
_context.next = 2;
return 1;
case 2:
a = _context.sent;
_context.next = 5;
return 2;
case 5:
b = _context.sent;
case 6:
case "end":
return _context.stop();
}
}
}, _marked);
}
Just cannot understand what is the purpose of the while (1) in the generated code for
(as in the case section, it uses return and it will just jump out of while(1) and also if while(1) is taking effects, the whole Javascript thread will be occupied and nothing else can be done)

Related

Using redux-saga with setInterval - how and when to yield

Having just moved from thunks to sagas I'm trying to find the best way to call setTimeout and then from within that function call another function (in this case corewar.step()). This was my original code which works as I'd expect.
runner = window.setInterval(() => {
for(let i = 0; i < processRate; i++) {
corewar.step()
}
operations += processRate;
}, 1000/60)
This code is inside a saga and I believe that I should be able to wrap function calls within call as I've done in other areas in the application.
I've tried wrapping the setInterval call in a call and leaving everything else as it is, which results in step() never being called.
runner = yield call(window.setInterval, () => {
for(let i = 0; i < processRate; i++) {
corewar.step()
}
operations += processRate;
}, 1000/60)
I've tried, leaving the setInterval as it is and wrapping the step() function in a call and changing the anonymous function signature to function* which also results in step() never being called.
runner = window.setInterval(function*() {
for(let i = 0; i < processRate; i++) {
yield call([corewar, corewar.step])
}
operations += processRate;
}, 1000/60)
Finally, I've tried wrapping both, which again results in step() never being called.
runner = yield call(window.setInterval, function*() {
for(let i = 0; i < processRate; i++) {
yield call([corewar, corewar.step])
}
operations += processRate;
}, 1000/60)
It feels like I'm missing something here so my question is, should I need to wrap these functions up in call at all or is this wrong?
The follow on question if I am supposed to wrap the outer setInterval in a call would be how should I be defining a function as a parameter to call which also wants to yield either a put or call itself?
There is a section in the saga-redux docs called "Using the eventChannel factory to connect to external events", that suggests using channels.
This section is also providing an example for a setInterval implementation:
import { eventChannel, END } from 'redux-saga'
function countdown(secs) {
return eventChannel(emitter => {
const iv = setInterval(() => {
secs -= 1
if (secs > 0) {
emitter(secs)
} else {
// this causes the channel to close
emitter(END)
}
}, 1000);
// The subscriber must return an unsubscribe function
return () => {
clearInterval(iv)
}
}
)
}
You would then use yield call and yield takeEvery to set it up:
const channel = yield call(countdown, 10);
yield takeEvery(channel, function* (secs) {
// Do your magic..
});
const anotherSaga = function * () {
const runner = yield call(setInterval, () => {
console.log('yes');
}, 1000);
console.log(runner);
}
This works pretty fine for me. In your second snippet there is a double ) at the end where should be only one.
A little late to the party here but this is the top search result for the question of setting a timer in a saga. There's an alternate solution due to the nature of sagas. From here.
I adapted this so:
function* callSelfOnTimer({ value }) {
// Do your work here
...
// If still true call yourself in 2 seconds
if (value) {
yield delay(2000);
yield call(callSelfOnTimer, { value });
}
}
For this to work you also need to add this:
const delay = (ms) => new Promise(res => setTimeout(res, ms))
function* callSelfOnTimer({ value }) {
// Do your work here
...
// If still true call yourself in 2 seconds
if (value) {
yield delay(2000);
yield call(callSelfOnTimer, { value });
}
}

ES6 for of - is iterable evaluated only once?

Consider a simple for of:
for (const elem of document.getElementsByTagName('*') {
// do something with elem
}
does getElementsByTagName evaluated only once or on each iteration ?
thx!
In this case, it's evaluated once to obtain an iterable, which it then uses to obtain an iterator. It reuses that iterator to grab all the values and pass them to your for block. It's very similar to doing the following with a generator function:
function* getIntegers(max) {
for (let i = 0; i <= max; i++) {
yield i;
}
}
const iterator = getIntegers(15);
while (true) {
const { done, value } = iterator.next();
if (done) {
break;
}
console.log(value);
}
As noted by loganfsmyth, generator functions return an iterator directly. Note: generator functions can also be used with the for..of construct.
See this article on MDN for more info.

Reuse sortCompareFunction across DataGridColumns in AS3

I put together a DataGrid using the following functions
public function get dataGridColumns():Array {
var dataGridColumnArray:Array = [];
dataGridColumnArray.push(createDataGridColumn(col1, "field1", 0.17, sortStrings));
dataGridColumnArray.push(createDataGridColumn(col2, "field2", 0.17, sortStrings));
dataGridColumnArray.push(createDataGridColumn(col3, "field3", 0.17, sortStrings));
return dataGridColumnArray;
}
private static function createDataGridColumn(columnName:String, dataField:String, width:Number,sortCompareFunction:Function = null):DataGridColumn {
var column:DataGridColumn = new DataGridColumn(columnName);
column.dataField = dataField;
column.width = width;
column.sortCompareFunction = sortCompareFunction;
return column
}
private function sortStrings(a:Object, b:Object, fields:Array = null):int{
if(a.toString().toLowerCase() > b.toString().toLowerCase()){
return 1;
}else if(a.toString().toLowerCase() < b.toString().toLowerCase()){
return -1;
}else{
return 0;
}
}
Since my dataProvider is an array of data objects I run into the problem that a and b in the sort function are objects, and I want to somehow pass the data field (column title) into the sort so I can check the correct object property all using one sort function so it would look something like:
private function sortStrings(a:Object, b:Object, field:String):int{
if(a.field.toLowerCase() > b.field.toString().toLowerCase()){
return 1;
}else if(a.field.toString().toLowerCase() < b.field.toString().toLowerCase()){
return -1;
}else{
return 0;
}
}
I have not figured out a good way to do this yet, and worst case scenario I have 6 extremely similar sort functions.
you can make a comparators factory function, which will return closures, enclosing the field name:
private function createComparatorFor(field:String):Function {
return function (a:Object, b:Object, fields:Array = null):int{
const aField:String = a[field].toString().toLowerCase();
const bField:String = b[field].toString().toLowerCase();
if (aField > bField) {
return 1;
}
if (aField < bField) {
return -1;
}
return 0;
}
}
and use it like this:
dataGridColumnArray.push(createDataGridColumn(
col1, "field1", 0.17, createComparatorFor("field1")));
untested, but should work

How to obtain arguments.callee.caller?

I am trying to find out the name of the function that called my Google Apps Script function, by using arguments.callee.caller as in How do you find out the caller function in JavaScript?, but it seems there's no such property exported. (However, arguments.callee exists.)
How can I get that calling function's name in Google Apps Script?
As a secondary question, why isn't arguments.callee.caller there?
I made this function:
function getCaller()
{
var stack;
var ret = "";
try
{
throw new Error("Whoops!");
}
catch (e)
{
stack = e.stack;
}
finally
{
var matchArr = stack.match(/\(.*\)/g);
if (matchArr.length > 2)
{
tmp = matchArr[2];
ret = tmp.slice(1, tmp.length - 1) + "()";
}
return ret;
}
}
It throws as Error() and then gets the function name from the stack trace.
Try vary the '2' in matchArr[2] when using wrappers.
caller is a non-standard extension to JavaScript (that is, many browsers have it but it's not part of the EcmaScript standard) and not implemented in Apps Script.
I made a function to get the call stack based on jgrotius's answer:
function getCallStack()
{
var returnValue = "";
var framePattern = /\sat (.+?):(\d+) \((.+?)\)/;
try
{
throw new Error('');
}
catch (e)
{
returnValue = e.stack
.split('\n')
.filter(function(frame, index) {
return !frame.isBlank() && index > 0;
})
// at app/lib/debug:21 (getCaller)
.map(function(frame) {
var parts = frame.match(framePattern);
return {
file: parts[1],
line: parseInt(parts[2]),
func: parts[3]
};
});
}
return returnValue;
}
This is my updated version of the other two proposed solutions:
const getStacktrace = () => {
try {
throw new Error('')
} catch (exception) {
// example: at getStacktrace (helper:6:11)
const regex = /\sat (.+?) \((.+?):(\d+):(\d+)\)/
return exception
.stack
.split('\n')
.slice(1, -1)
.filter((frame, index) => {
return frame && index > 0
})
.map((frame) => {
const parts = frame.match(regex)
return {
function: parts[1],
file: parts[2],
line: parseInt(parts[3]),
column: parseInt(parts[4])
}
})
}
}
P.S.: please not that the regex has changed and also we are ignoring the first element of the stacktrace, since it is the getStacktrace function itself.

How can you overload a function in ActionScript?

I want a function to be able to take in various types. AS3 doesn't support overloading directly... so I can't do the following:
//THIS ISN'T SUPPORTED BY AS3
function someFunction(xx:int, yy:int, someBoolean:Boolean = true){
//blah blah blah
}
function someFunction(arr:Array, someBoolean:Boolean = true){
someFunction(arr[0], arr[1], someBoolean);
}
How can I work around it and still have a function that is able to take arguments of various types?
If you just want to be able to accept any type, you can use * to allow any type:
function someFunction( xx:*, yy:*, flag:Boolean = true )
{
if (xx is Number) {
...do stuff...
} else if (xx is String) {
...do stuff...
} else {
...do stuff...
}
}
If you have a large number of various parameters where order is unimportant, use an options object:
function someFunction( options:Object )
{
if (options.foo) doFoo();
if (options.bar) doBar();
baz = options.baz || 15;
...etc...
}
If you have a variable number of parameters, you can use the ... (rest) parameter:
function someFunction( ... args)
{
switch (args.length)
{
case 2:
arr = args[0];
someBool = args[1];
xx = arr[0];
yy = arr[1];
break;
case 3:
xx = args[0];
yy = args[1];
someBool = args[2];
break;
default:
throw ...whatever...
}
...do more stuff...
}
For cases where you need to call a common function to a number of classes, you should specify the interface common to each class:
function foo( bar:IBazable, flag:Boolean )
{
...do stuff...
baz = bar.baz()
...do more stuff...
}
Could just have:
function something(...args):void
{
trace(args[0], args[1]);
}
This way you can easily loop through your arguments and such too (and even check the argument type):
function something(...args):void
{
for each(var i:Object in args)
{
trace(typeof(i) + ": " + i);
}
}
something("hello", 4, new Sprite()); // string: hello
// number: 4
// object: [object Sprite]