I can't wrap my head around callback syntax, can you please help me re-write my code so that it executes in this order:
MenuBuilder.load()
MenuBuilder.draw()
Translator.load()
(in my case it executes in this order MenuBuilder.load(), Translator.load(), MenuBuilder.draw() so it doesn't do what I want)
onload.js
import MenuBuilder from "./menu-builder.js";
import Translator from "./translator.js";
var menuBuilder = new MenuBuilder();
var translator = new Translator();
menuBuilder.load();
translator.load();
menu-builder.js
"use strict"
class MenuBuilder {
constructor() {
this._nav = document.getElementsByTagName("nav")[0];
this._url = window.location.href;
}
load() {
console.log("MenuBuilder.load() start");
fetch(`/json/menu.json`)
.then((res) => res.json())
.then((jsonMenu) => {
this.draw(jsonMenu);
})
/*.catch(() => {
console.error(`Could not load ${this._lang}.json.`);
});*/
console.log("MenuBuilder.load() end");
}
draw(jsonMenu) {
console.log("MenuBuilder.draw(jsonMenu) start");
var htmlMenu = `<div id="siteTitleDiv"><p id="siteTitle" data-i18n="general.title"></p><p id="siteTitleShadow" data-i18n="general.title-shadow"></p><p id="siteSubtitle"data-i18n="general.subtitle"></p></div><ul>`;
for(var i = 0; i < jsonMenu.length; i++) {
var menuItem = jsonMenu[i];
var regexp = /http:\/\/cypher-f\.com\/(([a-z\-]*\/)?([a-z\-]*\/))?/g;
var fullPage = "something format_abc";
var match = regexp.exec(this._url);
var level_1 = match[1];
var level_2 = match[3];
var parent = match[2];
var full_suffix = match[0];
if ((parent == null) || (menuItem.parent === parent)) {
var material_icon = menuItem["material-icon"];
var href = menuItem["href"];
var i18n = menuItem["data-i18n"];
htmlMenu += `<li><i class="material-icons">${material_icon}</i></li>`;
}
}
htmlMenu += `</ul>`;
this._nav.innerHTML = htmlMenu;
console.log("MenuBuilder: nav.innerHTML");
console.log(this._nav.innerHTML);
console.log("MenuBuilder: document.elements");
console.log(document.querySelectorAll("[data-i18n]"));
console.log("MenuBuilder.draw(jsonMenu) end");
}
}
export default MenuBuilder;
translator.js
"use strict"
class Translator {
constructor() {
this._lang = this.getLanguage();
this._elements = document.querySelectorAll("[data-i18n]");
}
getLanguage() {
var lang = navigator.languages ? navigator.languages[0] : navigator.language;
return lang.substr(0, 2);
}
load(lang = null) {
console.log("Translator.load() start");
console.log("this._elements");
console.log(this._elements);
if (lang) {
this._lang = lang;
}
else {
var re = new RegExp("lang=([^;]+)");
var value = re.exec(document.cookie);
var cookieLang = (value != null) ? unescape(value[1]) : null;
if (cookieLang) {
this._lang = cookieLang;
}
}
fetch(`/i18n/${this._lang}.json`)
.then((res) => res.json())
.then((translation) => {
this.translate(translation);
})
.then(this.toggleLangTag())
.then(document.cookie = `lang=${this._lang};path=/`)
/*.catch(() => {
console.error(`Could not load ${this._lang}.json.`);
});*/
console.log("Translator.load() end");
}
translate(translation) {
console.log("Translator.load(translation) start");
this._elements.forEach((element) => {
var keys = element.dataset.i18n.split(".");
var text = keys.reduce((obj, i) => obj[i], translation);
if (text) {
element.innerHTML = text;
}
else {
element.innerHTML = `key ${keys} not found for ${this._lang}!`
}
});
console.log("Translator.load(translation) end");
}
toggleLangTag() {
if (document.documentElement.lang !== this._lang) {
document.documentElement.lang = this._lang;
}
}
switchLanguage(translator) {
var availableLang = ["en", "fr"];
var currentLangIndex = availableLang.indexOf(translator._lang);
var nextLang = availableLang[(currentLangIndex + 1)%availableLang.length];
translator.load(nextLang);
}
}
export default Translator;
I'm sorry I know this is kind of a newbie question but I haven't programmed in three years.
You're working with Promises here, so you want to stick with that paradigm. Return the promise that is returned from the fetch call, then "chain" off of that promise to call the translator.
load() {
console.log("MenuBuilder.load() start");
// The return here gives control of the promise to the caller...
return fetch(`/json/menu.json`)
.then((res) => res.json())
.then((jsonMenu) => {
this.draw(jsonMenu);
})
/*.catch(() => {
console.error(`Could not load ${this._lang}.json.`);
});*/
console.log("MenuBuilder.load() end");
}
So back in onload.js you can use the promise returned from menuBuilder.load() to call translator.load() after menuBuilder.load() is done.
import MenuBuilder from "./menu-builder.js";
import Translator from "./translator.js";
var menuBuilder = new MenuBuilder();
var translator = new Translator();
menuBuilder.load().then(() => translator.load());
Related
I refer to this doc
https://nodejs.org/api/stream.html#simplified-construction
The _construct() method is never called in my version. Why?
Note: In simplified construction you provide the implementation of _construct() method in the constructor options {construct() {...}, ...}
This is a working class version of a readable stream counter example:
const { Readable } = require('stream');
class Counter extends Readable {
constructor(options) {
super(options);
this._max = 10;
this._index = 1;
}
_read() {
const i = this._index++;
if (i > this._max) {
this.push(null);
} else {
const str = String(i);
const buf = Buffer.from(str, 'utf-8');
this.push(buf);
}
}
}
const readable = new Counter();
readable.on('readable', function() {
console.log('readable');
let data;
while ((data = this.read()) !== null) {
console.log(String(data));
}
});
readable.on('close', function() {
console.log('close');
});
This is my not working simplified construction version:
const { Readable } = require('stream');
const counter = new Readable({
construct() {
this._max = 10;
this._index = 1;
console.log(this._max); // This is never executed
},
read() {
console.log(this._max); // This is undefined
this.push(null);
}
// read() {
// const i = this._index++;
// if (i > this._max) {
// this.push(null);
// } else {
// const str = String(i);
// const buf = Buffer.from(str, 'utf-8');
// this.push(buf);
// }
// }
});
counter.on('readable', function() {
let data;
while ((data = this.read()) !== null) {
console.log(String(data));
}
});
counter.on('close', function() {
console.log('close');
});
The docs say:
const { Writable } = require('node:stream');
const myWritable = new Writable({
construct(callback) {
// Initialize state and load resources...
},
write(chunk, encoding, callback) {
// ...
},
destroy() {
// Free resources...
}
});
Now I'm confused. Thought initialization code belongs into the _construct() method?
As stated in my comment above, _construct()is added in Node Version 15.x. For further reference, follows the working example:
const { Readable } = require('stream');
const counter = new Readable({
construct(callback) {
this._max = 10;
this._index = 1;
callback(); // Signal completion of initialization
},
read() {
const i = this._index++;
if (i > this._max) {
this.push(null);
} else {
const str = String(i);
const buf = Buffer.from(str, 'utf-8');
this.push(buf);
}
}
});
counter.on('readable', function() {
let data;
while ((data = this.read()) !== null) {
console.log(String(data));
}
});
counter.on('close', function() {
console.log('close');
});
I am working on an alert-similar function in javascript, and I got in trouble.
The code of the first function (prepBkCrt) looks like this:
function prepBkCrt() {
event.preventDefault();
crtBkMnl = true;
crtBkCnl = false;
const url = crtBkTab.querySelector("#url");
const name = crtBkTab.querySelector("#name");
if (name.value.length >= 17) {
poper("Bookmark","I will pass this part",true).then((response) => {
if (response === false) {
crtBkCnl = true;
hideBkTab();
return;
}
});
}
addBookmark(url.value,name.value);
if (crtBkCnl === false) {
poper("Bookmark","Bookmark successfully created!",false);
}
url.value = "";
name.value = "";
}
and the second function looks like this:
function poper(headerTxt, descTxt, option) {
return new Promise((resolve, reject) => {
const poper = document.querySelector("#poper");
const header = poper.querySelector("h2");
const buttonDiv = poper.querySelector("div");
const desc = poper.querySelector("span");
header.innerText = headerTxt;
desc.innerHTML = descTxt;
const yBtn = buttonDiv.querySelectorAll("button")[0];
yBtn.addEventListener("click", (event) => {poper.style.transform = "translateY(-300px)"; resolve(true);});
const nBtn = buttonDiv.querySelectorAll("button")[1];
nBtn.addEventListener("click", (event) => {poper.style.transform = "translateY(-300px)"; resolve(false);});
if (option === false) {
nBtn.style.display = "none";
} else {
nBtn.style.display = "block";
}
poper.style.transform = "none";
setTimeout(() => {poper.style.transform = "translateY(-300px)"; resolve(false);},10000);
});
}
This used to work well on other code, but it seems to not work on this javascript file. I've checked that it does run the first poper function in prepBkCrt function, which is the problem. The expected behavior is if the name.value's length is over 17, it should run poper function (it should work like this, and run this one, but the code only runs the second image. What's the problem?
You seem to be looking for an else statement. The return inside the .then() callback does not break from prepBkCrt.
function prepBkCrt() {
event.preventDefault();
crtBkMnl = true;
crtBkCnl = false;
const url = crtBkTab.querySelector("#url");
const name = crtBkTab.querySelector("#name");
if (name.value.length >= 17) {
poper("Bookmark","I will pass this part",true).then(response => {
if (response === false) {
crtBkCnl = true;
hideBkTab();
}
});
} else {
addBookmark(url.value,name.value);
poper("Bookmark","Bookmark successfully created!",false);
url.value = "";
name.value = "";
}
}
oddsData is undefined when i want to run the code below.
getOdds = async(data) => {
var receivedData = "";
send({"Command":"GetMatchMarkets","Params":data});
var message = JSON.stringify({"Command":"GetMatchMarkets","Params":data});
var length = Buffer.byteLength(message),
buffer = new Buffer(4 + Buffer.byteLength(message));
buffer.writeUInt32LE(length, 0);
buffer.write(message, 4);
client.write(buffer);
var bytesToReceive = length;
var oddsData = "";
client.on('data', async(buf) => {
function calc(){
var offset = 0;
if (bytesToReceive === 0) {
if(buf.length < 4){ return; }
bytesToReceive = buf.readUInt32LE(0);
offset = 4;
}
var currentCommandBytes = Math.min(bytesToReceive, buf.length - offset);
receivedData += buf.slice(offset, offset + currentCommandBytes);
bytesToReceive -= currentCommandBytes;
if (bytesToReceive === 0) {
bytesToReceive = 0;
if (receivedData != ""){
oddsData += receivedData;
}
receivedData = "";
}
if (currentCommandBytes < buf.length - offset) {
calc(buf.slice(currentCommandBytes+offset))
}
}
await calc();
});
console.log(oddsData);
}
return ReferenceError: oddsData is not defined.
oddsData is undefined when i want to run the code below.
Assuming you used oddsData in in your ...OTHER CODES..., maybe the error is due to those nesting of functions,
Declare functions as variables and then pass it to your code.
function test(){
var calc = function (){
...OTHER CODES....
receivedData = "";
return oddsdata
}
var asFun = async (buf) => {
await calc();
}
var oddsData = "";
client.on('data', asFun);
console.log(oddsData);
}
[updated to match changes in question's code]
Stripping out all the code that doesn't affect oddsData leaves the following structure:
const getOdds = async () => {
var oddsData = '';
client.on('data', async () => {
function calc () {
oddsData += 'something';
}
await calc();
});
console.log(oddsData);
};
I see no indication that oddsData should be undefined.
A little test program to explore what's happening:
const getOdds = async() => {
result.innerHTML += 'enter getOdds async()\n';
var oddsData = "";
client.addEventListener('data', async() => {
result.innerHTML += `client.on data async() oddsData: '${oddsData}'\n`;
function calc() {
oddsData += "calc!";
result.innerHTML += `calc() oddsData: '${oddsData}'\n`;
}
await calc();
});
result.innerHTML += `getOdds async() oddsData: '${oddsData}'\n`;
};
window.onload = () => {
result.innerHTML = 'window.onload calling getOdds()\n';
getOdds();
}
<p id="client">This is client.
<button onclick="emitData()">Emit 'data' to client</button>
</p>
<p>Results:</p>
<pre id="result"></pre>
<script>
const client = document.getElementById('client');
const result = document.getElementById('result');
function emitData() {
let event = new Event('data');
client.dispatchEvent(event);
}
</script>
I have many different controllers throughout this project and all of them are declared the same way. This one now isn't getting called/giving an error and I have no clue why. I've looked through it and it all looks right to me.
I think it's probably some syntax error I'm not seeing. If its something else please tell me. I'm trying to learn angular and everything helps. Also if you need anything else just tell me.
I've made sure its not that the app.js name got changed and been looking for missing syntax but can't find anything.
https://docs.angularjs.org/error/ng/areq?p0=companyDepartmentController&p1=not%20a%20function,%20got%20undefined
company-department-controller.js
app.controller('companyDepartmentController', ['$scope', '$timeout', 'companyService', function ($scope, $timeout, companyService) {
/**
* Create/Manage Company Departments & Shifts
*
*/
// INITIALIZE VARIABLES *********************************************************************************
var vm = this;
vm.Departments = [];
vm.activeDepartment = {}
vm.departmentBeforeEdit = {};
vm.activeShift = {};
vm.OffsetString = "";
vm.SaveDepartmentSuccessMessage = null;
vm.SaveDepartmentErrorMessage = null;
// STARTUP **********************************************************************************************
(vm.GetDepartments = function () {
companyService.GetDepartmentsWithShiftInformation().success(function (data) {
console.log('hi');
for (i = 0; i < data.length; i++) {
console.log(data[i])
}
vm.Departments = data;
// for now we are limiting this to 1
vm.activeDepartment = vm.Departments[0];
vm.setTimeZoneOffsets(vm.activeDepartment);
});
})();
// move to global location? handle this better?
(vm.findLocalOffsetString = function () {
console.log('hi1');
vm.OffsetString = moment(new Date()).format('ZZ');
})();
// $BROADCAST/$ON EVENTS ********************************************************************************
// EVENTS ***********************************************************************************************
vm.saveDepartment = function (department) {
// new
if (department.DepartmentID === 0 || typeof department.DepartmentID === 'undefined') {
}
// update
else {
companyService.UpdateDepartmentHeader(department).success(function (data) {
vm.SaveDepartmentSuccessMessage = "Saved!";
resetDepartmentMessage();
department.InEdit = false
});
}
};
vm.editDepartment = function (department) {
vm.activeDepartment = department;
vm.departmentBeforeEdit = angular.copy(vm.activeDepartment);
vm.activeDepartment.InEdit = true;
};
vm.cancelDepartmentEdit = function (department) {
for (var i = 0; i < vm.Departments.length; i++) {
if (department.DepartmentID === vm.Departments[i].DepartmentID) {
vm.Departments[i] = vm.departmentBeforeEdit;
vm.departmentBeforeEdit = {};
vm.activeDepartment = vm.Departments[i];
break;
};
};
};
vm.addShift = function () {
if (!vm.activeDepartment) return;
vm.activeShift = {
DepartmentID: vm.activeDepartment.DepartmentID,
StartTime: new Date(),
LocalStartTime: new Date(new Date() + vm.OffsetString)
};
vm.activeShift.StartTime.setSeconds(0);
vm.activeShift.LocalStartTime.setSeconds(0);
};
vm.deleteShift = function (shift) {
if (!shift) return;
if (confirm("Are you sure you want to delete the shift: " + shift.Name + "?")) {
companyService.DeleteShift(shift).success(function () {
angular.forEach(vm.activeDepartment.Shifts, function (c, i) {
if (c.ShiftID === shift.ShiftID) {
vm.activeDepartment.Shifts.splice(i, 1);
};
});
});
};
};
vm.setTimeZoneOffsets = function (department) {
if (!department || !department.Shifts || department.Shifts.length === 0) return;
for (var i = 0; i < department.Shifts.length; i++) {
department.Shifts[i].LocalStartTime = new Date(department.Shifts[i].StartTime + vm.OffsetString);
department.Shifts[i].EndTime = moment(department.Shifts[i].StartTime).add(department.Shifts[i].Duration, 'hours').toDate()
};
};
var fixTimezoneOnSave = function (shift) {
shift.StartTime = new Date(shift.LocalStartTime).toLocaleString();
};
vm.setActiveShift = function (shift) {
if (!shift) return;
vm.activeShift = angular.copy(shift);
};
vm.saveShift = function (shift) {
fixTimezoneOnSave(shift);
// new shift
if (shift.ShiftID === 0 || typeof shift.ShiftID === 'undefined') {
companyService.AddShift(shift).success(function (data) {
shift.ShiftID = data;
vm.SaveDepartmentSuccessMessage = "Saved!";
resetDepartmentMessage();
getUpdatedShiftsAndInfo();
}).error(function (e) {
vm.SaveDepartmentErrorMessage = e.error;
resetDepartmentMessage();
});
}
// updating existing
else {
companyService.UpdateShift(shift).success(function (data) {
vm.SaveDepartmentSuccessMessage = "Saved!";
resetDepartmentMessage();
getUpdatedShiftsAndInfo();
}).error(function (e) {
vm.SaveDepartmentErrorMessage = e.error;
resetDepartmentMessage();
});
}
}
var getUpdatedShiftsAndInfo = function () {
companyService.DepartmentAndShiftInformation(vm.activeDepartment.DepartmentID).success(function (data) {
vm.activeDepartment.DepartmentShiftInformation = data.DepartmentShiftInformation;
vm.activeDepartment.Shifts = data.Shifts;
vm.setTimeZoneOffsets(vm.activeDepartment);
});
};
var resetDepartmentMessage = function () {
// clear error/success message if they have values still
if (vm.SaveDepartmentSuccessMessage != null) {
$timeout(function () { vm.SaveDepartmentSuccessMessage = null; }, 2000);
}
if (vm.SaveDepartmentErrorMessage != null) {
$timeout(function () { vm.SaveDepartmentErrorMessage = null; }, 2000);
}
};
// create controller object in console if we have logging turned on
if (spectrum.LoggingEnabled) {
spectrum.logController(vm);
};
}]);
_CompanyDepartment.cshtml
<div class="container-fluid" data-ng-controller="companyDepartmentController as cd">
</div>
#section scripts {
#Scripts.Render("~/bundles/companyDepartments")
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/angularjs")
}
app.js
var app = angular.module('app', ['angularFileUpload', 'ngSanitize', 'ui.mask', 'ui.select', 'ui.bootstrap', 'ui.bootstrap.tpls', 'angular.filter', 'smart-table', 'colorpicker.module'])
.config(function ($httpProvider) {
//make delete type json to facilitate passing object
//to our generic method.
$httpProvider.defaults.headers["delete"] = {
'Content-Type': 'application/json;charset=utf-8'
};
});
Outside of a naming issue with the controller(which I can't see), I would imagine your issue dealing with the company-department-controller.js not being loaded.
In setting up your angular project, I would suggest that you follow this angular styleguide. It has been very helpful to me in creating a well structured project.
Suppose I have following piece of code that contains an infinite loop:
function infiniteLoop() {
while(true) {
//do something, eg.
document.getElementById("someID").innerHTML = "Blah";
}
}
If we execute this code in an online compiler, browser will crash. I want to prevent that from happening. So I tried following code following this answer:
function willNotCrash() {
myVar = setInterval(infiniteLoop, 5000);
setTimeout(function(){
clearInterval(myVar);
}, 4000);
}
This code doesn't make the browser to crash, because I am stopping the execution before infiniteLoop() gets called by clearInterval(myVar).
My question is how do I stop executing such functions if they don't response within some period of time (eg. after 5 seconds or before the browser is crashed).
For example, if we copy paste following java code in https://www.compilejava.net/
public class HelloWorld {
public static void main(String[] args) {
while(true) {
System.out.println("Blah");
}
}
}
we get a nice output saying,
Script was taking longer than 5 seconds to execute so it was killed.
Here is my current code: http://js.do/code/106546
This is a bit tricky but perfectly doable. You need to tokenize the script and then rebuild it but insert a counter increment in every loop and function call. If the counter goes above some threshold, then bomb out. I did it here: https://littleminigames.com/
You can see the source at https://bitbucket.org/cskilbeck/littleminigames/src
The interesting bits are in wrapper.js (https://bitbucket.org/cskilbeck/littleminigames/src/ac29d0d0787abe93c75b88520050a6792c04d34d/public_html/static/js/wrapper.js?at=master&fileviewer=file-view-default)
Google escodegen, estraverse and esprima
I relied heavily on this: https://github.com/CodeCosmos/codecosmos/blob/master/www/js/sandbox.js
wrapper.js, as requested:
// Don't obfuscate this file! We depend on the toString() of functions!
// this was all nicked from https://github.com/CodeCosmos/codecosmos/blob/master/www/js/sandbox.js
(function(mainApp) {
'use strict';
var esprima = window.esprima,
estraverse = window.estraverse,
escodegen = window.escodegen,
errors = [],
eng,
Syntax = estraverse.Syntax;
// This implements the jankiest possible "source map", where we keep an array
// of [generatedLine, knownSourceLine]. Seems to essentially work.
function SourceNode(line, col, _sourceMap, generated) {
this.line = line;
this.col = col;
this.generated = generated;
}
SourceNode.prototype.toStringWithSourceMap = function toStringWithSourceMap() {
var code = [];
var mapLines = {};
var map = [];
// assumes that wrapCode adds two lines
var line = 3;
var lastMapLine = null;
function walk(node) {
if (typeof(node) === "string") {
if (node) {
code.push(node);
var matches = node.match(/\n/g);
if (matches !== null) {
line += matches.length;
}
}
} else if (node instanceof SourceNode) {
if (node.line !== null) {
if (!mapLines[line]) {
map.push([line, node.line]);
mapLines[line] = node.line;
}
}
walk(node.generated);
} else {
node.forEach(walk);
}
}
walk(this);
return {
code: code.join(''),
map: map
};
};
SourceNode.prototype.toString = function toString() {
return this.toStringWithSourceMap().code;
};
// This is used by escodegen
window.sourceMap = {
SourceNode: SourceNode
};
// TODO (chs): add in all the things that need to be masked
function runWrapper($userCode, __sys) {
var clear = __sys.clear,
setpixel = __sys.setpixel,
rectangle = __sys.rectangle,
box = __sys.box,
line = __sys.line,
getpixel = __sys.getpixel,
getpixeli = __sys.getpixeli,
keypress = __sys.keypress,
keyrelease = __sys.keyrelease,
keyheld = __sys.keyheld,
reset = __sys.reset;
__sys.userFunction = __sys.catchErrors($userCode);
}
function extractCode(fn) {
var code = fn.toString();
return code.substring(code.indexOf('{') + 1, code.lastIndexOf('}'));
}
function makeOneLine(code) {
return code.replace(/(\/\/[^\n]+|\n\s|\r\n\s*)/g, '');
}
var runTemplate = makeOneLine(extractCode(runWrapper));
function wrapCode(code, template, functionName, postCode) {
// avoid interpretation of the replacement string by using a fun.
// otherwise mo' $ mo problems.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_string_as_a_parameter
return ("'use strict';" + template.replace(/\$userCode/, function() {
return 'function ' + functionName + '() {\n' + code + postCode + '\n}';
}));
}
var injectStatement = esprima.parse("if (++__sys.ctr >= __sys.maxctr) throw new Error('Script halted - infinite loop?');").body[0];
var injectElseStatement = esprima.parse("if (++__sys.ctr >= __sys.maxctr) throw new Error('Script halted - infinite loop?'); else ;").body[0];
function CallExpression(callee, args) {
this.callee = callee;
this.arguments = args;
}
CallExpression.prototype.type = Syntax.CallExpression;
function Identifier(name) {
this.name = name;
}
Identifier.prototype.type = Syntax.Identifier;
function BlockStatement(body) {
this.body = body;
}
BlockStatement.prototype.type = Syntax.BlockStatement;
function ReturnStatement(argument) {
this.argument = argument;
}
ReturnStatement.prototype.type = Syntax.ReturnStatement;
function FunctionExpression(id, params, body) {
this.id = id;
this.params = params;
this.body = body;
this.defaults = [];
this.expression = false;
this.generator = false;
this.rest = null;
}
FunctionExpression.prototype.type = Syntax.FunctionExpression;
function wrapId(node, defaultName) {
if (node.loc) {
var id = (node.id || {
name: null,
loc: null
});
var loc = id.loc || node.loc;
var name = id.name || defaultName;
return new Identifier(name + '$' + loc.start.line);
} else {
return node.id;
}
}
function instrumentAST(ast) {
var identifierStack = [];
function pushIdentifier(s) {
identifierStack[identifierStack.length - 1].push(s);
}
function popIdentifierStack() {
identifierStack.pop();
}
function pushIdentifierStack() {
identifierStack.push([]);
}
function peekLastIdentifier() {
var lastStackIdx = identifierStack.length - 1;
if (lastStackIdx >= 0) {
var stack = identifierStack[lastStackIdx];
if (stack.length) {
return stack[stack.length - 1];
}
}
return '';
}
pushIdentifierStack();
return estraverse.replace(ast, {
enter: function enterAST(node) {
switch (node.type) {
case Syntax.VariableDeclarator:
if (node.id.type === Syntax.Identifier) {
pushIdentifier(node.id.name);
}
break;
case Syntax.MemberExpression:
if (node.object.type === Syntax.Identifier) {
var id = node.object.name;
if (node.property.type === Syntax.Identifier) {
id += '__dot__' + node.property.name; // huh? why mangle these?
// console.log(id);
}
pushIdentifier(id);
} else if (node.property.type === Syntax.Identifier) {
pushIdentifier(node.property.name);
}
break;
case Syntax.FunctionDeclaration:
pushIdentifierStack();
break;
case Syntax.FunctionExpression:
pushIdentifierStack();
break;
default:
break;
}
return node;
},
leave: function leaveAST(node) {
switch (node.type) {
case Syntax.DoWhileStatement:
break;
case Syntax.ForStatement:
break;
case Syntax.FunctionDeclaration:
break;
case Syntax.FunctionExpression:
break;
case Syntax.WhileStatement:
break;
default:
return estraverse.SKIP;
}
// modify the BlockStatement in-place to inject the instruction counter
if(node.body.body === undefined) {
// they have used a non-block statement as the body of a function or loop construct
// not allowed for function declarations - should never get here
if(node.type === Syntax.FunctionDeclaration) {
errors.push({
message: "Missing {",
line: node.loc.start.line,
column: node.loc.start.column
});
}
else {
// otherwise insert the test
var newBody = angular.copy(injectElseStatement);
newBody.alternate = node.body;
node.body = newBody;
}
return estraverse.SKIP;
}
node.body.body.unshift(injectStatement);
if (node.type === Syntax.FunctionExpression) {
popIdentifierStack();
// __catchErrors(node)
node.id = wrapId(node, peekLastIdentifier());
return new CallExpression(
new Identifier("__sys.catchErrors"), [node]);
}
if (node.type === Syntax.FunctionDeclaration) {
popIdentifierStack();
// modify the BlockStatement in-place to be
// return __catchErrors(function id() { body });
var funBody = node.body;
node.body = new BlockStatement([
new ReturnStatement(
new CallExpression(
new CallExpression(
new Identifier("__sys.catchErrors"), [new FunctionExpression(
wrapId(node, peekLastIdentifier()), [],
funBody)]), []))
]);
}
return node;
}
});
}
// mainApp.sandbox('var a = 1; function update(frame) { clear(0); }').code
// give it the source code as a string
mainApp.sandbox = function(code) {
var rc = {};
this.errors = [];
try {
this.ast = instrumentAST(esprima.parse(code, { range: true, loc: true }));
this.map = escodegen.generate(this.ast, { sourceMap: true, sourceMapWithCode: true });
this.code = wrapCode(this.map.code, runTemplate, '', ';\n__sys.updateFunction = (typeof update === "function") ? update : null;');
}
catch(e) {
this.errors.push({
message: e.description,
line: e.lineNumber,
column: e.column
});
}
if(this.code) {
this.code = "eng.clientFunction = function(__sys) {" + this.code + "};";
}
};
mainApp.sandbox.prototype.searchMap = function(needle) {
// binary search
var lo = 0;
var hi = this.map.map.length;
var mid, here;
while (true) {
mid = lo + ((hi - lo) >> 1);
here = this.map.map[mid];
if (mid === lo || here[0] === needle) {
return here[1];
} else if (here[0] > needle) {
hi = mid;
} else {
lo = mid;
}
}
};
})(mainApp);
Typically all JavaScript runs in one thread, so it is impossible to run any JavaScript that could stop your loop while your loop is running. Using HTML5 web workers, you can run the infinite loop in a separate thread, and then you can terminate it:
var myWorker = new Worker( '/infinite.js ');
setTimeout( function ( ) {
myWorker.terminate( );
}, 5000 );
However your web worker won't have access to the DOM, so the contents of your infinite loop would need to be different that what you have in your question.
I found exactly what I was looking for in Bergi's comment,
Alternatively, place a if (Date.now() > dateAtStartOfExecution+5000) return; in every loop body.
So now my code looks like:
function infiniteLoop() {
dateAtStartOfExecution = Date.now();
while(true) {
//do something
document.getElementById("someID").innerHTML = "Blah";
if (Date.now() > dateAtStartOfExecution+5000) {
alert("Taking too much time. Killing.");
return;
}
}
}
If I run this code after 5 seconds I will get an alert and the execution will stop. Try this:
http://js.do/code/106565