Introduction
I have a question coming from this one:
Loop calling an asynchronous function
Once I got a loop that calls an asynchronous function using a promises-like pattern (jsdeferred library in an add-on developed with SDK).
var sess = ...;
Deferred.define(this);
function asyncFunction (session) {
Deferred.next(function() {
var d = new Deferred();
(new Request({
url: form.php,
content: "sess=" + session,
onComplete: function (response) {
d.call(response.json);
}
})).get();
return d;
}).next(function(resp) {
if (resp.id) {
asyncFunction(session);
console.log(resp.msg);
}
});
}
asyncFunction(sess);
I want to chain a new asynchrnous function into this request, for example, opening a browser's tab: I don't want to continue the execution until is ready.
Problem
So I get to this:
var sess = ...;
Deferred.define(this);
function asyncFunction (session) {
Deferred.next(function() {
var d = new Deferred();
(new Request({
url: form.php,
content: "sess=" + session,
onComplete: function (response) {
d.call(response.json);
}
})).get();
return d;
})
.next(function(resp) {
var d = new Deferred();
// Load tab
tabs.activeTab.url = 'http://foo.com';
tabs.activeTab.on('ready', function() {
console.log(resp.id);
d.call(resp.id);
});
return d;
})
.next(function(resp) {
if (resp.id) {
asyncFunction(session);
console.log(resp.msg);
}
});
}
asyncFunction(sess);
And the result that I get is that, for each iteration of the loop, it runs all the previous requests again! So I got the following log in my console:
id1
msg1
id1
msg1
id2
msg2
id1
msg1
id2
msg2
id3
msg3
...
Obviously what I expected was:
id1
msg1
id2
msg2
id3
msg3
Question
It's crystal clear that I don't understand the promises pattern. I've read a lot in two APIs:
The first is the one I've been using until now. The second one gives an error when I try to require the library:
const { defer } = require('api-utils/promise'); (+)
So, my question is if you know any good tutorial of promises or some good example about chaining multiple asynchronous functions. Or if you know how to do it, how would you do it.
Thank you
EDIT: The error that gives the line (+)
error: An exception occurred.
Traceback (most recent call last):
File "resource://ares-at-iiia-dot-csic/api-utils/lib/promise.js", line 16, in
}.call(this, function(require, exports) {
File "resource://ares-at-iiia-dot-csic/api-utils/lib/promise.js", line 6, in return function promised
if (typeof(define) === 'function') { // RequireJS
File "function promisedConcat(promises, unknown) {return promises.then(function (values) {return resolve(unknown).then(function (valu
e) {return values.concat(value);});});", line NaN, in function promisedConcat
File "function execute(args) {return call.apply(call, args);", line NaN, in function execute
File "exports.reject = reject;var promised = function () {var call = Function.call;var concat = Array.prototype.concat", line NaN, in
exports.reject = reject;var promised = function
File "function reject(reason, prototype) {var deferred = defer(prototype);deferred.reject(reason);return deferred.promise;", line NaN
, in function reject
File "exports.resolve = resolve", line NaN, in exports.resolve = resolve
File "function resolve(value, prototype) {var deferred = defer(prototype);deferred.resolve(value);return deferred.promise;", line NaN
, in function resolve
File "exports.defer = defer", line NaN, in exports.defer = defer
File "if (pending) {pending.push([resolved, rejected]);} else {result.then(resolved, rejected);}return deferred.promise;}}});var defe
rred = {promise: promise, resolve: function resolve(value) {if (pending) {result = isPromise(value) ? value : resolution(value);while (
pending.length) {result.then.apply(result, pending.shift());}pending = null;}}, reject", line NaN, in if
File "function rejected(reason) {deferred.resolve(reject(reason));", line NaN, in function rejected
File "function resolved(value) {deferred.resolve(resolve(value));", line NaN, in function resolved
File "function defer(prototype) {var pending = [], result;prototype = prototype || prototype === null ? prototype : Object.prototype;
var promise = Object.create(prototype, {then: {value: function then(resolve, reject) {var deferred = defer(prototype);resolve = resolve
? attempt(resolve) : resolution;reject = reject ? attempt(reject) ", line NaN, in function defer
File "function isPromise(value) {return value && typeof value.then === "function";", line NaN, in function isPromise
File "function attempt(f) {return function effort(options) {try {return f(options);} catch (error) {return rejection(error);}};", lin
e NaN, in function attempt
File "function rejection(reason) {return {then", line NaN, in function rejection
File "function resolution(value) {return {then", line NaN, in function resolution
File "((function (require, exports) {"use strict"", line NaN, in
File "resource://ares-at-iiia-dot-csic/api-utils/lib/globals!.js", line 75, in getter
value: define.bind(this)
TypeError: can't redefine non-configurable property 'define'
Hm, looks a bit like a scoping issue. In your last function, there should be no variable resp
available - I'm not sure what it really logs.
And yes, because you call asyncFunction
once again in the end, as long as there is an id in the response, you might get an endless loop.
However, lets try to use the add-on sdk's promises (and no, I've never used them before - but it says it was based on CommonJS Promises/A):
const { defer } = require('api-utils/promise');
// or whatever you need in your code to use them - I'm no addOn-developer
var sess = ...;
Deferred.define(this);
function asyncFunction (session) {
/* returns a promise itself */
var d = defer();
(new Request({
url: form.php,
content: "sess=" + session,
onComplete: function (response) {
d.resolve(response.json);
}
})).get();
return d.promise.then(function(resp) {
var d = defer();
// Load tab
tabs.activeTab.url = 'http://foo.com';
tabs.activeTab.on('ready', function() {
d.resolve(resp);
});
return d.promise;
}).then(function(resp) {
console.log("request #"+resp.id+"sucessful: "+resp.msg);
if (resp.id) {
asyncFunction(session); // call recursively
}
});
}
asyncFunction(sess);