-
Notifications
You must be signed in to change notification settings - Fork 1
/
can-reflect-promise.js
136 lines (123 loc) · 3.76 KB
/
can-reflect-promise.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
"use strict";
var canReflect = require("can-reflect");
var canSymbol = require("can-symbol");
var ObservationRecorder = require("can-observation-recorder");
var queues = require("can-queues");
var KeyTree = require("can-key-tree");
var dev = require("can-log/dev/dev");
var getKeyValueSymbol = canSymbol.for("can.getKeyValue"),
observeDataSymbol = canSymbol.for("can.meta");
var promiseDataPrototype = {
isPending: true,
state: "pending",
isResolved: false,
isRejected: false,
value: undefined,
reason: undefined
};
function setVirtualProp(promise, property, value) {
var observeData = promise[observeDataSymbol];
var old = observeData[property];
observeData[property] = value;
queues.enqueueByQueue(observeData.handlers.getNode([property]), promise, [value,old], function() {
return {};
},["Promise", promise, "resolved with value", value, "and changed virtual property: "+property]);
}
function initPromise(promise) {
var observeData = promise[observeDataSymbol];
if(!observeData) {
Object.defineProperty(promise, observeDataSymbol, {
enumerable: false,
configurable: false,
writable: false,
value: Object.create(promiseDataPrototype)
});
observeData = promise[observeDataSymbol];
observeData.handlers = new KeyTree([Object, Object, Array]);
}
promise.then(function(value){
queues.batch.start();
setVirtualProp(promise, "isPending", false);
setVirtualProp(promise, "isResolved", true);
setVirtualProp(promise, "value", value);
setVirtualProp(promise, "state", "resolved");
queues.batch.stop();
}, function(reason){
queues.batch.start();
setVirtualProp(promise, "isPending", false);
setVirtualProp(promise, "isRejected", true);
setVirtualProp(promise, "reason", reason);
setVirtualProp(promise, "state", "rejected");
queues.batch.stop();
//!steal-remove-start
if (process.env.NODE_ENV !== 'production') {
dev.error("Failed promise:", reason);
}
//!steal-remove-end
});
}
function setupPromise(value) {
var oldPromiseFn;
var proto = "getPrototypeOf" in Object ? Object.getPrototypeOf(value) : value.__proto__; //jshint ignore:line
if(value[getKeyValueSymbol] && value[observeDataSymbol]) {
// promise has already been set up. Don't overwrite.
return;
}
if(proto === null || proto === Object.prototype) {
// promise type is a plain object or dictionary. Set up object instead of proto.
proto = value;
if(typeof proto.promise === "function") {
// Duck-type identification as a jQuery.Deferred;
// In that case, the promise() function returns a new object
// that needs to be decorated.
oldPromiseFn = proto.promise;
proto.promise = function() {
var result = oldPromiseFn.call(proto);
setupPromise(result);
return result;
};
}
}
canReflect.assignSymbols(proto, {
"can.getKeyValue": function(key) {
if(!this[observeDataSymbol]) {
initPromise(this);
}
ObservationRecorder.add(this, key);
switch(key) {
case "state":
case "isPending":
case "isResolved":
case "isRejected":
case "value":
case "reason":
return this[observeDataSymbol][key];
default:
return this[key];
}
},
"can.getValue": function() {
return this[getKeyValueSymbol]("value");
},
"can.isValueLike": false,
"can.onKeyValue": function(key, handler, queue) {
if(!this[observeDataSymbol]) {
initPromise(this);
}
this[observeDataSymbol].handlers.add([key, queue || "mutate", handler]);
},
"can.offKeyValue": function(key, handler, queue) {
if(!this[observeDataSymbol]) {
initPromise(this);
}
this[observeDataSymbol].handlers.delete([key, queue || "mutate", handler]);
},
"can.hasOwnKey": function(key) {
if (!this[observeDataSymbol]) {
initPromise(this);
}
return (key in this[observeDataSymbol]);
}
});
}
module.exports = setupPromise;