XMLHttpRequest() Inside RequireJS Module - javascript

all. I'm working on a browser-based RPG for fun as well as programming education. I recently learned RequireJS—which is amazing—but during the revision of my code to AMD, I ran into an application breaking snag.
In my original implementation, I used an XMLHttpRequest() request to get the player's selected hero from my server via a GET call to a GetHero.php file. Asynchronous calls are great, but I struggling with the timing of the callback function and continuation of the game—i.e. the game continues before the callback, which results in an error.
My questions are:
1) How can I get the game to wait for the XMLHttpRequest() to return a value before moving to the next block of code?
2) When in my RequireJS AMD should I execute this function?
I tried turning off asynchronous in the .open() function, but since RequireJS uses similar calls, I don't think it works as it would in the synchronous execution of my code. I've also omitted the majority of the code to isolate the specific issue and heighten readability.
Thanks in advance to anyone willing to help me understand this!
main.js
require.config({
baseUrl: "assets/js",
deps: ["game"],
paths: {
jquery: "lib/jquery-3.3.1.min",
bootstrap: "lib/bootstrap.min",
game: "game",
hero: "models/hero",
getHero: "models/getHero",
stats: "models/stats",
heroClasses: "models/heroClasses"
},
shim: {
bootstrap: ["jquery"]
}
});
game.js
define(["hero", "bootstrap"], function (h) {
Hero = h;
console.log(Hero);
console.log(Hero.Stats.Get("str"));
console.log(Hero.Stats.GetInfo()); // <==== this fails
});
hero.js
define(["stats"], function (s) {
Stats = s;
return {
Stats: Stats
};
});
stats.js
define(["getHero"], function(myHero) {
var Info = myHero; // returns hero obj "{ name: "Hero", level: 1, etc..}"
var _HeroClasses = heroClasses;
var _GetInfo = function (arr) {
return Info;
};
return {
GetInfo: _GetInfo
};
});
getHero.js
define([], function () {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var myObj = JSON.parse(this.responseText);
return myObj;
}
};
xmlhttp.open("GET", "/game/assets/php/GetHero.php", false);
xmlhttp.send();
});
GetHero.php
<?php
// TODO create MYSQL call to load hero
echo '{ "id": 0, "name": "Ryan", "classId": 0, "level": 50, "exp": 251 }';
?>
Updated Code: (Working, but likely an ugly/poor solution)
-- game.js (Updated)
var a = new Promise(function (resolve, reject) {
document.getElementById("message").innerHTML = "Loading...";
Hero = h;
resolve(true);
});
a.then(function() {
Hero.Stats.GetInfo();
document.getElementById("message").innerHTML = "Ready";
})
.catch(function(err) {
console.error('There was an error 2!', err.statusText);
});
-- getHero.js (Updated)
define([], function () {
return function (method, url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = function () {
if (this.status >= 200 && this.status < 300) {
resolve(xhr.response);
} else {
reject({
status: this.status,
statusText: xhr.statusText
});
}
};
xhr.onerror = function () {
reject({
status: this.status,
statusText: xhr.statusText
});
};
xhr.send();
});
};
});

Related

How to fetch data from XMLHttpRequest to GridView QML

I am trying to show movies in a grid with 5 movies per row. I use a predefined movie list. I successfully fetched data from https://api.themoviedb.org/4/list/1.
Here is my code:
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
width: 1920
height: 1080
visible: true
title: qsTr("Hello World")
Component.onCompleted: {
mojgrid.focus = true
}
function dobioResponseNapraviModel(response) {
console.log("dobioResponseNapraviModel", typeof response)
mojgrid.model=response
}
function request(){
console.log("BOK")
const xhr=new XMLHttpRequest()
const method="GET";
const url="http://api.themoviedb.org/4/list/1";
xhr.open(method, url, true);
xhr.setRequestHeader( "Authorization", 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI5YjBkOGVlMGQzODdiNjdhYTY0ZjAzZDllODM5MmViMyIsInN1YiI6IjU2MjlmNDBlYzNhMzY4MWI1ZTAwMTkxMyIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.UxgW0dUhS62m41KjqEf35RWfpw4ghCbnSmSq4bsB32o');
xhr.onreadystatechange=function(){
if(xhr.readyState===XMLHttpRequest.DONE){
var status=xhr.status;
if(status===0 || (status>=200 && status<400)){
//the request has been completed successfully
// console.log(xhr.responseText.results)
dobioResponseNapraviModel(JSON.parse(xhr.responseText).results)
}else{
console.log("There has been an error with the request", status, JSON.stringify(xhr.responseText))
}
}
}
xhr.send();
}
/* function request(url, callback) {
var xhr=new XMLHttpRequest();
xhr.open("GET", url, true)
xhr.onreadystatechange = function() {
if(xhr.readyState===4) {
callback(xhr.responseText)
}
}
xhr.open("GET", url)
xhr.setRequestHeader( "Authorization", 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI5YjBkOGVlMGQzODdiNjdhYTY0ZjAzZDllODM5MmViMyIsInN1YiI6IjU2MjlmNDBlYzNhMzY4MWI1ZTAwMTkxMyIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.UxgW0dUhS62m41KjqEf35RWfpw4ghCbnSmSq4bsB32o');
xhr.send()
}*/
GridView {
id:mojgrid
anchors.fill: parent
Keys.onUpPressed: {
request()
}
delegate: Rectangle{ id: rect; width:500;height: 400; color:'gray'
Image{ anchors.fill:parent
fillMode: Image.PreserveAspectFit
source:modelData.backdrop_path
}
Text{ text:modelData.original_title}
}
}
}
Here are the properties I need to show in GridView when I run the application:
But I get message that the image cannot be opened:
Does anybody have any idea what is wrong? Any help would be appreciated, thank you.
What you're missing is a model to store the data in. You are incorrectly trying to bind the GridView's model to a function. Instead, create an empty ListModel, and then when your request is complete, populate that model. The GridView will then update automatically when the model updates.
ListModel {
id: movieModel
}
GridView {
...
model: movieModel
}
Component.onCompleted: {
request()
}
function request() {
...
xhr.onreadystatechange=function(){
if(xhr.readyState===XMLHttpRequest.DONE){
var status=xhr.status;
if(status===0 || (status>=200 && status<400)){
// Parse response data and append it to your model
for (var i = 0; ...) {
movieModel.append( {...} )
}
}
}
}
}
UPDATE:
You've changed your question now. The reason you can't open those images is because you are not using the full path of the url. Your http request is apparently returning relative paths. So just prepend the base url to the image name.
source: "http://api.themoviedb.org/4/list/1" + modelData.backdrop_path

Method working when data is static and filed when data is getting from promise

I have a scenario like I need to get some data from api request and based on that show images on the screen.
return Class.extend({
loadData: function (callbacks) {
callbacks.onSuccess(
[
{
"id":"1",
"title":"Apple",
"img" : "static/img/fruit/apple.png"
},
{
"id":"2",
"title":"Banana",
"img" : "static/img/fruit/banana.png"
},
{
"id":"3",
"title":"Grapes",
"img" : "static/img/fruit/grapes.png"
},
{
"id":"4",
"title":"Orange",
"img" : "static/img/fruit/orange.png"
},
{
"id":"5",
"title":"Peach",
"img" : "static/img/fruit/peach.png"
},
{
"id":"6",
"title":"Pear",
"img" : "static/img/fruit/pear.png"
}
]
);
}
});
When I use the above code it works perfectly, But I need data from api so I have implemented a method to get data and I have created a promise also, But this one is not working
return Class.extend({
// You will probably want to do something more useful then returning static data
loadData: function (callbacks) {
callbacks.onSuccess(
evtBind().then(function (res) {
console.log("res", res);
return res;
})
);
}
});
function evtBind() {
var device = RuntimeContext.getDevice();
var get = function () {
return new Promise(function (resolve, reject) {
// do a thing, possibly async, then…
device.executeCrossDomainGet('http://localhost:3000/result', {
onSuccess: function (responseObject) {
resolve(responseObject);
},
onError: function (response) {
reject(response);
}
});
});
};
return get();
}
My code looks like the above one. Can someone help me to resolve this .
If you're getting the correct array in res, then why don't you call your onSuccess with the array? At the moment you're passing a promise to onSuccess so the code there would have to be adjusted if you did that.
loadData: function (callbacks) {
evtBind().then(callbacks.onSuccess);
}
Which is short for:
loadData: function (callbacks) {
evtBind().then(res => callbacks.onSuccess(res));
}

What's the right way to use require.js with web-workers?

Currently, I'm working on porting an existing web app to require.js. Most things seem to work, but the functionality that uses web-workers. For example, I had a worker, defined in a separate js file MeshLoader.js, that loads a 3D model from an STL file:
importScripts('../lib/three.min.js', '../lib/STLLoader.js');
onmessage = function(e) {
var blob = e.data;
var reader = new FileReaderSync();
readContents(reader.readAsArrayBuffer(blob));
};
function readContents(contents) {
try {
var geometry = new THREE.STLLoader().parse(contents);
} catch (e) {
// error handling
}
var attributes = {};
// parsing the file is going on here
// ...
postMessage({
status: 'completed',
attributes: attributes,
});
}
A small remark: STLLoader.js module is a three.js plugin that defines STLLoader object and adds it to THREE namespace. This is how I rewrote it with require.js:
importScripts('../lib/require.min.js');
require({
baseUrl: '../lib'
}, [
'require', 'three.min', 'stlloader'
],
function(require, THREE, STLLoader) {
onmessage = function(e) {
var blob = e.data;
var reader = new FileReaderSync();
readContents(reader.readAsArrayBuffer(blob));
};
function readContents(contents) {
try {
var geometry = new THREE.STLLoader().parse(contents);
} catch (e) {
// error handling
}
var attributes = {};
// same code as in the initial version
// ...
postMessage({
status: 'completed',
attributes: attributes,
});
}
return onmessage;
});
The worker is called the following way:
var worker = new Worker('js/workers/MeshLoader.js');
worker.postMessage(blob);
worker.onmessage = function (event) {
if (event.data.status == 'completed') {
// ...
} else if (event.data.status == 'failed') {
// ...
} else if (event.data.status == 'working') {
// ...
}
};
So, the problem is it seems the worker isn't called at all. Maybe I need to declare it as a module in requirejs.config() section and then add the module as a dependency to other modules calling this worker?
I use it like this (jsfiddle):
importScripts("require.js");
requirejs.config({
//Lib path
baseUrl: '.',
// Some specific paths or alternative CDN's
paths: {
"socket.io": [
"//cdn.socket.io/socket.io-1.3.7",
"socket.io.backup"]
},
waitSeconds: 20
});
requirejs(["LibName"], (TheLibrary) = > {
// code after all is loaded
self.onmessage = (msg)=>{
// do stuff
TheLibrary.doStuff(msg.data);
}
// I tend to dispatch some message here
self.postMessage("worker_loaded");
}
Note that it is only after you receive "worker_loaded" that you can post messages to the worker, because only then the message will be accepted. Before, the onmessage callback is not yet estabilished. So your main code should look:
var worker = new Worker("myworker.js");
worker.onmessage = function(e) {
if(e.data=="worker_loaded") {
// Tell worker to do some stuff
}
}

Loading a script with the same variables than other and override

I`m using 2 different javascript files for two different languages, 1 file call lang.en.js that is in english and the other is lang.es.js, both files contains the same variables for example:
lang.es.js
var messages: {
settings: {
settings: "Opciones",
language: "Idioma"
}
}
lang.en.js
var messages: {
settings: {
settings: "Settings",
language: "Language"
}
}
So at first of my phonegap application i load only one of the files, i want to give an option to the user so he can change the language during the execution of the app, how can i override the other file or reload the entire app?.
Regards.
Use XHR to load the file as JSON and parse it. Something like this:
var messages = {};
var request = new XMLHttpRequest();
request.open("GET", "lang.en.json", true);
request.onreadystatechange = function(){
console.log("state = " + request.readyState);
if (request.readyState == 4) {
if (request.status == 200 || request.status == 0) {
messages = JSON.parse(request.responseText);
}
}
}
request.send();
and change you .js file to be a .json:
{
settings: {
settings: "Settings",
language: "Language"
}
}

FileReader & FileWriter issues (cordova 2.0.0 js)

I've been working on this bit of PhoneGap (cordova-2.0.0.js) code for an Android* device to persist some data. I'm getting this weird error code and the file doesn't seem to be getting written. I started with the example code and could write an inline string to a file handle successfully, so I'm sure my permissions and all are correct.
Perhaps I'm not handling all the callbacks correctly? There are a lot to listen for! It could be something with truncate(0); I had a hard time finding much documentation on it. Do I need to call window.requestFileSystem more than once? I do it 2x to register different callbacks. If not this, what is causing the error?
Suggestions on reducing the total number of lines for a Read + Write operation will be gladly accepted as well...
*Emulator running Android 2.3.4
here's my code:
var CREDENTIALS_FILE_NAME = "credentials.json";
var credentials;
// INIT -- Wait for PhoneGap to load
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, initCredentialReader, fail);
}
function initCredentialReader(fileSystem) {
fileSystem.root.getFile(CREDENTIALS_FILE_NAME, {create: true}, gotFileEntryReader, fail);
}
function gotFileEntryReader(fileEntry) {
fileEntry.file(gotFileToRead, fail);
}
function gotFileToRead(file){
var reader = new FileReader();
reader.onloadend = function(e) {
console.log("--FILE READER: "+e.target.result);
if( e.target.result.length < 1 ) {
credentials = newCredentials();
} else {
credentials = JSON.parse( e.target.result );
}
};
reader.readAsText(file);
}
// END CHAIN
function initCredentialWriter(fileSystem) {
fileSystem.root.getFile(CREDENTIALS_FILE_NAME, {create: true}, gotFileEntryWriter, fail);
}
function gotFileEntryWriter(fileEntry) {
fileEntry.createWriter(gotFileWriter, fail);
}
function gotFileWriter(writer) {
writer.onwrite = function(e) {
console.log("--- write success");
};
var toWrite = JSON.stringify(credentials);
console.log("--- toWrite: "+toWrite);
writer.truncate(0);
writer.seek(0);
writer.write(toWrite);
}
function fail(error) {
console.log("--- write FAIL: "+error.code);
}
function newCredentials() {
console.log("returning newCredentials!");
return {
"username" : "",
"password" : "",
"organization" : "",
"cookieValue" : "" };
}
function getCredentials() {
console.log("--- getCredentials: "+credentials);
return credentials;
}
function saveCredentials( jsonCredentials ) {
console.log('--- saveCredentials jsonCredentials: '+ jsonCredentials);
credentials = JSON.stringify( jsonCredentials );
console.log('--- credentials to save: '+credentials)
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, initCredentialWriter, fail);
return credentials;
}
ERROR
08-14 18:41:38.839: I/Web Console(2678): Error in success callback:
File9 =
{"code":7,"line":2863,"expressionBeginOffset":91407,"expressionEndOffset":91455,"sourceId":4122528,"sourceURL":"file:///android_asset/www/cordova-2.0.0.js"}
at file:///android_asset/www/cordova-2.0.0.js:258
So, turns out that calling truncate() and write() is not asynchronously correct-- just had implement more callbacks, like so:
function initCredentialTruncate(fileSystem) {
fileSystem.root.getFile(CREDENTIALS_FILE_NAME, {create: true}, gotFileEntryTruncate, fail);
}
function gotFileEntryTruncate(fileEntry) {
fileEntry.createWriter(gotFileTruncate, fail);
}
function gotFileTruncate(writer) {
writer.onwrite = function(e) {
console.log("--- truncate success");
};
writer.truncate(0);
//writer.seek(0);
}
// END CHAIN
and call the init function when necessary. thx for letting me vent, StackOverflow...

Categories

Resources