Can't execute 2 $http calls in angularjs at the same time - javascript

I am implementing long polling to know the state of some long running event on the server side. I create my own factory that will notify me when a server event triggers. Here is the factory.
.factory("$httpPolling", function ($http) {
function $httpPolling($httpService) {
var _responseListener, _finishListener;
var cancelCall = false;
var _pollId;
function waitForServerCall(id) {
console.log("executing waitForServerCall");
$httpService.get(href("~/polling/" + id))
.success(function (response) {
var cancelPolling = _responseListener(response);
if (cancelPolling || cancelCall) {
return;
}
else {
waitForServerCall(id);
}
});
};
function _sendData(httpMethod, url) {
var pollingId = guid();
_pollId = pollingId;
if (url.split("?").length == 2) {
url += "&pollid=" + pollingId;
}
else {
url += "?pollid=" + pollingId;
}
if (httpMethod == 0) {
$httpService.get(url).success(function (response) {
if (_finishListener) {
_finishListener(response);
}
cancelCall = true;
});
}
else {
$httpService.post(url).success(function (response) {
if (_finishListener) {
_finishListener(response);
}
cancelCall = true;
});
}
}
var $self = this;
this.get = function (url) {
_sendData(0,url);
return $self;
};
this.post = function (url) {
_sendData(1, url);
return $self;
};
this.listen = function (_listener) {
_responseListener = _listener;
waitForServerCall(_pollId);
return $self;
}
this.finish = function (_finish) {
_finishListener = _finish;
return $self;
}
}
return new $httpPolling($http);
});
Where the sintax of usage should be:
$httpPolling.get("url")
.listen(function(event){
// fires when server event happend
})
.finish(function(response){
// fires when the long running process finish
});
The problem is that _sendData method does not execute asynchronously because the waitForServerCall only executes the ajax call when the _sendData(long running process) method get the response from the server.
Why? Is this an angular behavior?

Angular $httpProvider has an option provided for async http calls, which is set to false as default value.
Try
app.config(function ($httpProvider) {
$httpProvider.useApplyAsync(true);
});

Related

Set a function to await response when calling through another function

Using JavaScript and AngularJS, I have a controller function looking to set a bool based on the return of a service call, which awaits the return of an API call. How do I write the controller function to wait for the API response?
Controller...
var someBoolValue = false;
function firstCheck() {
// function checks other things
if (someCheck) {
// how do I set someBoolValue to await the response, this code did NOT work
SomeService.CheckforSomething(data, moreData).then(function (response) {
someBoolValue = response;
});
}
Service...
function CheckforSomething(data, moreData) {
var anImportantNumber = null;
var anotherNumber = 456;
// function does other things
anImportantNumber = 123;
if (someCondition) {
ApiService.GetMyData()
.then(function (data) {
anImportantNumber = data.ThisValue.WRT;
}
}
return (anImportantNumber != anotherNumber);
}
API Service...
function GetMyData() {
uri = 12345;
$http.get(uri)
.then(function (response) {
dererred.resolve(response.data)
}
return deferred.promise;
}

Elegant way of executing callback functions with params [duplicate]

wifiservice.js:
angular.module('app.WifiServices', [])
.factory('WifiService', function(){
var unique_array = angular.fromJson('[]');
function win_wifi(e){
alert("Success");
}
function fail_wifi(e){
alert("Error");
}
function connectWifi(wifi_ssid){
WifiWizard.connectNetwork(wifi_ssid, win_wifi, fail_wifi);
}
function listHandler(a){
var network_array = [];
for(var i=0; i<a.length; i++){
network_array.push("SSID: " + a[i].SSID + " Signal: " + a[i].level);
}
unique_array = network_array.filter(function(elem, pos) {
return network_array.indexOf(elem) == pos;
});
// alert("Wifi List Ready!");
}
function getScanResult(){
WifiWizard.getScanResults(listHandler, failNetwork);
}
function successNetwork(e){
window.setTimeout(function(){
getScanResult();
}, 3000);
}
function failNetwork(e){
alert("Network Failure: " + e);
}
window.setTimeout(function(){
WifiWizard.startScan(successNetwork, failNetwork);
}, 1000);
return {
list: function(){
return unique_array;
},
connectionToWifi: function(name){
connectWifi(name);
}
};
});
My whole controller:
app.controller('WifiController', ['$scope', 'WifiService', function($scope, WifiService) {
$scope.wifiList = [];
window.setTimeout(function() {
$scope.wifiList = WifiService.list();
// alert($scope.wifiList);
$scope.$apply();
}, 5000);
$scope.getList = function() {
$scope.wifiList = WifiService.list();
return $scope.wifiList;
}
$scope.connectWifi = function(name) {
WifiService.connectionToWifi(name);
}
$scope.checkin = function() {
$scope.getList()
.then(function(result) {
console.log(result);
});
}
}]);
What I am trying to do is, to call the $scope.getList(), which returns a list of the surrounding wifi SSIDs, then within $scope.checkin() I would like to process those data.
Since scanning needs some time I have to wait the getList function to finish, thats Why I am trying to use .then, but it gives me the error says on the title. Any ideas?
How to create a AngularJS promise from a callback-based API
To create an AngularJS promise from a callback-based API such as WifiWizard.connectNetwork, use $q.defer:
function connectWifi(wifi_ssid) {
var future = $q.defer();
var win_wifi = future.resolve;
var fail_wifi = future.reject;
WifiWizard.connectNetwork(wifi_ssid, win_wifi, fail_wifi);
return future.promise;
};
The above example returns a $q Service promise that either resolves or rejects using the callbacks from the API.
Well, I came up with something different:
var unique_array = [];
$scope.wifiList = [];
$ionicLoading.show({
template: "Scanning surrounding AP's..."
});
window.setTimeout(function() {
$scope.wifiList = WifiService.list();
// alert($scope.wifiList);
while ($scope.wifiList == []) {
console.log('Scanning...');
}
$scope.$apply();
$ionicLoading.hide();
}, 5000);
What I realize is that the scanning starts once I load the view. So, I added an IonicLoader to force the user to wait, and not be able to press any buttons till the scan is finished. So no function shall wait one another. Not quite code-wise correct, but it does what I need.

$watch a service when theres changes

I want to watch changes in my services like in my system logs when there's someone who login the getlogs function must trigger how to achieve this ???
dashboard.controller
function getLogs() {
return dataservice.getLogs().then(function (data) {
vm.logs = data;
return vm.logs;
});
}
dataservice.js
function getLogs() {
return $http.get('/api/timeLogs')
.then(success)
.catch(fail);
function success(response) {
return response.data;
}
function fail(e) {
return exception.catcher('XHR Failed for getPeople')(e);
}
}
I've tried this but its not working
$scope.$watch('dataservice.getLogs()', function () {
getLogs();
}, true);
This is a case for observable pattern where you subscribe for changes on your service
app.service('dataservice', function($http) {
var subscribers = [];
var addSubscriber = function(func){
subscribers.push(func);
}
var notifySubscribers = function(){
for(var i = 0; i< subscribers.length; i++){
subscribers[i](); //invoke the subscriber
}
};
var addLog = function(){
//let's say that the logs are added here
//then notify the subscribers that a new log has been added
notifySubscribers();
};
var getLogs = function() {
return $http.get('/api/timeLogs')
.then(success)
.catch(fail);
function success(response) {
return response.data;
}
function fail(e) {
return exception.catcher('XHR Failed for getPeople')(e);
}
};
return {
addSubscriber: addSubscriber,
addLog: addLog,
getLogs: getLogs
}
});
Then in your controller add a subscriber function to the service
dataservice.addSubscriber(function(){
console.log('new log added');
dataservice.getLogs();
});
NOTE: this can also be done with the RxJs library
if you want to check and get data's change from server, watching a service is not for that, use a polling service.
you check for every 1sec (for example) from a server:
example:
$interval(function() {
dataservice.getLogs().then(function(data) {
vm.logs = data;
});
}, 1000);
or much better:
getLogs = function () {
dataservice.getLogs().then(function(data){
vm.logs = data;
$timeout(getLogs, 1000)
});
}

Combining two promises

I am really new to JavaScript and promises and to be honest I don't fully understand how promises work so I need some help.
I am using Google Cloud Messaging to push notifications from my site to my users. When users receive a notification and clicks on it, it opens a URL stored in a IndexedDB.
importScripts('IndexDBWrapper.js');
var KEY_VALUE_STORE_NAME = 'key-value-store', idb;
function getIdb() {
if (!idb) {
idb = new IndexDBWrapper(KEY_VALUE_STORE_NAME, 1, function (db) {
db.createObjectStore(KEY_VALUE_STORE_NAME);
});
}
return idb;
}
self.addEventListener('notificationclick', function (event) {
console.log('On notification click: ', event);
event.notification.close();
event.waitUntil(getIdb().get(KEY_VALUE_STORE_NAME, event.notification.tag).then(function (url) {
var redirectUrl = '/';
if (url) redirectUrl = url;
return clients.openWindow(redirectUrl);
}));
});
So in the code above, I know that the getIdb()...then() is a promise, but is the event.waitUntil also a promise?
The problem with the above code is that it opens a instance of Chrome every time the notification is clicked and I would prefer that it would utilize an existing instance if available. The following does just that:
self.addEventListener('notificationclick', function(event) {
console.log('On notification click: ', event.notification.tag);
event.notification.close();
event.waitUntil(
clients.matchAll({
type: "window"
})
.then(function(clientList) {
for (var i = 0; i < clientList.length; i++) {
var client = clientList[i];
if (client.url == '/' && 'focus' in client)
return client.focus();
}
if (clients.openWindow) {
return clients.openWindow('/');
}
})
);
});
However, now I have two promises, getIdb and clients.matchAll and I really have no idea how to combine the two promises and the two sets of code. Any help would be greatly appreciated. Thanks!
For reference, here is IndexDBWrapper.js:
'use strict';
function promisifyRequest(obj) {
return new Promise(function(resolve, reject) {
function onsuccess(event) {
resolve(obj.result);
unlisten();
}
function onerror(event) {
reject(obj.error);
unlisten();
}
function unlisten() {
obj.removeEventListener('complete', onsuccess);
obj.removeEventListener('success', onsuccess);
obj.removeEventListener('error', onerror);
obj.removeEventListener('abort', onerror);
}
obj.addEventListener('complete', onsuccess);
obj.addEventListener('success', onsuccess);
obj.addEventListener('error', onerror);
obj.addEventListener('abort', onerror);
});
}
function IndexDBWrapper(name, version, upgradeCallback) {
var request = indexedDB.open(name, version);
this.ready = promisifyRequest(request);
request.onupgradeneeded = function(event) {
upgradeCallback(request.result, event.oldVersion);
};
}
IndexDBWrapper.supported = 'indexedDB' in self;
var IndexDBWrapperProto = IndexDBWrapper.prototype;
IndexDBWrapperProto.transaction = function(stores, modeOrCallback, callback) {
return this.ready.then(function(db) {
var mode = 'readonly';
if (modeOrCallback.apply) {
callback = modeOrCallback;
}
else if (modeOrCallback) {
mode = modeOrCallback;
}
var tx = db.transaction(stores, mode);
var val = callback(tx, db);
var promise = promisifyRequest(tx);
var readPromise;
if (!val) {
return promise;
}
if (val[0] && 'result' in val[0]) {
readPromise = Promise.all(val.map(promisifyRequest));
}
else {
readPromise = promisifyRequest(val);
}
return promise.then(function() {
return readPromise;
});
});
};
IndexDBWrapperProto.get = function(store, key) {
return this.transaction(store, function(tx) {
return tx.objectStore(store).get(key);
});
};
IndexDBWrapperProto.put = function(store, key, value) {
return this.transaction(store, 'readwrite', function(tx) {
tx.objectStore(store).put(value, key);
});
};
IndexDBWrapperProto.delete = function(store, key) {
return this.transaction(store, 'readwrite', function(tx) {
tx.objectStore(store).delete(key);
});
};
One way to deal with multiple promises is with Promise.all
Promise.all([promise0, promise1, promise2]).then(function(valArray) {
// valArray[0] is result of promise0
// valArray[1] is result of promise1
// valArray[2] is result of promise2
});
read about promise.all - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
event.waitUntil() takes a promise - this allows the browser to keep your worker alive until you've finished what you want to do (i.e. until the promise that you gave to event.waitUntil() has resolved).
As the other answer suggests, you can use Promise.all() within event.waitUntil. Promise.all() takes an array of promises and returns a promise, so you can call then on it. Your handling function will get an array of promise results when all of the promises you've provided to Promise.all have resolved. Your code will then look something like this (I haven't actually tested this, but it should be close):
self.addEventListener('notificationclick', function (event) {
event.notification.close();
event.waitUntil(Promise.all([
getIdb().get(KEY_VALUE_STORE_NAME, event.notification.tag),
clients.matchAll({ type: "window" })
]).then(function (resultArray) {
var url = resultArray[0] || "/";
var clientList = resultArray[1];
for (var i = 0; i < clientList.length; i++) {
var client = clientList[i];
if (client.url == '/' && 'focus' in client)
return client.focus();
}
if (clients.openWindow) {
return clients.openWindow(url);
}
}));
});
Though I am very late to the party but,
I think you can use this very light, vanilla JS library that does things for you. https://www.npmjs.com/package/easy-promise-all
Here is the small code sample for it.
var { EasyPromiseAll } = require('easy-promise-all');
EasyPromiseAll({
resultFromPromise1: Promise.resolve('first'),
resultFromPromise2: Promise.reject('second'),
}).then((results) => {
const { resultFromPromise1, resultFromPromise2 } = results;
console.log(resultFromPromise1, resultFromPromise2);
});

Angularjs $resource get/post returning same result

I'm brand new to angular, so I'm probably doing things all wrong. My query is returning an array of objects like it should be. I then do a click event to test the post..it hits my web api just fine...but then it returns that same array from my get. I'm guessing this is cached? Why would my post show the results of my earlier get?
Edit - Sorry, I could have been more clear. When I run my saveTest method, a post fires and my array saves, however the 'result' variable of that save..is the array from my original get.
app.directive('referenceSection', function () {
return {
restrict: 'E',
templateUrl: '/app/loanapplication/views/reference-section.html',
controller: function ($scope, referenceService) {
$scope.json = angular.toJson($scope.referenceArray);
$scope.referenceArray = [];
referenceService.query().$promise.then(function (result) {
$scope.referenceArray = result;
}, function () {
alert("fail");
});
$scope.saveTest = function () {
referenceService.save(angular.toJson($scope.referenceArray)).$promise.then(function (result) {
var x = result;
}, function () {
alert("save fail");
});
}
}
};
});
Service
app.factory('referenceService', function ($resource) {
var requestUri = '/api/reference';
return $resource(requestUri)
});
Web api
public class ReferenceController : BaseController
{
public HttpResponseMessage Get()
{
List<WebReference> references = new List<WebReference>();
WebReference reference = new WebReference();
WebReference reference2 = new WebReference();
reference.Name = "Andrew";
reference.Relationship = "QuickSupport";
reference.Id = 1;
reference2.Name = "Josh";
reference2.Relationship = "Hansen";
reference2.Id = 2;
references.Add(reference);
references.Add(reference2);
if (references == null) throw new HttpResponseException(HttpStatusCode.NotFound);
return Request.CreateResponse<IEnumerable<WebReference>>(HttpStatusCode.OK, references);
}
public HttpResponseMessage Post([FromBody]WebReference[] references)
{
try
{
var msg = new HttpResponseMessage(HttpStatusCode.Created);
return msg;
}
catch (Exception e)
{
throw new HttpResponseException(HttpStatusCode.Conflict);
}
}
}
}
referenceService.query().$promise.then(function (result) {
$scope.referenceArray = result;
After this, you need to call $scope.$apply() to inform angular of your changes made to be bound. If I guessed your question correctly .
:-)
From where you are reading response? From x that is not available outside then function or it is mistake to not attach it to referenceArray
referenceService.save(angular.toJson($scope.referenceArray)).$promise
.then(function (result) {
var x = result;
}, function () {
alert("save fail");
});

Categories

Resources