Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
/**
2
 Copyright 2018 Google Inc. All rights reserved.
3
 
4
 Licensed under the Apache License, Version 2.0 (the "License");
5
 you may not use this file except in compliance with the License.
6
 You may obtain a copy of the License at:
7
 
8
 http://www.apache.org/licenses/LICENSE-2.0
9
 
10
 Unless required by applicable law or agreed to in writing, software
11
 distributed under the License is distributed on an "AS IS" BASIS,
12
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 See the License for the specific language governing permissions and
14
 limitations under the License.
15
 */
16
 
17
#import "FBLPromisePrivate.h"
18
 
19
/** All states a promise can be in. */
20
typedef NS_ENUM(NSInteger, FBLPromiseState) {
21
  FBLPromiseStatePending = 0,
22
  FBLPromiseStateFulfilled,
23
  FBLPromiseStateRejected,
24
};
25
 
26
typedef void (^FBLPromiseObserver)(FBLPromiseState state, id __nullable resolution);
27
 
28
static dispatch_queue_t gFBLPromiseDefaultDispatchQueue;
29
 
30
@implementation FBLPromise {
31
  /** Current state of the promise. */
32
  FBLPromiseState _state;
33
  /**
34
   Set of arbitrary objects to keep strongly while the promise is pending.
35
   Becomes nil after the promise has been resolved.
36
   */
37
  NSMutableSet *__nullable _pendingObjects;
38
  /**
39
   Value to fulfill the promise with.
40
   Can be nil if the promise is still pending, was resolved with nil or after it has been rejected.
41
   */
42
  id __nullable _value;
43
  /**
44
   Error to reject the promise with.
45
   Can be nil if the promise is still pending or after it has been fulfilled.
46
   */
47
  NSError *__nullable _error;
48
  /** List of observers to notify when the promise gets resolved. */
49
  NSMutableArray<FBLPromiseObserver> *_observers;
50
}
51
 
52
+ (void)initialize {
53
  if (self == [FBLPromise class]) {
54
    gFBLPromiseDefaultDispatchQueue = dispatch_get_main_queue();
55
  }
56
}
57
 
58
+ (dispatch_queue_t)defaultDispatchQueue {
59
  @synchronized(self) {
60
    return gFBLPromiseDefaultDispatchQueue;
61
  }
62
}
63
 
64
+ (void)setDefaultDispatchQueue:(dispatch_queue_t)queue {
65
  NSParameterAssert(queue);
66
 
67
  @synchronized(self) {
68
    gFBLPromiseDefaultDispatchQueue = queue;
69
  }
70
}
71
 
72
+ (instancetype)pendingPromise {
73
  return [[self alloc] initPending];
74
}
75
 
76
+ (instancetype)resolvedWith:(nullable id)resolution {
77
  return [[self alloc] initWithResolution:resolution];
78
}
79
 
80
- (void)fulfill:(nullable id)value {
81
  if ([value isKindOfClass:[NSError class]]) {
82
    [self reject:(NSError *)value];
83
  } else {
84
    @synchronized(self) {
85
      if (_state == FBLPromiseStatePending) {
86
        _state = FBLPromiseStateFulfilled;
87
        _value = value;
88
        _pendingObjects = nil;
89
        for (FBLPromiseObserver observer in _observers) {
90
          observer(_state, _value);
91
        }
92
        _observers = nil;
93
        dispatch_group_leave(FBLPromise.dispatchGroup);
94
      }
95
    }
96
  }
97
}
98
 
99
- (void)reject:(NSError *)error {
100
  NSAssert([error isKindOfClass:[NSError class]], @"Invalid error type.");
101
 
102
  if (![error isKindOfClass:[NSError class]]) {
103
    // Give up on invalid error type in Release mode.
104
    @throw error;  // NOLINT
105
  }
106
  @synchronized(self) {
107
    if (_state == FBLPromiseStatePending) {
108
      _state = FBLPromiseStateRejected;
109
      _error = error;
110
      _pendingObjects = nil;
111
      for (FBLPromiseObserver observer in _observers) {
112
        observer(_state, _error);
113
      }
114
      _observers = nil;
115
      dispatch_group_leave(FBLPromise.dispatchGroup);
116
    }
117
  }
118
}
119
 
120
#pragma mark - NSObject
121
 
122
- (NSString *)description {
123
  if (self.isFulfilled) {
124
    return [NSString stringWithFormat:@"<%@ %p> Fulfilled: %@", NSStringFromClass([self class]),
125
                                      self, self.value];
126
  }
127
  if (self.isRejected) {
128
    return [NSString stringWithFormat:@"<%@ %p> Rejected: %@", NSStringFromClass([self class]),
129
                                      self, self.error];
130
  }
131
  return [NSString stringWithFormat:@"<%@ %p> Pending", NSStringFromClass([self class]), self];
132
}
133
 
134
#pragma mark - Private
135
 
136
- (instancetype)initPending {
137
  self = [super init];
138
  if (self) {
139
    dispatch_group_enter(FBLPromise.dispatchGroup);
140
  }
141
  return self;
142
}
143
 
144
- (instancetype)initWithResolution:(nullable id)resolution {
145
  self = [super init];
146
  if (self) {
147
    if ([resolution isKindOfClass:[NSError class]]) {
148
      _state = FBLPromiseStateRejected;
149
      _error = (NSError *)resolution;
150
    } else {
151
      _state = FBLPromiseStateFulfilled;
152
      _value = resolution;
153
    }
154
  }
155
  return self;
156
}
157
 
158
- (void)dealloc {
159
  if (_state == FBLPromiseStatePending) {
160
    dispatch_group_leave(FBLPromise.dispatchGroup);
161
  }
162
}
163
 
164
- (BOOL)isPending {
165
  @synchronized(self) {
166
    return _state == FBLPromiseStatePending;
167
  }
168
}
169
 
170
- (BOOL)isFulfilled {
171
  @synchronized(self) {
172
    return _state == FBLPromiseStateFulfilled;
173
  }
174
}
175
 
176
- (BOOL)isRejected {
177
  @synchronized(self) {
178
    return _state == FBLPromiseStateRejected;
179
  }
180
}
181
 
182
- (nullable id)value {
183
  @synchronized(self) {
184
    return _value;
185
  }
186
}
187
 
188
- (NSError *__nullable)error {
189
  @synchronized(self) {
190
    return _error;
191
  }
192
}
193
 
194
- (void)addPendingObject:(id)object {
195
  NSParameterAssert(object);
196
 
197
  @synchronized(self) {
198
    if (_state == FBLPromiseStatePending) {
199
      if (!_pendingObjects) {
200
        _pendingObjects = [[NSMutableSet alloc] init];
201
      }
202
      [_pendingObjects addObject:object];
203
    }
204
  }
205
}
206
 
207
- (void)observeOnQueue:(dispatch_queue_t)queue
208
               fulfill:(FBLPromiseOnFulfillBlock)onFulfill
209
                reject:(FBLPromiseOnRejectBlock)onReject {
210
  NSParameterAssert(queue);
211
  NSParameterAssert(onFulfill);
212
  NSParameterAssert(onReject);
213
 
214
  @synchronized(self) {
215
    switch (_state) {
216
      case FBLPromiseStatePending: {
217
        if (!_observers) {
218
          _observers = [[NSMutableArray alloc] init];
219
        }
220
        [_observers addObject:^(FBLPromiseState state, id __nullable resolution) {
221
          dispatch_group_async(FBLPromise.dispatchGroup, queue, ^{
222
            switch (state) {
223
              case FBLPromiseStatePending:
224
                break;
225
              case FBLPromiseStateFulfilled:
226
                onFulfill(resolution);
227
                break;
228
              case FBLPromiseStateRejected:
229
                onReject(resolution);
230
                break;
231
            }
232
          });
233
        }];
234
        break;
235
      }
236
      case FBLPromiseStateFulfilled: {
237
        dispatch_group_async(FBLPromise.dispatchGroup, queue, ^{
238
          onFulfill(self->_value);
239
        });
240
        break;
241
      }
242
      case FBLPromiseStateRejected: {
243
        dispatch_group_async(FBLPromise.dispatchGroup, queue, ^{
244
          onReject(self->_error);
245
        });
246
        break;
247
      }
248
    }
249
  }
250
}
251
 
252
- (FBLPromise *)chainOnQueue:(dispatch_queue_t)queue
253
              chainedFulfill:(FBLPromiseChainedFulfillBlock)chainedFulfill
254
               chainedReject:(FBLPromiseChainedRejectBlock)chainedReject {
255
  NSParameterAssert(queue);
256
 
257
  FBLPromise *promise = [[FBLPromise alloc] initPending];
258
  __auto_type resolver = ^(id __nullable value) {
259
    if ([value isKindOfClass:[FBLPromise class]]) {
260
      [(FBLPromise *)value observeOnQueue:queue
261
          fulfill:^(id __nullable value) {
262
            [promise fulfill:value];
263
          }
264
          reject:^(NSError *error) {
265
            [promise reject:error];
266
          }];
267
    } else {
268
      [promise fulfill:value];
269
    }
270
  };
271
  [self observeOnQueue:queue
272
      fulfill:^(id __nullable value) {
273
        value = chainedFulfill ? chainedFulfill(value) : value;
274
        resolver(value);
275
      }
276
      reject:^(NSError *error) {
277
        id value = chainedReject ? chainedReject(error) : error;
278
        resolver(value);
279
      }];
280
  return promise;
281
}
282
 
283
@end
284
 
285
@implementation FBLPromise (DotSyntaxAdditions)
286
 
287
+ (instancetype (^)(void))pending {
288
  return ^(void) {
289
    return [self pendingPromise];
290
  };
291
}
292
 
293
+ (instancetype (^)(id __nullable))resolved {
294
  return ^(id resolution) {
295
    return [self resolvedWith:resolution];
296
  };
297
}
298
 
299
@end