I have a test that requires one promise to be run and later in its then handler another promise returning function is run.
The first promise resolves, and a successful call is made to the next function which returns a promise. However, the then handler for that second promise never fires.
Is there another way to test nested promises using Jasmine 2.0?
Example problem:
describe("nested promise suite", function () {
var prom1 = function () {
var deferred = $q.defer();
$timeout(function () {
deferred.resolve('prom1');
}, 500)
return deferred.promise;
};
var prom2 = function () {
var deferred = $q.defer();
$timeout(function () {
deferred.resolve('prom2');
}, 500);
return deferred.promise;
};
iit("nested promise test", function (done) {
prom1()
.then(function (result) {
console.log('prom1 result ', result);
prom2()
.then(function (result2) {
console.log('prom2 result ', result2);
})
.finally(function() {
console.log('you did it');
})
})
.finally(done); //this causes promise one to resolve properly but unsure of how to make the second promise resolve properly
$timeout.flush();
})
});
I'm not sure if this is the problem in your original code as well, but in this example, your second console.log doesn't fire because prom2 adds a new timeout after the first has been flushed. The promise from prom2 then waits for this new timeout to flush, which never happens:
prom1() is called
The first promise and timeout are created
The first timeout is flushed, resolving the first promise
The then() block is triggered, calling prom2()
The second promise and timeout are created
done() is called.
You can try adding a second $timeout.flush(); call right after the one that's already there, which will flush the second timeout, resolve the second promise, and log the missing messages.
Related
In javascripts webconsole in my browser I read the Error message:
TypeError: A promise cannot be resolved with itself
which however does not furnish a source code line number or other reference, giving me trouble to even now where that type Error occurs. In all honesty debuging asynchronous things (as are par excellence Promises) turns out to be a challeng. Any idea how I can track down, what and where this TypeError happens?
What I have done is to be able to provoke the same error message
using this code:
var promise = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(promise);
},0);
});
But this does only confirm that such a thing as resolving
a promise with itself is indeed something that can occur
and which leads subsequenty to the promise being rejected
due to the occuring error;
console.log(promise);
// prints: Promise { <state>: "rejected" }
An ideal answer would provide some sort of guide (steps)
that help to show which part of the javascript source code
is actually causes the error.
A helpful answer could also list simply all the ways (other
than the one I have provided example code for) that can lead
to the very same error.
Updated
The only place in the source, where a new Promise(callback) occurs looks like this, and appears to me cannot cause a circular pattern.
function xhrGet(url){
return new Promise((resolve,reject)=>{
var xhr = new XMLHttpRequest();
xhr.open("GET",url);
xhr.responseType = "arraybuffer";
xhr.addEventListener("load",function(){
resolve(xhr.response);
});
xhr.addEventListener("error",function(er){
reject(er);
});
xhr.send();
});
}
It seems imho, unlikely to be the culprit.
here a screenshot of the console output of the debug window of Firefox(ver 68) showing that it sadly does not always show the line number
When resolve in a Promise constructor is called with a different Promise, the value that the constructed Promise resolves to will be what that Promise passed into resolve resolves to.
const existingProm = new Promise(resolve => {
setTimeout(() => {
console.log('existing promise resolving');
resolve();
}, 2000);
});
var secondPromise = new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("second promise's resolve being called");
resolve(existingProm);
},1000);
});
secondPromise.then(() => {
console.log('second promise fulfilled');
});
As you can see in the above snippet, although secondPromise's resolve is called after 1000 ms, the secondPromise only actually resolves after 2000 ms, because existingProm (which resolves after 2000 ms) was passed to the resolve.
It's just like returning a Promise from inside a .then - the next .then will resolve to the value of what the returned Promise resolved to.
But if you pass the Promise you're constructing to the same resolve that's being used to construct the Promise, you've created a cycle - resolve(promise); will mean that the constructed Promise resolves only once promise resolves. But promise is that constructed Promise. The interpreter sees this circular situation and throws an error (by rejecting the Promise).
Any sort of circular Promise cycle can result in this error. For example, if prom1 calls its resolve with prom2, and prom2 calls its resolve with prom1:
const prom1 = new Promise(resolve => {
setTimeout(() => {
console.log('prom1 resolve being called');
resolve(prom1);
}, 1000);
});
const prom2 = new Promise(resolve => {
setTimeout(() => {
console.log('prom2 resolve being called');
resolve(prom2);
}, 1000);
});
prom1.catch((e) => console.log(e.message));
To debug an error like this, look at the Promise that generates the error, and examine what its resolve was called with, or what was returned from the .then that created the Promise. You have a circular chain in there somewhere. For example:
const prom1 = new Promise(resolve => {
setTimeout(() => {
console.log('prom1 resolve being called');
resolve(prom1);
}, 1000);
});
const prom2 = new Promise(resolve => {
setTimeout(() => {
console.log('prom2 resolve being called');
resolve(prom2);
}, 1000);
});
prom1.catch((e) => console.dir(e.stack));
On my machine, this shows the error as occurring
at https://stacksnippets.net/js:16:5
which corresponds to this line:
resolve(prom1);
which is indeed the source of the circular dependency for prom1.
I'm having a (seemingly fundamental) problem understanding promises. First the code:
'use strict';
var Q = require("q");
var mockPromise = function (statement) {
var deferred = Q.defer();
console.log("I'm running before I'm queued ...");
setTimeout(function () {
deferred.resolve(statement);
}, 5000);
return deferred.promise;
};
var promises = [
mockPromise("1st statement"),
mockPromise("2nd statement"),
mockPromise("3rd statement")
];
Q.all(promises)
.then(function (results) {
console.log(results);
});
Each promise function gets invoked upon adding it to the promise array, as opposed to when Q.all is called as I thought.
What am I not getting here?
How do I queue an array of promises without immediately invoking said promises?
Promises are objects. They are not 'executed'. They are 'resolved' or 'rejected'. When you create the array, you are executing the mockPromise() function three times. This function is immediately executed in that point of the code.
The mockPromise() function creates a deferred and returns the associated promise. It also sets a timer to resolve the returned promise in the future.
Q.all() just waits for the 3 promises to be 'resolved'. (technically it returns a new promise that will be resolved when the 3 previous promises are resolved)
If you want to execute the three async functions one after the other, I would recommend using the excellent async.js library. It provides many async flow control primitives. In your case you may be interested in series or waterfall methods.
It seems the confusion is that you understand the promise API to be designed for lazy evaluation, which is not the case.
Promises are a way of handling long running requests, they were designed to start IMMEDIATELY to minimize waiting time, and to utilize chaining and joining to clarify how the results of these long running requests should be processed.
You might try to utilize the api Q-Lazy which allows you to delay invocation of promises until they have been subscribed to.
You'd normally defer asynchronous functionality, not just a value. For example:
'use strict';
var Q = require("q");
var mockPromise = function (statement) {
var deferred = Q.defer();
console.log("I'm running before I'm queued ...");
setTimeout(function () {
deferred.resolve(statement());
}, 5000);
return deferred.promise;
};
var promises = [
mockPromise(function() {
console.log("running1");
return "1st statement";
}),
mockPromise(function() {
console.log("running2");
return "2nd statement";
}),
mockPromise(function() {
console.log("running3");
return "3rd statement";
}),
];
Q.all(promises)
.then(function (results) {
console.log(results);
});
Note that the deferred functionality is going to run regardless of whether you ever call a .then on a promise.
Let me show a sample using standard promises. They work pretty much the same as Q promises:
function mockPromise(value) {
return new Promise(resolve => {
console.log("I'm not running before I'm queued ...");
setTimeout(() => {
resolve(value);
}, 1000);
});
}
mockPromise("1st promise").then(x => {
console.log(x);
return mockPromise("2nd promise");
}).then(x => {
console.log(x);
return mockPromise("3nd promise");
}).then(x => {
console.log(x);
});
I'm trying to setup a test which involves promises. Here is my example code:
var promise;
beforeEach(inject(function ($q) {
promise = $q.resolve();
}));
it('should resolve', function (done) {
promise.then(function () {
expect(true).toBeTruthy();
done();
});
});
For some reason, when I run this, I get a TIMEOUT
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Why doesn't the promise execute the callback given to then ?
Cheers
You need to call scope/rootScope $digest method to resolve promises.
So it should be:
var result = false;
promise.then(function() { result = true;});
$rootScope.$digest();
expect(result).toBeTruthy();
I'm trying to understand Promise. But here I'm confused.
I want to create a test function that will print 3000 after 3 second, then print 2000 after 2 second, then print 1000 after 1 second. Here is my code:
'use strict';
var Q = require('q');
function delayConsole(timeOut) {
var defer = Q.defer();
setTimeout(function(){
console.log(timeOut);
defer.resolve(2000);
},timeOut);
return defer.promise;
}
// This works
delayConsole(3000).then(function(){
return delayConsole(2000);
}).then(function(){
return delayConsole(1000);
});
// This doesn't work. Why?
delayConsole(3000).then(delayConsole(2000)).then(delayConsole(1000));
There, you call the function delayConsole immediately :
.then(delayConsole(2000))
That is : you don't pass the function but the result of the function call, you don't wait for the promises to be chained.
When you do
then(function(){
return delayConsole(2000);
})
then you pass a function, not the result of that function call. The function can be called when the previous element in the promise chain is solved.
I just thought I'd share that you can make this construction work which is sometimes easier to use:
promise.then(delayConsole(3000)).then(delayConsole(2000)).then(delayConsole(1000));
by changing delayConsole() to this:
function delayConsole(timeOut) {
return function() {
var defer = Q.defer();
setTimeout(function(){
console.log(timeOut);
defer.resolve(2000);
},timeOut);
return defer.promise;
}
}
This way, calling delayConsole() just captures the timeout argument and returns a function that can be called later by the promise .then handler. So, you are still passing a function reference to the .then() handler which lets the promise engine call the internal function sometime later rather than execute it now.
In the following code, attenCalendar.refresh(); method calls a sequence of requests/responses and executes asynchronously. It needs to be finished in order to attenCalendar.getItem be successful. I used promises to solve it, but apparently promise resolves before the refresh completes and causes to attenCalendar.getItem returns null.
How can I block the sequence till refresh() finishes?
yield promise_attendee1_assert();
function attendeeRefresh(){
let deferred = Promise.defer();
attenCalendar.refresh();
deferred.resolve("refreshed");
return deferred.promise;
}
function promise_attendee1_assert(){
let deferred = Promise.defer();
let promise = attendeeRefresh();
promise.then(function onRefreshed(refreshNote) {
dump("refreshedattendee"+refreshNote);
attenCalendar.getItem(attendItem.id, {
onGetResult: function onGetResult(cal, stat, type, detail, count, items) {
retrievedItem = items[0];
//prints null
dump("\nattendeeitem:"+retrievedItem.title);
},
onOperationComplete: function() {
deferred.resolve();
}
});
});
return deferred.promise;
}