• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"use strict";
2module.exports = function(Promise, tryConvertToPromise, NEXT_FILTER) {
3var util = require("./util");
4var CancellationError = Promise.CancellationError;
5var errorObj = util.errorObj;
6var catchFilter = require("./catch_filter")(NEXT_FILTER);
7
8function PassThroughHandlerContext(promise, type, handler) {
9    this.promise = promise;
10    this.type = type;
11    this.handler = handler;
12    this.called = false;
13    this.cancelPromise = null;
14}
15
16PassThroughHandlerContext.prototype.isFinallyHandler = function() {
17    return this.type === 0;
18};
19
20function FinallyHandlerCancelReaction(finallyHandler) {
21    this.finallyHandler = finallyHandler;
22}
23
24FinallyHandlerCancelReaction.prototype._resultCancelled = function() {
25    checkCancel(this.finallyHandler);
26};
27
28function checkCancel(ctx, reason) {
29    if (ctx.cancelPromise != null) {
30        if (arguments.length > 1) {
31            ctx.cancelPromise._reject(reason);
32        } else {
33            ctx.cancelPromise._cancel();
34        }
35        ctx.cancelPromise = null;
36        return true;
37    }
38    return false;
39}
40
41function succeed() {
42    return finallyHandler.call(this, this.promise._target()._settledValue());
43}
44function fail(reason) {
45    if (checkCancel(this, reason)) return;
46    errorObj.e = reason;
47    return errorObj;
48}
49function finallyHandler(reasonOrValue) {
50    var promise = this.promise;
51    var handler = this.handler;
52
53    if (!this.called) {
54        this.called = true;
55        var ret = this.isFinallyHandler()
56            ? handler.call(promise._boundValue())
57            : handler.call(promise._boundValue(), reasonOrValue);
58        if (ret === NEXT_FILTER) {
59            return ret;
60        } else if (ret !== undefined) {
61            promise._setReturnedNonUndefined();
62            var maybePromise = tryConvertToPromise(ret, promise);
63            if (maybePromise instanceof Promise) {
64                if (this.cancelPromise != null) {
65                    if (maybePromise._isCancelled()) {
66                        var reason =
67                            new CancellationError("late cancellation observer");
68                        promise._attachExtraTrace(reason);
69                        errorObj.e = reason;
70                        return errorObj;
71                    } else if (maybePromise.isPending()) {
72                        maybePromise._attachCancellationCallback(
73                            new FinallyHandlerCancelReaction(this));
74                    }
75                }
76                return maybePromise._then(
77                    succeed, fail, undefined, this, undefined);
78            }
79        }
80    }
81
82    if (promise.isRejected()) {
83        checkCancel(this);
84        errorObj.e = reasonOrValue;
85        return errorObj;
86    } else {
87        checkCancel(this);
88        return reasonOrValue;
89    }
90}
91
92Promise.prototype._passThrough = function(handler, type, success, fail) {
93    if (typeof handler !== "function") return this.then();
94    return this._then(success,
95                      fail,
96                      undefined,
97                      new PassThroughHandlerContext(this, type, handler),
98                      undefined);
99};
100
101Promise.prototype.lastly =
102Promise.prototype["finally"] = function (handler) {
103    return this._passThrough(handler,
104                             0,
105                             finallyHandler,
106                             finallyHandler);
107};
108
109
110Promise.prototype.tap = function (handler) {
111    return this._passThrough(handler, 1, finallyHandler);
112};
113
114Promise.prototype.tapCatch = function (handlerOrPredicate) {
115    var len = arguments.length;
116    if(len === 1) {
117        return this._passThrough(handlerOrPredicate,
118                                 1,
119                                 undefined,
120                                 finallyHandler);
121    } else {
122         var catchInstances = new Array(len - 1),
123            j = 0, i;
124        for (i = 0; i < len - 1; ++i) {
125            var item = arguments[i];
126            if (util.isObject(item)) {
127                catchInstances[j++] = item;
128            } else {
129                return Promise.reject(new TypeError(
130                    "tapCatch statement predicate: "
131                    + "expecting an object but got " + util.classString(item)
132                ));
133            }
134        }
135        catchInstances.length = j;
136        var handler = arguments[i];
137        return this._passThrough(catchFilter(catchInstances, handler, this),
138                                 1,
139                                 undefined,
140                                 finallyHandler);
141    }
142
143};
144
145return PassThroughHandlerContext;
146};
147