957182d093cafdd7266004ac00ce9ff27a2184d2
[aai/esr-gui.git] /
1 "use strict";
2 module.exports = function (Promise, apiRejection, tryConvertToPromise,
3     createContext) {
4     var TypeError = require("./errors.js").TypeError;
5     var inherits = require("./util.js").inherits;
6     var PromiseInspection = Promise.PromiseInspection;
7
8     function inspectionMapper(inspections) {
9         var len = inspections.length;
10         for (var i = 0; i < len; ++i) {
11             var inspection = inspections[i];
12             if (inspection.isRejected()) {
13                 return Promise.reject(inspection.error());
14             }
15             inspections[i] = inspection._settledValue;
16         }
17         return inspections;
18     }
19
20     function thrower(e) {
21         setTimeout(function(){throw e;}, 0);
22     }
23
24     function castPreservingDisposable(thenable) {
25         var maybePromise = tryConvertToPromise(thenable);
26         if (maybePromise !== thenable &&
27             typeof thenable._isDisposable === "function" &&
28             typeof thenable._getDisposer === "function" &&
29             thenable._isDisposable()) {
30             maybePromise._setDisposable(thenable._getDisposer());
31         }
32         return maybePromise;
33     }
34     function dispose(resources, inspection) {
35         var i = 0;
36         var len = resources.length;
37         var ret = Promise.defer();
38         function iterator() {
39             if (i >= len) return ret.resolve();
40             var maybePromise = castPreservingDisposable(resources[i++]);
41             if (maybePromise instanceof Promise &&
42                 maybePromise._isDisposable()) {
43                 try {
44                     maybePromise = tryConvertToPromise(
45                         maybePromise._getDisposer().tryDispose(inspection),
46                         resources.promise);
47                 } catch (e) {
48                     return thrower(e);
49                 }
50                 if (maybePromise instanceof Promise) {
51                     return maybePromise._then(iterator, thrower,
52                                               null, null, null);
53                 }
54             }
55             iterator();
56         }
57         iterator();
58         return ret.promise;
59     }
60
61     function disposerSuccess(value) {
62         var inspection = new PromiseInspection();
63         inspection._settledValue = value;
64         inspection._bitField = 268435456;
65         return dispose(this, inspection).thenReturn(value);
66     }
67
68     function disposerFail(reason) {
69         var inspection = new PromiseInspection();
70         inspection._settledValue = reason;
71         inspection._bitField = 134217728;
72         return dispose(this, inspection).thenThrow(reason);
73     }
74
75     function Disposer(data, promise, context) {
76         this._data = data;
77         this._promise = promise;
78         this._context = context;
79     }
80
81     Disposer.prototype.data = function () {
82         return this._data;
83     };
84
85     Disposer.prototype.promise = function () {
86         return this._promise;
87     };
88
89     Disposer.prototype.resource = function () {
90         if (this.promise().isFulfilled()) {
91             return this.promise().value();
92         }
93         return null;
94     };
95
96     Disposer.prototype.tryDispose = function(inspection) {
97         var resource = this.resource();
98         var context = this._context;
99         if (context !== undefined) context._pushContext();
100         var ret = resource !== null
101             ? this.doDispose(resource, inspection) : null;
102         if (context !== undefined) context._popContext();
103         this._promise._unsetDisposable();
104         this._data = null;
105         return ret;
106     };
107
108     Disposer.isDisposer = function (d) {
109         return (d != null &&
110                 typeof d.resource === "function" &&
111                 typeof d.tryDispose === "function");
112     };
113
114     function FunctionDisposer(fn, promise, context) {
115         this.constructor$(fn, promise, context);
116     }
117     inherits(FunctionDisposer, Disposer);
118
119     FunctionDisposer.prototype.doDispose = function (resource, inspection) {
120         var fn = this.data();
121         return fn.call(resource, resource, inspection);
122     };
123
124     function maybeUnwrapDisposer(value) {
125         if (Disposer.isDisposer(value)) {
126             this.resources[this.index]._setDisposable(value);
127             return value.promise();
128         }
129         return value;
130     }
131
132     Promise.using = function () {
133         var len = arguments.length;
134         if (len < 2) return apiRejection(
135                         "you must pass at least 2 arguments to Promise.using");
136         var fn = arguments[len - 1];
137         if (typeof fn !== "function") return apiRejection("fn must be a function\u000a\u000a    See http://goo.gl/916lJJ\u000a");
138
139         var input;
140         var spreadArgs = true;
141         if (len === 2 && Array.isArray(arguments[0])) {
142             input = arguments[0];
143             len = input.length;
144             spreadArgs = false;
145         } else {
146             input = arguments;
147             len--;
148         }
149         var resources = new Array(len);
150         for (var i = 0; i < len; ++i) {
151             var resource = input[i];
152             if (Disposer.isDisposer(resource)) {
153                 var disposer = resource;
154                 resource = resource.promise();
155                 resource._setDisposable(disposer);
156             } else {
157                 var maybePromise = tryConvertToPromise(resource);
158                 if (maybePromise instanceof Promise) {
159                     resource =
160                         maybePromise._then(maybeUnwrapDisposer, null, null, {
161                             resources: resources,
162                             index: i
163                     }, undefined);
164                 }
165             }
166             resources[i] = resource;
167         }
168
169         var promise = Promise.settle(resources)
170             .then(inspectionMapper)
171             .then(function(vals) {
172                 promise._pushContext();
173                 var ret;
174                 try {
175                     ret = spreadArgs
176                         ? fn.apply(undefined, vals) : fn.call(undefined,  vals);
177                 } finally {
178                     promise._popContext();
179                 }
180                 return ret;
181             })
182             ._then(
183                 disposerSuccess, disposerFail, undefined, resources, undefined);
184         resources.promise = promise;
185         return promise;
186     };
187
188     Promise.prototype._setDisposable = function (disposer) {
189         this._bitField = this._bitField | 262144;
190         this._disposer = disposer;
191     };
192
193     Promise.prototype._isDisposable = function () {
194         return (this._bitField & 262144) > 0;
195     };
196
197     Promise.prototype._getDisposer = function () {
198         return this._disposer;
199     };
200
201     Promise.prototype._unsetDisposable = function () {
202         this._bitField = this._bitField & (~262144);
203         this._disposer = undefined;
204     };
205
206     Promise.prototype.disposer = function (fn) {
207         if (typeof fn === "function") {
208             return new FunctionDisposer(fn, this, createContext());
209         }
210         throw new TypeError();
211     };
212
213 };