└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # CSPEC 1 - Titanium Multithreading 2 | 3 | This [CSPEC](https://github.com/appcelerator/cspec) is for adding an official multithreading API to Titanium. 4 | 5 | ## Audience 6 | 7 | The primary audience for this API is a Titanium application developer. 8 | 9 | ## Goals 10 | 11 | The goals are to provide an official cross-platform way to perform asynchoronous tasks in a Titanium application. 12 | 13 | ## Description 14 | 15 | The problem is that Titanium has no official way to control what types of tasks are performed and how they are scheduled. There are currently two different "modes" -- using a Kroll thread (i.e. JS runs on it's own separate thread than the "main" UI thread) or main thread (i.e. JS runs on the main thread). Starting in 6.0, we intend to move the default to "main thread" for application execution. However, this potentially presents problems where certain operations should not block the UI thread and current hacks (setTimeout) don't provide sufficient control or predicitability and can be unstable or different per platform. 16 | 17 | ## Proposal 18 | 19 | The proposal would be to create a new API which specifically handles scheduling tasks to be run on either a new thread or the main UI thread. The API would give the developer the specific control to handle when and where tasks should run and provide clear intentions on how to run them. The API would also hide the complexities and platform specific differences between executing JS on different threads in different JS Contexts. 20 | 21 | ### API Definition 22 | 23 | #### Namespace 24 | 25 | The proposed namespace is `Titanium.Async`. All APIs will be defined as properties that are children of this namespace. 26 | 27 | #### Creating Queues 28 | 29 | A Queue will define a set of tasks that run serially on a system thread or other similar internal mechanism defined by the platform implementation. The API will provide a default queue called the "main queue" which is automatically created. The main queue is special and cannot be deleted. 30 | 31 | You can create a new Queue using the `createQueue` method. 32 | 33 | ```javascript 34 | var queue = Ti.Async.createQueue(); 35 | ``` 36 | 37 | #### Destroying Queues 38 | 39 | You can destroy a user-generated Queue with `destroy` using the queue instance. 40 | 41 | ```javascript 42 | queue.destroy(); 43 | ``` 44 | 45 | Once a queue is destroyed, any further usage will result in unpredictable behavior. 46 | 47 | Any pending jobs in the queue will automatically be cancelled. Any job instances should report `Ti.Async.STATUS_CANCELLED` as their `status` property if they are cancelled when the queue is destroyed. 48 | 49 | ##### Referencing the Main Queue 50 | 51 | Since the main queue is a system provided queue, you can reference it by using the `getMainQueue` method. 52 | 53 | ```javascript 54 | var mainQueue = Ti.Async.getMainQueue(); 55 | ``` 56 | 57 | ##### Dispatching a Job 58 | 59 | You dispatch a job using the `dispatch` method on the queue instance. 60 | 61 | ```javascript 62 | var job = queue.dispatch(function() { 63 | // my job is contained in this function 64 | }); 65 | ``` 66 | 67 | When you dispatch a job, the queue will return a Promise instance for the job. The Promise instance has several methods for interacting with the job. 68 | 69 | ##### Passing data to the Job 70 | 71 | Jobs cannot directly reference JS variables or functions not contained inside the job function. When a queue is created, a new JS context is created which does not share any application state (variables, functions, etc) with your application or with other queues or other jobs. 72 | 73 | In order to pass data between jobs, you must pass serialized data (serializable as JSON although implementations likely are free to "copy by value" the data) to and from the job function. 74 | 75 | To pass data into the job, you can use with `with` method on the job instance. 76 | 77 | ```javascript 78 | job.with({foo:1}); 79 | ``` 80 | 81 | And then in your job function, the first argument would receive your data. 82 | 83 | ```javascript 84 | var job = queue.dispatch(function (imports) { 85 | // imports.foo === 1 86 | }) 87 | .with({foo:1}); 88 | ``` 89 | 90 | ##### Passing data from the Job 91 | 92 | Since jobs cannot pass data directly back to the application or set variables in the application scope, it must pass any optional data back through a provided Object which is passed as the second argument to the job function. 93 | 94 | ```javascript 95 | var job = queue.dispatch(function (imports, exports) { 96 | exports.foo = 'bar'; 97 | }); 98 | ``` 99 | 100 | To receive the data, the job instance provides a `then` method which you can pass a callback function to receive any data. 101 | 102 | ```javascript 103 | var job = queue.dispatch(function (imports, exports) { 104 | exports.foo = 'bar'; 105 | }) 106 | .then(function (exports) { 107 | // exports.foo === 'bar' 108 | }); 109 | ``` 110 | 111 | The `then` function is always called (if set) regardless if you set any data in the `exports` or not. 112 | 113 | #### Tracking Job Status 114 | 115 | You can receive callbacks when the job state changes using the `status` method to pass a callback function. 116 | 117 | ```javascript 118 | var job = queue.dispatch(function (imports, exports) { 119 | }) 120 | .status(function (status) { 121 | switch (status) { 122 | case Ti.Async.STATUS_CREATED: 123 | Ti.API.debug('job created'); 124 | default: break; 125 | } 126 | }); 127 | ``` 128 | 129 | The following are the valid job states: 130 | 131 | - *STATUS_CREATED* - the job has been created ("queued") but not yet executed 132 | - *STATUS_RUNNING* - the job is currently running 133 | - *STATUS_COMPLETED* - the job has completed running without error or cancellation 134 | - *STATUS_ERRORED* - the job encountered an unhandled exception 135 | - *STATUS_CANCELLED* - the job was cancelled 136 | 137 | #### Handling Job Errors 138 | 139 | You can receive a callback when an unhandled exception happens processing a job using the `error` method to pass a callback function. 140 | 141 | ```javascript 142 | var job = queue.dispatch(function (imports, exports) { 143 | }) 144 | .error(function (err) { 145 | // err is the Error object for the exception 146 | }); 147 | ``` 148 | 149 | #### Cancelling a job 150 | 151 | You can cancel a job with the `cancel` method on the job instance. 152 | 153 | ```javascript 154 | var job = queue.dispatch(function (imports, exports) { 155 | }); 156 | job.cancel(); 157 | ``` 158 | 159 | Once a job is cancelled, the `status` property on the job instance will be set to `Ti.Async.STATUS_CANCELLED`. 160 | 161 | You cannot cancel a running job. If you attempt to cancel a running job, the request will be ignored. 162 | 163 | Once a job is cancelled, it will be removed from the queue and if there is a pending job, it will then move into the running state. 164 | 165 | ## Implementation Impacts 166 | 167 | Currently in master for both Android and iOS, we have a `tiapp.xml` setting which drives whether we use the kroll thread or UI thread for JS. We need to re-work the current compiler directive driven enablement to be runtime enabled. For example, in iOS, we need the Kroll subsystem to be able to be smart about which thread to run on and manage threading for the context appropriately. 168 | 169 | ## Timeline 170 | 171 | The implementation has not been scheduled, yet. 172 | 173 | ## Status 174 | 175 | - 12-02-2015 - Initial Draft 176 | - 10-24-2016 - Update Timeline and Copyright 177 | 178 | ## Legal Stuff 179 | 180 | This proposal is non-binding and may not be implemented, may be implemented partially, differently or not at all. Any intellectual property developed as part of this proposal is owned and Copyright © 2015-Present by Appcelerator, Inc. All Rights Reserved. 181 | 182 | For more information about what a CSPEC is, please visit the [Community Specifications & Proposals Repo](https://github.com/appcelerator/cspec). 183 | --------------------------------------------------------------------------------