I want to wait constructor() which has async method handled by Promise
What I want to do is waiting two async method in constructor, and then wait constructor itself.
However my code shows the error Uncaught (in promise) ReferenceError: resolve is not defined
What is the best practices for this purpose??
class MyClass{
constructor(){
var p1 = new Promise(function(resolve, reject){
$.ajax({
type: "GET",
url: api1
success: function(response) {
console.log(response);
resolve();
}
});
});
var p2 = new Promise(function(resolve, reject){
$.ajax({
type: "GET",
url: api2
success: function(response) {
console.log(response);
resolve();
}
});
});
Promise.all([p1,p2]).then(function(value){
console.log("finish");
resolve(); // this shows error.
});
}
}
$(function() {
var temp = new Promise(function(resolve,reject){
var myClass = new MyClass();
});
temp.then(function (value) {
console.log("finish all");
});
}
A constructor will always run synchronously. In addition to that, you don't want the explicit Promise construction anti-pattern, so you wouldn't want to call resolve inside a Promise.all anyway. The Promise.all([p1,p2]) will be a Promise that resolves when both p1 and p2 have resolved, so assign that a property to the instance, and call .then on that property. Change
Promise.all([p1,p2]).then(function(value){
// ...
});
to
this.apiPromises = Promise.all([p1,p2]);
and, when constructing the instance:
$(function() {
const myInstance = new MyClass();
myInstance.apiPromises
.then(function (value) {
console.log("finish all");
})
.catch((error) => {
// handle errors
});
});
(If you aren't calling either of the resolves of with anything in p1 or p2, best to delete the value parameter, since it holds no useful information)
$.ajax returns a thenable already, so you don't need to call new Promise when creating the p1 and p2s:
var p1 = $.ajax({
type: "GET",
url: api1, // remember to put commas after values in an object to avoid a SyntaxError
success: function(response) {
console.log(response);
}
});
var p2 = $.ajax({
type: "GET",
url: api2, // remember to put commas after values in an object to avoid a SyntaxError
success: function(response) {
console.log(response);
}
});
(using the above will also properly reject the Promise.all when an error occurs)
Related
Prior to adding a promise, this code/spec was successfully passing
// Code
function getAvailability() {
$.ajax({
type:"POST",
url: "/get-availability",
dataType:"json",
contentType:"application/json",
data: params,
success: function() {
// ...
},
error: function() {
console.log(">> in error")
catalogDOM.updateAvailabilityForItem(0)
}
})
}
// Spec
describe("if ajax fails", function() {
beforeEach(function() {
ajaxSpy = spyOn($, "ajax")
ajaxSpy.and.callFake(function(e) {
e.error()
})
spyOn(catalogDOM, "updateAvailabilityForItem")
getAvailability()
})
it("should call updateAvailabilityForItem with 0", function() {
expect(catalogDOM.updateAvailabilityForItem).toHaveBeenCalledWith(0)
}
})
// Console output
>> in error
But then I made the ajax fire after another async function ran. I thought I spied on things correctly, and in fact, the console.log continues to indicate that the code is running. In fact, if I mocked a return value for like so spyOn(catalogDOM, "updateAvailabilityForItem").and.returnValue("works") and then in the code wrote: console.log(catalogDOM.updateAvailabilityForItem(0)), then the log does output "works"!
Yet the spec fails. Why?
// Code
function getAvailability() {
//////// START NEW LINE
utilityOrders.checkElement('#overlay').then((selector) => {
//////// END NEW LINE
$.ajax({
type:"POST",
url: "/get-availability",
dataType:"json",
contentType:"application/json",
data: params,
success: function() {
// ...
},
error: function() {
console.log(">> in error")
catalogDOM.updateAvailabilityForItem(0)
}
})
})
}
// Spec
describe("if ajax fails", function() {
beforeEach(function() {
//////// START NEW LINE
resolved_promise = new Promise(function(resolve, reject) { resolve() })
spyOn(utilityOrders,"checkElement").and.returnValue(resolved_promise)
//////// END NEW LINE
ajaxSpy = spyOn($, "ajax")
ajaxSpy.and.callFake(function(e) {
e.error()
})
spyOn(catalogDOM, "updateAvailabilityForItem")
getAvailability()
})
it("should call updateAvailabilityForItem with 0", function() {
expect(catalogDOM.updateAvailabilityForItem).toHaveBeenCalledWith(0)
}
})
// Console output
>> in error
Try making the following changes:
// Code
// !! add async here so we have control to wait until it goes inside of the .then before continuing !!
async function getAvailability() {
//////// START NEW LINE
utilityOrders.checkElement('#overlay').then((selector) => {
//////// END NEW LINE
$.ajax({
type:"POST",
url: "/get-availability",
dataType:"json",
contentType:"application/json",
data: params,
success: function() {
// ...
},
error: function() {
console.log(">> in error")
catalogDOM.updateAvailabilityForItem(0)
}
})
})
}
// Spec
describe("if ajax fails", function() {
// !! add async and done argument to call when we are done with this function !!
beforeEach(async function(done) {
//////// START NEW LINE
resolved_promise = new Promise(function(resolve, reject) { resolve() })
spyOn(utilityOrders,"checkElement").and.returnValue(resolved_promise)
//////// END NEW LINE
ajaxSpy = spyOn($, "ajax")
ajaxSpy.and.callFake(function(e) {
e.error()
})
spyOn(catalogDOM, "updateAvailabilityForItem")
// await getAvailability so the promise is resolved before proceeding (then is called)
await getAvailability();
// call done to let Jasmine know you're done with the test
done();
})
it("should call updateAvailabilityForItem with 0", function() {
expect(catalogDOM.updateAvailabilityForItem).toHaveBeenCalledWith(0)
}
The above modifications should hopefully fix it for you. Dealing with promises and tests is difficult because sometimes in the test you have to tell Jasmine that the promises that were created in the code that is being tested, wait for them to complete before doing assertions.
I have a function getPackageName, and inside this function i defined a promise , i need to save the value of this promise and return the value outside , so whenever i call getPackageName,it will return promise result value ...
getPackageName(modelName){
let modelReq = new Ext.Promise(function(resolve, reject){
Ext.Ajax.request({
url: `db/code/pkgname?tablename=${modelName}`,
method: 'GET',
callback: function(options, success, response){
if (success){
if (response){
resolve(response);
}
else{
console.log("No response from server");
}
}
});
});
modelReq.then(res){
return res.res
}
}
it's not working as expected , and whenever i call getPackageName, it will return undefined .
Any help would be appreciated for sure .
I am trying to reject a deferred object within a $http.get call but it is not being properly rejected. The errorcallback is not being called, and I just can't figure out why.
Here is basically what I have:
var promise = this.SomeAsyncCall(this.$.SomeID)
.then(
function ()
{
service.SendAsyncDownloadReqeuest(someOtherID);
},
this.ErrorHandler.HandleError)
.then(this._DownloadInitiated, this.ErrorHandler.HandleError);
promise["finally"](this._DownloadRequestFinished);
And here is the service.SendAsyncDownloadRequest:
var SendAsyncDownloadReqeuest = function (ID)
{
var url = "someUrl?ID=" + customerID;
var navigator = self.$window.navigator;
var window = self.$window;
var deferred = self.$q.defer();
self.$http.get(url, { responseType: 'arraybuffer' })
.success( function(data, status, headers) {
var success = false;
//Initiate download via blob. Set success
success ? deferred.resolve() : deferred.reject();
})
.error(function (data, status)
{
var error =
{
//Some error properties
}
deferred.reject(error);
});
return deferred.promise;
};
When I test this by returning a 500 status code from the server, it reaches the .error block of the http get call and completes the reject line, but the ErrorHandler's HandleError method is not reached. The HandleError method is correct since it works with errorcallbacks for promises rejected in anything that's not $http.get.
You're never passing the promise from service.SendAsyncDownloadReqeuest(someOtherID); back to your HandleError function. You need to change your code to this:
var promise = this.SomeAsyncCall(this.$.SomeID)
.then(
function ()
{
return service.SendAsyncDownloadReqeuest(someOtherID);
},
this.ErrorHandler.HandleError)
.then(this._DownloadInitiated, this.ErrorHandler.HandleError);
promise["finally"](this._DownloadRequestFinished);
If you want to be a little clearer you could change it to this:
var promise = this.SomeAsyncCall(this.$.SomeID)
.then(function () {
service.SendAsyncDownloadReqeuest(someOtherID).then(
this._DownloadInitiated,
this.ErrorHandler.HandleError);
},
this.ErrorHandler.HandleError);
promise["finally"](this._DownloadRequestFinished);
Don't use the success method either way.Both methods have been deprecated.
The $http legacy promise methods success and error have been
deprecated. Use the standard then method instead. If
$httpProvider.useLegacyPromiseExtensions is set to false then these
methods will throw $http/legacy error.
Here is the shortcut method
$http.post('/someUrl', data, config).then(successCallback, errorCallback);
Here is a longer GET method sample
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
Official Documentation
i've a function that return a boolean according to http.get call.
function checkValue (value) {
var defer = $q.defer();
var request = $http({
method: "get",
url: "someurl"
});
request.success(function (data) {
defer.resolve(data.response);
});
return defer.promise;
}
The problem is that return value is an object like this:
d {$$state: Object}
$$state: Object
status: 1
value: true
__proto__: Object
__proto__: d
How could i resolve it?
Yes, your function is returning a Promise object. The $$state property belongs to the promise object and is used by Angular internally (as suggested by $$ prefix) and therefore not to be used by developers using Angular. More interesting is that promise object has a "then" method which can be used to attach handlers that are called when the promise gets resolved. Promise gets resolved when the defer object related to the promise is resolved.
So, you could use your code as
checkValue.then(function (data) {
// This is called when the XHR response comes in and
// defer.resolve() is called in the checkValue function.
});
But, there is a better approach to this. Using promise chaining.
Which essentially removes the need to create a new defer object.
function checkValue (value) {
return $http({
method: "get",
url: "someurl"
}).then(function (data) {
return data.response;
});
}
Explaining everything about how Promises work is may be too much for this post. There are already awesome works done on that.
But, basically, $http already returns a promise. So you can just use this promise instead of creating a new one. The end usage of the function remains exactly the same as the snippet above.
BTW, .success() handlers are deprecated. So better start using .then() on your apps already.
Since you are using Promise, you need to use .then callback in order to get response or error:
function checkValue (value) {
var defer = $q.defer();
var request = $http({
method: "get",
url: "someurl"
});
request.success(function (data) {
defer.resolve(data.response);
});
return defer.promise;
}
var promise = checkValue('Hello World');
promise.then(function(response) {
//success
console.log(response);
}, function(reason) {
//failed
console.log(reason);
});
then(successCallback, errorCallback, notifyCallback) – regardless of
when the promise was or will be resolved or rejected, then calls one
of the success or error callbacks asynchronously as soon as the result
is available. The callbacks are called with a single argument: the
result or rejection reason. Additionally, the notify callback may be
called zero or more times to provide a progress indication, before the
promise is resolved or rejected.
But the optimized approach would be using $http's inbuilt promise instead of creating a new one.
var request = $http({
method: 'GET',
url: '/someUrl'
});
request.then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
You should try this,
function checkValue (value) {
return $q(resolve, reject) {
$http({
method: "get",
url: "someurl"
}).success(function (data) {
resolve(data.response);
}).then (function (error) {
reject(error);
});
}
}
This will resolve your data
When using JQuery.Deferred is it OK to invoke reject() directly? Without having invoked a async function?
Perhaps I want some kind of test in the beginning of my async function. If the test fails I want to reject immediately. See the first if block below.
function doSomethingAsync() {
//Test if the ajax call should be invoked
var testFailed = true;
var dfd = $.Deferred();
//Check if test failed
if (testFailed) {
var asyncResult = {
success: false,
data: 'test failed'
};
//Is this OK usage of reject on the same thread?
dfd.reject(asyncResult);
return dfd.promise();
}
$.get('/api/testapi/get').done(function (data) {
var asyncResult = {
success: true,
data: data
};
dfd.resolve(asyncResult);
}).fail(function (err) {
var asyncResult = {
success: false,
data: err
};
dfd.reject(asyncResult);
});
return dfd.promise();
}
When using JQuery.Deferred is it OK to invoke reject() directly? Without having invoked a async function?
Yes, it's totally OK to return an already rejected promise, and to reject deferreds immediately. You only might need to verify that your callbacks don't rely on asynchronous resolution, which jQuery does not guarantee (in contrast to A+ implementations).
Notice that in your code you should use then instead of manually resolving the deferred:
function doSomethingAsync() {
var testFailed = /* Test if the ajax call should be invoked */;
var dfd = testFailed
? $.Deferred().reject('test failed')
: $.get('/api/testapi/get');
return dfd.then(function (data) {
return {
success: true,
data: data
};
}, function (err) {
return {
success: false,
data: err
};
});
}
You can do it quickly, as your function return a Promise object:
return Promise.reject('test failed');