I am trying to wait for javascript callback, however, the swift callback is being called before the javascript callback. How can I wait for JS callback?
AppWebView.evaluateJavaScript("$('.loading-gif').removeClass('hide', function() { return true } );") { (Any, Error) in
\\ .loading-gif successfully hided.
}
There is no way to do this using evaluateJavaScript, since it's callback is called as soon as the evaluated Javascript returns. This is most likely before your asynchronous call is done.
You will need to register a message handler in your swift code similar to…
let script = WKUserScript(source: javascriptString,
injectionTime: injectionTime,
forMainFrameOnly: true)
userContentController.addUserScript(script)
webView.configuration.userContentController.addScriptMessageHandler(self,
name: "didShowLoading")
You can then define a delegate method in your Swift code…
func userContentController(userContentController: WKUserContentController,
didReceiveScriptMessage message: WKScriptMessage) {
if message.name == "didShowLoading" {
// do something
}
}
Finally, you can post the message from your javascript code…
var script = "$('.loading-gif').removeClass('hide', function() {"
+ " webkit.messageHandlers['didShowLoading'].postMessage('');"
+ "});"
webView.evaluateJavaScript(script)
You can use this library https://github.com/marcuswestin/WebViewJavascriptBridge
On JS side:
bridge.registerHandler("show_loading", function(data, responseCallback) {
$('.loading-gif').removeClass('hide', function() {
responseCallback(true)
} );
})
And on Swift side:
self.bridge.callHandler("show_loading",data:nil, handler: { (data:AnyObject!) in
//do whatever you want...
})
You could notify the native iOS code via a callback from the javascript callback. If you add something like this to your javascript:
window.webkit.messageHandlers.observe.postMessage(JSON.stringify( { callback: 'show_loading' } ));
Then on the native side setup a WKUserContentController for your WKWebView (installed on the WKWebViewConfiguration). Your userContentController.didReceiveScriptMessage handler will get called with the JSON sent from the Javascript side.
Related
I am trying to pass messages between content script and the extension
Here is what I have in content-script
chrome.runtime.sendMessage({type: "getUrls"}, function(response) {
console.log(response)
});
And in the background script I have
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.type == "getUrls"){
getUrls(request, sender, sendResponse)
}
});
function getUrls(request, sender, sendResponse){
var resp = sendResponse;
$.ajax({
url: "http://localhost:3000/urls",
method: 'GET',
success: function(d){
resp({urls: d})
}
});
}
Now if I send the response before the ajax call in the getUrls function, the response is sent successfully, but in the success method of the ajax call when I send the response it doesn't send it, when I go into debugging I can see that the port is null inside the code for sendResponse function.
From the documentation for chrome.runtime.onMessage.addListener:
This function becomes invalid when the event listener returns, unless you return true from the event listener to indicate you wish to send a response asynchronously (this will keep the message channel open to the other end until sendResponse is called).
So you just need to add return true; after the call to getUrls to indicate that you'll call the response function asynchronously.
The accepted answer is correct, I just wanted to add sample code that simplifies this.
The problem is that the API (in my view) is not well designed because it forces us developers to know if a particular message will be handled async or not. If you handle many different messages this becomes an impossible task because you never know if deep down some function a passed-in sendResponse will be called async or not.
Consider this:
chrome.extension.onMessage.addListener(function (request, sender, sendResponseParam) {
if (request.method == "method1") {
handleMethod1(sendResponse);
}
How can I know if deep down handleMethod1 the call will be async or not? How can someone that modifies handleMethod1 knows that it will break a caller by introducing something async?
My solution is this:
chrome.extension.onMessage.addListener(function (request, sender, sendResponseParam) {
var responseStatus = { bCalled: false };
function sendResponse(obj) { //dummy wrapper to deal with exceptions and detect async
try {
sendResponseParam(obj);
} catch (e) {
//error handling
}
responseStatus.bCalled= true;
}
if (request.method == "method1") {
handleMethod1(sendResponse);
}
else if (request.method == "method2") {
handleMethod2(sendResponse);
}
...
if (!responseStatus.bCalled) { //if its set, the call wasn't async, else it is.
return true;
}
});
This automatically handles the return value, regardless of how you choose to handle the message. Note that this assumes that you never forget to call the response function. Also note that chromium could have automated this for us, I don't see why they didn't.
You can use my library https://github.com/lawlietmester/webextension to make this work in both Chrome and FF with Firefox way without callbacks.
Your code will look like:
Browser.runtime.onMessage.addListener( request => new Promise( resolve => {
if( !request || typeof request !== 'object' || request.type !== "getUrls" ) return;
$.ajax({
'url': "http://localhost:3000/urls",
'method': 'GET'
}).then( urls => { resolve({ urls }); });
}) );
I have a call back function which is getting data from an external API and depends on a data check I have tried for a slot elicitation inside callback but looks like elicitation is not working inside the callback. Please find the code snippet below,
GetCustomerDetails().then(response => {
var serializedcustomerDetails = convert.xml2json(response.data, {
compact: true,
spaces: 2
});
var customerDetails = JSON.parse(serializedcustomerDetails);
let filteredCustomerDetails = _.filter(customerDetails.CustomerInfo.CustomerDetails, function (o) {
return o.CustomerName._text.includes(customerName);
})
if (filteredCustomerDetails.length == 1) {
callback(elicitSlot(outputSessionAttributes, intentRequest.currentIntent.name,
intentRequest.currentIntent.slots, 'CustomerCode', {
contentType: 'PlainText',
content: `Do you mean ${filteredCustomerDetails[0].CustomerName._text} of ${filteredCustomerDetails[0].SpecialityName._text} department?`
}));
return;
}
}).catch(error => {
console.log(`${error}`)
})
This is my first Awnser on stack so please bear with me.
I have come accross the same problem in a recent project and there are a few things that you can check.
How long does the API call take?
If your API call takes a long time it will be worth checking the timeout settings on your Lambda function. AWS Console -> Lambda -> Your Function -> Basic settings -> Timeout.
Does your Lambda function finish before the API call is done?
I fixed this issue by building a node module to handle my business logic, the module has a function called getNextSlot it returns as a Promise. Inside this function I check the incoming event and figure out which slot I need to elicit next, part of my flow is to call an API endpoint that takes around 10 seconds to complete.
I use the request-promise package to make the api call, this node module makes sure that the lambda function keeps running while the call is running.
exports.getData = function (url, data) {
var pr = require("request-promise");
var options = {
method: 'POST',
url: 'api.example',
qs: {},
headers:
{
'Content-Type': 'application/json'
},
body: {
"example": data
},
json: true,
timeout: 60000
};
return pr(options);
}
In my main code I call this function as:
apiModule.getData("test", "data")
.then(function (data) {
//Execute callback
})
.catch(function (error) {
console.log(error);
reject(error);
});
This solved the issue for me anyways.
Thanks,
This code works:
firebase.database().ref($scope.language).orderByChild('word').equalTo($scope.word).once('value')
.then(function(snapshot) {
console.log(snapshot.val());
})
It logs the object and its key.
This code doesn't work:
firebase.database().ref($scope.language).orderByChild('word').equalTo($scope.word).remove()
.then(function(snapshot) {
console.log("Removed!");
})
The error message is:
TypeError: firebase.database(...).ref(...).orderByChild(...).equalTo(...).remove is not a function
The documentation makes remove() look simple. What am I missing?
You can only load data once you know its specific location in the JSON tree. To determine that location, you need to execute the query and loop through the matching results:
firebase.database().ref($scope.language).orderByChild('word').equalTo($scope.word).once("value").then(function(snapshot) {
snapshot.forEach(function(child) {
child.ref.remove();
console.log("Removed!");
})
});
If you only want to log after all have been removed, you can use Promise.all():
firebase.database().ref($scope.language).orderByChild('word').equalTo($scope.word).once("value").then(function(snapshot) {
var promises = [];
snapshot.forEach(function(child) {
promises.push(child.ref.remove());
})
Promise.all(promises).then(function() {
console.log("All removed!");
})
});
This is Frank's first code block with another closure. Without the closure the record is removed from the database but then there's an error message:
Uncaught (in promise) TypeError: snapshot.forEach(...).then is not a function
Adding a closure fixes the error message.
firebase.database().ref($scope.language).orderByChild('word').equalTo($scope.word).once("value").then(function(snapshot) {
snapshot.forEach(function(child) {
child.ref.remove();
}); // a closure is needed here
}).then(function() {
console.log("Removed!");
});
on (press)
{
here I want to call a javascript function
}
on (rollOver)
{
gotoAndPlay("guizhou");
}
on (rollOver)
{
startDrag ("1guizhou", true);
}
on (rollOut)
{
gotoAndPlay("kongk");
}
How to call a javascript function in the press event ? in as3.0 there is mouseevent but in as1.0 ,I don't konw how do that? Can you help me?
You can use getURL:
getURL("javascript:functionName()");
additional about this function can be found at:
http://help.adobe.com/en_US/as2/reference/flashlite/WS5b3ccc516d4fbf351e63e3d118ccf9c47f-7fc9.html
I have this site that i am working on that i need to figure out when the last ajax call is finished...I am using this jQuery plugin and all works great but the problem is the client uploads 2 files at a time... and i need to redirect to another page after the last ajax call. If you look the it in firebug while uploading files it run http://dev.posnation.com/test/j/example/upload.php twice and i need to have the page redirect after and only after the second run..is there a way in javascript or jQuery to tell if the ajax calls are complete.
Here is the code that instantiates it
// Initialize the jQuery File Upload widget:
$('#fileupload').fileupload();
and there is a onDone function in the jQuery.fileupload.js
_onDone: function (result, textStatus, jqXHR, options) {
but its running after the first run not after both files are uploaded....any ideas on how i can redirect to another page after both files are uploaded....thanks in advance
the short answer is to decrement a counter in the onDone callback. one possible implementation would be to use a global variable as illustrated below:
var numberFilesToUpload = 2;
// code to upload file
onDone: function() {
// this is the upload plug-ins callback function
numberFilesToUpload--;
if (numberFileToUpload == 0) {
windows.location.href = "/uploading-finished";
}
}
there are more elegant solutions, but they will all involve decrementing a counter.
You could use closure to produce the function which has a local records for files have uploaded.
var fileUploads = function (filesNum, callback) {
var numberFilesToUpload = filesNum;
// code to upload file
return function() {
// this is the upload plug-ins callback function
numberFilesToUpload--;
if (numberFileToUpload == 0) {
callback();
}
};
}(2, function () {
//what you want to do when files are all uploaded.
});
fileUploads will fire the callback function after 2 files are uploaded. The callback and filelimits are specified in this line
var fileUploads = function () {
//...
} (2, function () {});