[prev in list] [next in list] [prev in thread] [next in thread] 

List:       squeak-dev
Subject:    [squeak-dev] The Trunk: Kernel-fbs.762.mcz
From:       commits () source ! squeak ! org
Date:       2013-05-30 22:08:14
[Download RAW message or body]

Frank Shearar uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-fbs.762.mcz

==================== Summary ====================

Name: Kernel-fbs.762
Author: fbs
Time: 30 May 2013, 11:07:50.674 pm
UUID: 189ac08f-d45b-40f8-b7cd-e183ae46f3bf
Ancestors: Kernel-fbs.761

Turn Promise into a fully chainable object with decent error handling.

(This implements the Smalltalk equivalent of JavaScript's Promises/A+ specification.)

=============== Diff against Kernel-fbs.761 ===============

Item was changed:
  Object subclass: #Promise
+ 	instanceVariableNames: 'onError value resolvers mutex state error rejectors \
                rejecters'
- 	instanceVariableNames: 'isResolved value resolvers mutex'
  	classVariableNames: ''
  	poolDictionaries: ''
  	category: 'Kernel-Processes'!
  
+ !Promise commentStamp: 'fbs 5/17/2013 18:23' prior: 0!
- !Promise commentStamp: 'jcg 12/17/2009 02:22' prior: 0!
  I represent the result of an asynchronous message.  Once the message is processed, \
I will be resolved to a value.  I am typically instantiated by invocations of \
#futureSend:at:args: (and not by #futureDo:atArgs:).  
+ See class-comment of FutureNode.
+ 
+ I also implement the Promises/A+ Javascript specification. This allows you to chain \
my instances to perform arbitrarily complex asynchronous tasks with error handling \
baked in. + 
+ A Promise may be in one of three possible states: #pending, #fulfilled or \
#rejected. A Promise may move from #pending -> #fulfilled, or from #pending -> \
#rejected. No other state changes may occur. Once #fulfilled or #rejected, a \
                Promise's value must change.!
- See class-comment of FutureNode.!

Item was added:
+ ----- Method: Promise class>>ifRejected: (in category 'instance creation') -----
+ ifRejected: aBlock
+ 	^ Promise basicNew initializeWithIfRejected: aBlock.!

Item was added:
+ ----- Method: Promise class>>unit: (in category 'instance creation') -----
+ unit: anObject
+ 	"Return a resolved Promise. #new is the other half of Promise's unit function; \
#new returns an unresolved Promise." + 	^ Promise basicNew \
initializeWithResolvedValue: anObject.!

Item was added:
+ ----- Method: Promise>>error (in category 'accessing') -----
+ error
+ 	^ error.!

Item was added:
+ ----- Method: Promise>>evaluateRejecter: (in category 'private') -----
+ evaluateRejecter: rejecterBlock
+ 	^ rejecterBlock cull: error.!

Item was changed:
  ----- Method: Promise>>evaluateResolver: (in category 'private') -----
  evaluateResolver: resolverBlock
+ 	^ resolverBlock cull: value.!
- 	resolverBlock cull: value.!

Item was added:
+ ----- Method: Promise>>ifRejected: (in category 'monad') -----
+ ifRejected: errBlock
+ 	^ self then: [:ignored | "Do nothing"] ifRejected: errBlock.!

Item was changed:
  ----- Method: Promise>>initialize (in category 'initialize') -----
  initialize
+ 	state := #pending.
- 	isResolved := false.
  	resolvers := #().
+ 	rejecters := #().
  	mutex := Mutex new.!

Item was added:
+ ----- Method: Promise>>initializeWithIfRejected: (in category 'initialize') -----
+ initializeWithIfRejected: aBlock
+ 	self initialize.
+ 	rejecters := {aBlock}.!

Item was added:
+ ----- Method: Promise>>initializeWithResolvedValue: (in category 'initialize') \
----- + initializeWithResolvedValue: anObject
+ 	self initialize.
+ 	self resolveWith: anObject.!

Item was added:
+ ----- Method: Promise>>isPromise (in category 'testing') -----
+ isPromise
+ 	^ true.!

Item was added:
+ ----- Method: Promise>>isRejected (in category 'testing') -----
+ isRejected
+ 	^ state == #rejected.!

Item was changed:
  ----- Method: Promise>>isResolved (in category 'testing') -----
  isResolved
+ 	^ state == #fulfilled.!
- 	^isResolved!

Item was added:
+ ----- Method: Promise>>printOn: (in category 'printing') -----
+ printOn: aStream
+ 	aStream nextPutAll: 'a Promise'.
+ 	self isResolved ifTrue: [
+ 		aStream
+ 			nextPutAll: '(resolved: ';
+ 			nextPutAll: value printString;
+ 			nextPutAll: ')'].
+ 	self isRejected ifTrue: [
+ 		aStream
+ 			nextPutAll: '(rejected: ';
+ 			nextPutAll: error printString;
+ 			nextPutAll: ')'].!

Item was added:
+ ----- Method: Promise>>rejectWith: (in category 'resolving') -----
+ rejectWith: anObject
+ 	"Reject this promise."
+ 	mutex critical: [
+ 		(state == #fulfilled) ifTrue: [self error: 'Promise was already resolved'].
+ 		(state == #rejected) ifTrue: [self error: 'Promise was already rejected'].
+ 		error := anObject.
+ 		state := #rejected.
+ 		rejecters do: [:r | self evaluateRejecter: r]].!

Item was changed:
  ----- Method: Promise>>resolveWith: (in category 'resolving') -----
  resolveWith: arg
  	"Resolve this promise"
  	mutex critical: [
+ 		(state == #fulfilled) ifTrue: [self error: 'Promise was already resolved'].
+ 		(state == #rejected) ifTrue: [self error: 'Promise was already resolved'].
- 		isResolved ifTrue: [self error: 'Promise was already resolved'].
  		value := arg.
+ 		state := #fulfilled.
+ 		resolvers do: [:r |
+ 			self evaluateResolver: r]].!
- 		isResolved := true.
- 		resolvers do: [:r | self evaluateResolver: r].
- 	].!

Item was added:
+ ----- Method: Promise>>then: (in category 'monad') -----
+ then: resolvedBlock
+ 	^ self then: resolvedBlock ifRejected: [:ignored | "Do nothing"].!

Item was added:
+ ----- Method: Promise>>then:ifRejected: (in category 'monad') -----
+ then: resolvedBlock ifRejected: errBlock
+ 	"Return a Promise that, if it resolves, runs the resolvedBlock. If resolution \
throws an Exception, it runs the errBlock." + 	| p |
+ 	p := Promise new.
+ 	self whenResolved: [:v |
+ 		[p resolveWith: (resolvedBlock value: v)]
+ 			on: Error do: [:e | p rejectWith: e]].
+ 	self whenRejected: [:e | p rejectWith: (errBlock value: e)].
+ 	^ p.!

Item was changed:
  ----- Method: Promise>>waitTimeoutMSecs: (in category 'waiting') -----
  waitTimeoutMSecs: msecs
  	"Wait for at most the given number of milliseconds for this promise to resolve. \
Answer true if it is resolved, false otherwise."  | sema delay |
  	sema := Semaphore new.
  	self whenResolved: [sema signal].
  	delay := Delay timeoutSemaphore: sema afterMSecs: msecs.
  	[sema wait] ensure: [delay unschedule].
+ 	^ self isResolved.!
- 	^isResolved!

Item was added:
+ ----- Method: Promise>>whenRejected: (in category 'resolving') -----
+ whenRejected: aBlock
+ 	"Evaluate aBlock when I am rejected"
+ 	aBlock numArgs <= 1 ifFalse: [self error: 'Must be 0- or 1-argument block'].
+ 	^ mutex critical: [
+ 		rejecters := rejecters copyWith: aBlock.
+ 		self isRejected ifTrue:[self evaluateRejecter: aBlock].
+ 	]!

Item was changed:
  ----- Method: Promise>>whenResolved: (in category 'resolving') -----
  whenResolved: aBlock
  	"Evaluate aBlock when I am resolved"
  	aBlock numArgs <= 1 ifFalse:[self error: 'Must be 0- or 1-argument block'].
+ 	^ mutex critical: [
- 	mutex critical: [
  		resolvers := resolvers copyWith: aBlock.
  		self isResolved ifTrue:[self evaluateResolver: aBlock].
  	]!


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic