├── .gitignore ├── .npmignore ├── README.md ├── build.js ├── dist ├── promise.common.js ├── promise.esm.js └── promise.min.js ├── index.html ├── index.js ├── package.json ├── src ├── async_callback.js ├── core.js ├── es6_extensions.js └── index.js ├── test.js ├── test.png └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # vim 2 | *~ 3 | *.sw? 4 | 5 | # misc editing tools 6 | .vscode/ 7 | tags 8 | 9 | #mac 10 | *.DS_Store 11 | 12 | # build tools 13 | node_modules/ 14 | 15 | # logfiles 16 | *.log 17 | .idea/ 18 | 19 | #npm 20 | yarn.lock 21 | package-lock.json -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # vim 2 | *~ 3 | *.sw? 4 | 5 | # generic editing tools 6 | tags 7 | tags.lock 8 | 9 | # mac 10 | *.DS_Store 11 | 12 | # wandows 13 | .vscode/ 14 | 15 | # build tools 16 | node_modules/ 17 | 18 | # logfiles 19 | *.log 20 | .idea/ 21 | 22 | # dev artifacts 23 | yarn.lock 24 | package-lock.json 25 | 26 | # misc 27 | README.md 28 | 29 | # source maps 30 | *.js.map 31 | 32 | # source files 33 | lib/ 34 | 35 | # test projects 36 | test.js 37 | test.png 38 | 39 | # self 40 | .npmignore 41 | .gitignore 42 | 43 | #example 44 | example/ 45 | 46 | #docs 47 | docs/ 48 | 49 | #src 50 | src/ 51 | 52 | # other configs 53 | webpack.config.js 54 | index.js 55 | index.html 56 | webpack.config.js 57 | build.js -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Promise/es6 2 | [![NPM version][npm-image]][npm-url] 3 |
4 | This `Promise` case is implemented in `es6`, and it is compatible with the native `async/await` function through the official unit test of the promise. This case is for study only, if it is used in a production environment, you can use the files in the `dist` directory. 5 | 6 | ### test 7 | You can use `npm i` or `yarn` install dependencies, then use `npm run test` or `yarn test` to test. 8 |

9 | ![test](./test.png) 10 | 11 | [npm-url]: https://www.npmjs.com/package/es2015-promise 12 | [npm-image]: https://img.shields.io/npm/v/es2015-promise.svg 13 | -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | const rollup = require('rollup') 2 | const babel = require('rollup-plugin-babel') 3 | const cleanup = require('rollup-plugin-cleanup') 4 | 5 | const esm = { 6 | input: 'src/index.js', 7 | output: { 8 | file: 'dist/promise.esm.js', 9 | format: 'es', 10 | } 11 | } 12 | 13 | const umd = { 14 | input: 'src/index.js', 15 | output: { 16 | file: 'dist/promise.min.js', 17 | format: 'umd', 18 | name: 'Promise', 19 | } 20 | } 21 | 22 | const cjs = { 23 | input: 'src/index.js', 24 | output: { 25 | file: 'dist/promise.common.js', 26 | format: 'cjs', 27 | } 28 | } 29 | 30 | async function build (cfg) { 31 | const bundle = await rollup.rollup({ 32 | input: cfg.input, 33 | plugins: [ 34 | cleanup(), 35 | babel({ 36 | exclude: 'node_modules/**', 37 | presets: ['es2015-rollup'], 38 | }), 39 | ] 40 | }) 41 | await bundle.generate(cfg.output) 42 | await bundle.write(cfg.output) 43 | } 44 | 45 | build(esm) 46 | build(cjs) 47 | build(umd) 48 | 49 | -------------------------------------------------------------------------------- /dist/promise.common.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var capacity = 1024; 4 | var queue = []; 5 | var index = 0; 6 | var flushing = false; 7 | var requestFlush = void 0; 8 | var BrowserMutationObserver = void 0; 9 | var isNode = typeof module !== 'undefined' && module.exports; 10 | function createMutationObserverCallback(callback) { 11 | var toggle = 1; 12 | var observer = new BrowserMutationObserver(callback); 13 | var node = document.createTextNode(''); 14 | observer.observe(node, { characterData: true }); 15 | return function () { 16 | toggle = -toggle; 17 | node.data = toggle; 18 | }; 19 | } 20 | function createTimerCallback(callback) { 21 | return function () { 22 | var t = setTimeout(handleTimer); 23 | var i = setInterval(handleTimer, 50); 24 | function handleTimer() { 25 | clearTimeout(t); 26 | clearInterval(i); 27 | t = null; 28 | i = null; 29 | callback(); 30 | } 31 | }; 32 | } 33 | function setImmediateOrNexttick(callback) { 34 | return function () { 35 | if (flushing && typeof setImmediate === 'function') { 36 | setImmediate(callback); 37 | } else { 38 | process.nextTick(callback); 39 | } 40 | }; 41 | } 42 | if (isNode) { 43 | requestFlush = setImmediateOrNexttick(_requestFlush); 44 | } else { 45 | var scope = typeof global !== 'undefined' ? global : self; 46 | BrowserMutationObserver = scope.MutationObserver || scope.WebKitMutationObserver; 47 | requestFlush = typeof BrowserMutationObserver === 'function' ? createMutationObserverCallback(_requestFlush) : createTimerCallback(_requestFlush); 48 | } 49 | function _requestFlush() { 50 | while (index < queue.length) { 51 | var currentIndex = index; 52 | index++; 53 | queue[currentIndex].call(); 54 | if (index > capacity) { 55 | var newLength = queque.length - index; 56 | for (var i = 0; i < newLength; i++) { 57 | queue[i] = queue[index + i]; 58 | } 59 | queue.length -= index; 60 | index = 0; 61 | } 62 | } 63 | queue.length = 0; 64 | index = 0; 65 | flushing = false; 66 | } 67 | function ascb(task) { 68 | if (!queue.length) { 69 | requestFlush(); 70 | flushing = true; 71 | } 72 | queue[queue.length] = task; 73 | } 74 | 75 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { 76 | return typeof obj; 77 | } : function (obj) { 78 | return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; 79 | }; 80 | 81 | var classCallCheck = function (instance, Constructor) { 82 | if (!(instance instanceof Constructor)) { 83 | throw new TypeError("Cannot call a class as a function"); 84 | } 85 | }; 86 | 87 | var createClass = function () { 88 | function defineProperties(target, props) { 89 | for (var i = 0; i < props.length; i++) { 90 | var descriptor = props[i]; 91 | descriptor.enumerable = descriptor.enumerable || false; 92 | descriptor.configurable = true; 93 | if ("value" in descriptor) descriptor.writable = true; 94 | Object.defineProperty(target, descriptor.key, descriptor); 95 | } 96 | } 97 | 98 | return function (Constructor, protoProps, staticProps) { 99 | if (protoProps) defineProperties(Constructor.prototype, protoProps); 100 | if (staticProps) defineProperties(Constructor, staticProps); 101 | return Constructor; 102 | }; 103 | }(); 104 | 105 | var slicedToArray = function () { 106 | function sliceIterator(arr, i) { 107 | var _arr = []; 108 | var _n = true; 109 | var _d = false; 110 | var _e = undefined; 111 | 112 | try { 113 | for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { 114 | _arr.push(_s.value); 115 | 116 | if (i && _arr.length === i) break; 117 | } 118 | } catch (err) { 119 | _d = true; 120 | _e = err; 121 | } finally { 122 | try { 123 | if (!_n && _i["return"]) _i["return"](); 124 | } finally { 125 | if (_d) throw _e; 126 | } 127 | } 128 | 129 | return _arr; 130 | } 131 | 132 | return function (arr, i) { 133 | if (Array.isArray(arr)) { 134 | return arr; 135 | } else if (Symbol.iterator in Object(arr)) { 136 | return sliceIterator(arr, i); 137 | } else { 138 | throw new TypeError("Invalid attempt to destructure non-iterable instance"); 139 | } 140 | }; 141 | }(); 142 | 143 | var noop = function noop() {}; 144 | 145 | var Promise$1 = function () { 146 | function Promise(fun) { 147 | classCallCheck(this, Promise); 148 | 149 | if (!(this instanceof Promise)) { 150 | throw TypeError('Calling a Promise constructor without new is forbidden'); 151 | } 152 | if (typeof fun !== 'function') { 153 | throw TypeError("Promise constructor's argument must be a function"); 154 | } 155 | this._state = 0; 156 | this._value = null; 157 | this._deferreds = null; 158 | if (fun === noop) return; 159 | doResolve(fun, this); 160 | } 161 | 162 | createClass(Promise, [{ 163 | key: 'then', 164 | value: function then(onFulfilled, onRejected) { 165 | var p = new Promise(noop); 166 | handle(this, new Handler(p, onFulfilled, onRejected)); 167 | return p; 168 | } 169 | }, { 170 | key: 'catch', 171 | value: function _catch(onRejected) { 172 | return this.then(null, onRejected); 173 | } 174 | }, { 175 | key: 'finally', 176 | value: function _finally(fun) { 177 | return this.then(function (value) { 178 | return Promise.resolve(fun()).then(function () { 179 | return value; 180 | }); 181 | }, function (reason) { 182 | return Promise.resolve(fun()).then(function () { 183 | throw reason; 184 | }); 185 | }); 186 | } 187 | }, { 188 | key: 'getValue', 189 | value: function getValue() { 190 | if (this._state === 3) { 191 | return this._value.getValue(); 192 | } 193 | return this._value; 194 | } 195 | }, { 196 | key: 'toString', 197 | value: function toString() { 198 | return '[object Promise]'; 199 | } 200 | }]); 201 | return Promise; 202 | }(); 203 | 204 | Promise$1._noop = noop; 205 | function doResolve(fun, promise) { 206 | var done = false; 207 | function _resolve(value) { 208 | if (done) return; 209 | done = true; 210 | resolve(promise, value); 211 | } 212 | function _reject(reason) { 213 | if (done) return; 214 | done = true; 215 | reject(promise, reason); 216 | } 217 | try { 218 | fun(_resolve, _reject); 219 | } catch (error) { 220 | _reject(error); 221 | } 222 | } 223 | function resolve(promise, newValue) { 224 | if (promise === newValue) { 225 | reject(promise, new TypeError('A promise cannot be resolved with itself')); 226 | return; 227 | } 228 | if (newValue && ((typeof newValue === 'undefined' ? 'undefined' : _typeof(newValue)) === 'object' || typeof newValue === 'function')) { 229 | var _getThen = getThen(newValue), 230 | _getThen2 = slicedToArray(_getThen, 2), 231 | isError = _getThen2[0], 232 | then = _getThen2[1]; 233 | 234 | if (isError) { 235 | reject(promise, then); 236 | return; 237 | } 238 | if (then === promise.then && newValue instanceof Promise$1) { 239 | promise._state = 3; 240 | promise._value = newValue; 241 | finale(promise); 242 | return; 243 | } else if (typeof then === 'function') { 244 | doResolve(then.bind(newValue), promise); 245 | return; 246 | } 247 | } 248 | promise._state = 1; 249 | promise._value = newValue; 250 | finale(promise); 251 | } 252 | function reject(promise, reason) { 253 | promise._state = 2; 254 | promise._value = reason; 255 | finale(promise); 256 | } 257 | function finale(promise) { 258 | if (promise._deferreds) { 259 | if (promise._deferreds.length === 1) { 260 | handle(promise, promise._deferreds[0]); 261 | promise._deferreds = null; 262 | return; 263 | } 264 | var _iteratorNormalCompletion = true; 265 | var _didIteratorError = false; 266 | var _iteratorError = undefined; 267 | 268 | try { 269 | for (var _iterator = promise._deferreds[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { 270 | var deferred = _step.value; 271 | 272 | handle(promise, deferred); 273 | } 274 | } catch (err) { 275 | _didIteratorError = true; 276 | _iteratorError = err; 277 | } finally { 278 | try { 279 | if (!_iteratorNormalCompletion && _iterator.return) { 280 | _iterator.return(); 281 | } 282 | } finally { 283 | if (_didIteratorError) { 284 | throw _iteratorError; 285 | } 286 | } 287 | } 288 | 289 | promise._deferreds = null; 290 | } 291 | } 292 | function handle(promise, deferred) { 293 | while (promise._state === 3) { 294 | promise = promise._value; 295 | } 296 | if (promise._state === 0) { 297 | promise._deferreds ? promise._deferreds.push(deferred) : promise._deferreds = [deferred]; 298 | return; 299 | } 300 | handleResolved(promise, deferred); 301 | } 302 | function handleResolved(promise, deferred) { 303 | ascb(function () { 304 | var isResolve = promise._state === 1; 305 | var callback = deferred[isResolve ? 'onFulfilled' : 'onRejected']; 306 | if (callback === null) { 307 | isResolve ? resolve(deferred.promise, promise._value) : reject(deferred.promise, promise._value); 308 | return; 309 | } 310 | try { 311 | var result = callback(promise._value); 312 | resolve(deferred.promise, result); 313 | } catch (error) { 314 | reject(deferred.promise, error); 315 | } 316 | }); 317 | } 318 | function getThen(value) { 319 | try { 320 | return [false, value.then]; 321 | } catch (error) { 322 | return [true, error]; 323 | } 324 | } 325 | function Handler(promise, onFulfilled, onRejected) { 326 | this.promise = promise; 327 | this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; 328 | this.onRejected = typeof onRejected === 'function' ? onRejected : null; 329 | } 330 | 331 | Promise$1.resolve = function (value) { 332 | if (value instanceof Promise$1) return value; 333 | function valueToPromise(val) { 334 | var p = new Promise$1(Promise$1._noop); 335 | p._state = 1; 336 | p._value = value; 337 | return p; 338 | } 339 | if ((typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' || typeof value === 'function') { 340 | try { 341 | var then = value.then; 342 | if (typeof then === 'function') { 343 | return new Promise$1(then.bind(value)); 344 | } 345 | } catch (error) { 346 | return new Promise$1(function (_, reject) { 347 | reject(error); 348 | }); 349 | } 350 | } 351 | return valueToPromise(value); 352 | }; 353 | Promise$1.reject = function (reason) { 354 | return new Promise$1(function (_, reject) { 355 | reject(reason); 356 | }); 357 | }; 358 | Promise$1.all = function (array) { 359 | !Array.isArray(array) && (array = Array.from(array)); 360 | return new Promise$1(function (resolve, reject) { 361 | if (array.length === 0) return resolve(array); 362 | var remaining = array.length; 363 | for (var i = 0; i < array.length; i++) { 364 | result(array[i], i); 365 | } 366 | function result(val, i) { 367 | if (!val || (typeof val === 'undefined' ? 'undefined' : _typeof(val)) !== 'object' && typeof val !== 'function') { 368 | array[i] = val; 369 | --remaining === 0 && resolve(array); 370 | return; 371 | } 372 | if (val instanceof Promise$1 && val.then === Promise$1.prototype.then) { 373 | while (val._state === 3) { 374 | val = val._value; 375 | } 376 | if (val._state === 1) return result(val._value, i); 377 | if (val._state === 2) reject(val._value); 378 | val.then(function (res) { 379 | return result(res, i); 380 | }, reject); 381 | return; 382 | } 383 | if (typeof val.then === 'function') { 384 | var p = new Promise$1(val.then.bind(val)); 385 | p.then(function (res) { 386 | return result(res, i); 387 | }, reject); 388 | return; 389 | } 390 | } 391 | }); 392 | }; 393 | Promise$1.race = function (array) { 394 | return new Promise$1(function (resolve, reject) { 395 | var _iteratorNormalCompletion = true; 396 | var _didIteratorError = false; 397 | var _iteratorError = undefined; 398 | 399 | try { 400 | for (var _iterator = array[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { 401 | var val = _step.value; 402 | 403 | Promise$1.resolve(val).then(resolve, reject); 404 | } 405 | } catch (err) { 406 | _didIteratorError = true; 407 | _iteratorError = err; 408 | } finally { 409 | try { 410 | if (!_iteratorNormalCompletion && _iterator.return) { 411 | _iterator.return(); 412 | } 413 | } finally { 414 | if (_didIteratorError) { 415 | throw _iteratorError; 416 | } 417 | } 418 | } 419 | }); 420 | }; 421 | 422 | module.exports = Promise$1; 423 | -------------------------------------------------------------------------------- /dist/promise.esm.js: -------------------------------------------------------------------------------- 1 | var capacity = 1024; 2 | var queue = []; 3 | var index = 0; 4 | var flushing = false; 5 | var requestFlush = void 0; 6 | var BrowserMutationObserver = void 0; 7 | var isNode = typeof module !== 'undefined' && module.exports; 8 | function createMutationObserverCallback(callback) { 9 | var toggle = 1; 10 | var observer = new BrowserMutationObserver(callback); 11 | var node = document.createTextNode(''); 12 | observer.observe(node, { characterData: true }); 13 | return function () { 14 | toggle = -toggle; 15 | node.data = toggle; 16 | }; 17 | } 18 | function createTimerCallback(callback) { 19 | return function () { 20 | var t = setTimeout(handleTimer); 21 | var i = setInterval(handleTimer, 50); 22 | function handleTimer() { 23 | clearTimeout(t); 24 | clearInterval(i); 25 | t = null; 26 | i = null; 27 | callback(); 28 | } 29 | }; 30 | } 31 | function setImmediateOrNexttick(callback) { 32 | return function () { 33 | if (flushing && typeof setImmediate === 'function') { 34 | setImmediate(callback); 35 | } else { 36 | process.nextTick(callback); 37 | } 38 | }; 39 | } 40 | if (isNode) { 41 | requestFlush = setImmediateOrNexttick(_requestFlush); 42 | } else { 43 | var scope = typeof global !== 'undefined' ? global : self; 44 | BrowserMutationObserver = scope.MutationObserver || scope.WebKitMutationObserver; 45 | requestFlush = typeof BrowserMutationObserver === 'function' ? createMutationObserverCallback(_requestFlush) : createTimerCallback(_requestFlush); 46 | } 47 | function _requestFlush() { 48 | while (index < queue.length) { 49 | var currentIndex = index; 50 | index++; 51 | queue[currentIndex].call(); 52 | if (index > capacity) { 53 | var newLength = queque.length - index; 54 | for (var i = 0; i < newLength; i++) { 55 | queue[i] = queue[index + i]; 56 | } 57 | queue.length -= index; 58 | index = 0; 59 | } 60 | } 61 | queue.length = 0; 62 | index = 0; 63 | flushing = false; 64 | } 65 | function ascb(task) { 66 | if (!queue.length) { 67 | requestFlush(); 68 | flushing = true; 69 | } 70 | queue[queue.length] = task; 71 | } 72 | 73 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { 74 | return typeof obj; 75 | } : function (obj) { 76 | return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; 77 | }; 78 | 79 | var classCallCheck = function (instance, Constructor) { 80 | if (!(instance instanceof Constructor)) { 81 | throw new TypeError("Cannot call a class as a function"); 82 | } 83 | }; 84 | 85 | var createClass = function () { 86 | function defineProperties(target, props) { 87 | for (var i = 0; i < props.length; i++) { 88 | var descriptor = props[i]; 89 | descriptor.enumerable = descriptor.enumerable || false; 90 | descriptor.configurable = true; 91 | if ("value" in descriptor) descriptor.writable = true; 92 | Object.defineProperty(target, descriptor.key, descriptor); 93 | } 94 | } 95 | 96 | return function (Constructor, protoProps, staticProps) { 97 | if (protoProps) defineProperties(Constructor.prototype, protoProps); 98 | if (staticProps) defineProperties(Constructor, staticProps); 99 | return Constructor; 100 | }; 101 | }(); 102 | 103 | var slicedToArray = function () { 104 | function sliceIterator(arr, i) { 105 | var _arr = []; 106 | var _n = true; 107 | var _d = false; 108 | var _e = undefined; 109 | 110 | try { 111 | for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { 112 | _arr.push(_s.value); 113 | 114 | if (i && _arr.length === i) break; 115 | } 116 | } catch (err) { 117 | _d = true; 118 | _e = err; 119 | } finally { 120 | try { 121 | if (!_n && _i["return"]) _i["return"](); 122 | } finally { 123 | if (_d) throw _e; 124 | } 125 | } 126 | 127 | return _arr; 128 | } 129 | 130 | return function (arr, i) { 131 | if (Array.isArray(arr)) { 132 | return arr; 133 | } else if (Symbol.iterator in Object(arr)) { 134 | return sliceIterator(arr, i); 135 | } else { 136 | throw new TypeError("Invalid attempt to destructure non-iterable instance"); 137 | } 138 | }; 139 | }(); 140 | 141 | var noop = function noop() {}; 142 | 143 | var Promise$1 = function () { 144 | function Promise(fun) { 145 | classCallCheck(this, Promise); 146 | 147 | if (!(this instanceof Promise)) { 148 | throw TypeError('Calling a Promise constructor without new is forbidden'); 149 | } 150 | if (typeof fun !== 'function') { 151 | throw TypeError("Promise constructor's argument must be a function"); 152 | } 153 | this._state = 0; 154 | this._value = null; 155 | this._deferreds = null; 156 | if (fun === noop) return; 157 | doResolve(fun, this); 158 | } 159 | 160 | createClass(Promise, [{ 161 | key: 'then', 162 | value: function then(onFulfilled, onRejected) { 163 | var p = new Promise(noop); 164 | handle(this, new Handler(p, onFulfilled, onRejected)); 165 | return p; 166 | } 167 | }, { 168 | key: 'catch', 169 | value: function _catch(onRejected) { 170 | return this.then(null, onRejected); 171 | } 172 | }, { 173 | key: 'finally', 174 | value: function _finally(fun) { 175 | return this.then(function (value) { 176 | return Promise.resolve(fun()).then(function () { 177 | return value; 178 | }); 179 | }, function (reason) { 180 | return Promise.resolve(fun()).then(function () { 181 | throw reason; 182 | }); 183 | }); 184 | } 185 | }, { 186 | key: 'getValue', 187 | value: function getValue() { 188 | if (this._state === 3) { 189 | return this._value.getValue(); 190 | } 191 | return this._value; 192 | } 193 | }, { 194 | key: 'toString', 195 | value: function toString() { 196 | return '[object Promise]'; 197 | } 198 | }]); 199 | return Promise; 200 | }(); 201 | 202 | Promise$1._noop = noop; 203 | function doResolve(fun, promise) { 204 | var done = false; 205 | function _resolve(value) { 206 | if (done) return; 207 | done = true; 208 | resolve(promise, value); 209 | } 210 | function _reject(reason) { 211 | if (done) return; 212 | done = true; 213 | reject(promise, reason); 214 | } 215 | try { 216 | fun(_resolve, _reject); 217 | } catch (error) { 218 | _reject(error); 219 | } 220 | } 221 | function resolve(promise, newValue) { 222 | if (promise === newValue) { 223 | reject(promise, new TypeError('A promise cannot be resolved with itself')); 224 | return; 225 | } 226 | if (newValue && ((typeof newValue === 'undefined' ? 'undefined' : _typeof(newValue)) === 'object' || typeof newValue === 'function')) { 227 | var _getThen = getThen(newValue), 228 | _getThen2 = slicedToArray(_getThen, 2), 229 | isError = _getThen2[0], 230 | then = _getThen2[1]; 231 | 232 | if (isError) { 233 | reject(promise, then); 234 | return; 235 | } 236 | if (then === promise.then && newValue instanceof Promise$1) { 237 | promise._state = 3; 238 | promise._value = newValue; 239 | finale(promise); 240 | return; 241 | } else if (typeof then === 'function') { 242 | doResolve(then.bind(newValue), promise); 243 | return; 244 | } 245 | } 246 | promise._state = 1; 247 | promise._value = newValue; 248 | finale(promise); 249 | } 250 | function reject(promise, reason) { 251 | promise._state = 2; 252 | promise._value = reason; 253 | finale(promise); 254 | } 255 | function finale(promise) { 256 | if (promise._deferreds) { 257 | if (promise._deferreds.length === 1) { 258 | handle(promise, promise._deferreds[0]); 259 | promise._deferreds = null; 260 | return; 261 | } 262 | var _iteratorNormalCompletion = true; 263 | var _didIteratorError = false; 264 | var _iteratorError = undefined; 265 | 266 | try { 267 | for (var _iterator = promise._deferreds[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { 268 | var deferred = _step.value; 269 | 270 | handle(promise, deferred); 271 | } 272 | } catch (err) { 273 | _didIteratorError = true; 274 | _iteratorError = err; 275 | } finally { 276 | try { 277 | if (!_iteratorNormalCompletion && _iterator.return) { 278 | _iterator.return(); 279 | } 280 | } finally { 281 | if (_didIteratorError) { 282 | throw _iteratorError; 283 | } 284 | } 285 | } 286 | 287 | promise._deferreds = null; 288 | } 289 | } 290 | function handle(promise, deferred) { 291 | while (promise._state === 3) { 292 | promise = promise._value; 293 | } 294 | if (promise._state === 0) { 295 | promise._deferreds ? promise._deferreds.push(deferred) : promise._deferreds = [deferred]; 296 | return; 297 | } 298 | handleResolved(promise, deferred); 299 | } 300 | function handleResolved(promise, deferred) { 301 | ascb(function () { 302 | var isResolve = promise._state === 1; 303 | var callback = deferred[isResolve ? 'onFulfilled' : 'onRejected']; 304 | if (callback === null) { 305 | isResolve ? resolve(deferred.promise, promise._value) : reject(deferred.promise, promise._value); 306 | return; 307 | } 308 | try { 309 | var result = callback(promise._value); 310 | resolve(deferred.promise, result); 311 | } catch (error) { 312 | reject(deferred.promise, error); 313 | } 314 | }); 315 | } 316 | function getThen(value) { 317 | try { 318 | return [false, value.then]; 319 | } catch (error) { 320 | return [true, error]; 321 | } 322 | } 323 | function Handler(promise, onFulfilled, onRejected) { 324 | this.promise = promise; 325 | this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; 326 | this.onRejected = typeof onRejected === 'function' ? onRejected : null; 327 | } 328 | 329 | Promise$1.resolve = function (value) { 330 | if (value instanceof Promise$1) return value; 331 | function valueToPromise(val) { 332 | var p = new Promise$1(Promise$1._noop); 333 | p._state = 1; 334 | p._value = value; 335 | return p; 336 | } 337 | if ((typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' || typeof value === 'function') { 338 | try { 339 | var then = value.then; 340 | if (typeof then === 'function') { 341 | return new Promise$1(then.bind(value)); 342 | } 343 | } catch (error) { 344 | return new Promise$1(function (_, reject) { 345 | reject(error); 346 | }); 347 | } 348 | } 349 | return valueToPromise(value); 350 | }; 351 | Promise$1.reject = function (reason) { 352 | return new Promise$1(function (_, reject) { 353 | reject(reason); 354 | }); 355 | }; 356 | Promise$1.all = function (array) { 357 | !Array.isArray(array) && (array = Array.from(array)); 358 | return new Promise$1(function (resolve, reject) { 359 | if (array.length === 0) return resolve(array); 360 | var remaining = array.length; 361 | for (var i = 0; i < array.length; i++) { 362 | result(array[i], i); 363 | } 364 | function result(val, i) { 365 | if (!val || (typeof val === 'undefined' ? 'undefined' : _typeof(val)) !== 'object' && typeof val !== 'function') { 366 | array[i] = val; 367 | --remaining === 0 && resolve(array); 368 | return; 369 | } 370 | if (val instanceof Promise$1 && val.then === Promise$1.prototype.then) { 371 | while (val._state === 3) { 372 | val = val._value; 373 | } 374 | if (val._state === 1) return result(val._value, i); 375 | if (val._state === 2) reject(val._value); 376 | val.then(function (res) { 377 | return result(res, i); 378 | }, reject); 379 | return; 380 | } 381 | if (typeof val.then === 'function') { 382 | var p = new Promise$1(val.then.bind(val)); 383 | p.then(function (res) { 384 | return result(res, i); 385 | }, reject); 386 | return; 387 | } 388 | } 389 | }); 390 | }; 391 | Promise$1.race = function (array) { 392 | return new Promise$1(function (resolve, reject) { 393 | var _iteratorNormalCompletion = true; 394 | var _didIteratorError = false; 395 | var _iteratorError = undefined; 396 | 397 | try { 398 | for (var _iterator = array[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { 399 | var val = _step.value; 400 | 401 | Promise$1.resolve(val).then(resolve, reject); 402 | } 403 | } catch (err) { 404 | _didIteratorError = true; 405 | _iteratorError = err; 406 | } finally { 407 | try { 408 | if (!_iteratorNormalCompletion && _iterator.return) { 409 | _iterator.return(); 410 | } 411 | } finally { 412 | if (_didIteratorError) { 413 | throw _iteratorError; 414 | } 415 | } 416 | } 417 | }); 418 | }; 419 | 420 | export default Promise$1; 421 | -------------------------------------------------------------------------------- /dist/promise.min.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 3 | typeof define === 'function' && define.amd ? define(factory) : 4 | (global.Promise = factory()); 5 | }(this, (function () { 'use strict'; 6 | 7 | var capacity = 1024; 8 | var queue = []; 9 | var index = 0; 10 | var flushing = false; 11 | var requestFlush = void 0; 12 | var BrowserMutationObserver = void 0; 13 | var isNode = typeof module !== 'undefined' && module.exports; 14 | function createMutationObserverCallback(callback) { 15 | var toggle = 1; 16 | var observer = new BrowserMutationObserver(callback); 17 | var node = document.createTextNode(''); 18 | observer.observe(node, { characterData: true }); 19 | return function () { 20 | toggle = -toggle; 21 | node.data = toggle; 22 | }; 23 | } 24 | function createTimerCallback(callback) { 25 | return function () { 26 | var t = setTimeout(handleTimer); 27 | var i = setInterval(handleTimer, 50); 28 | function handleTimer() { 29 | clearTimeout(t); 30 | clearInterval(i); 31 | t = null; 32 | i = null; 33 | callback(); 34 | } 35 | }; 36 | } 37 | function setImmediateOrNexttick(callback) { 38 | return function () { 39 | if (flushing && typeof setImmediate === 'function') { 40 | setImmediate(callback); 41 | } else { 42 | process.nextTick(callback); 43 | } 44 | }; 45 | } 46 | if (isNode) { 47 | requestFlush = setImmediateOrNexttick(_requestFlush); 48 | } else { 49 | var scope = typeof global !== 'undefined' ? global : self; 50 | BrowserMutationObserver = scope.MutationObserver || scope.WebKitMutationObserver; 51 | requestFlush = typeof BrowserMutationObserver === 'function' ? createMutationObserverCallback(_requestFlush) : createTimerCallback(_requestFlush); 52 | } 53 | function _requestFlush() { 54 | while (index < queue.length) { 55 | var currentIndex = index; 56 | index++; 57 | queue[currentIndex].call(); 58 | if (index > capacity) { 59 | var newLength = queque.length - index; 60 | for (var i = 0; i < newLength; i++) { 61 | queue[i] = queue[index + i]; 62 | } 63 | queue.length -= index; 64 | index = 0; 65 | } 66 | } 67 | queue.length = 0; 68 | index = 0; 69 | flushing = false; 70 | } 71 | function ascb(task) { 72 | if (!queue.length) { 73 | requestFlush(); 74 | flushing = true; 75 | } 76 | queue[queue.length] = task; 77 | } 78 | 79 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { 80 | return typeof obj; 81 | } : function (obj) { 82 | return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; 83 | }; 84 | 85 | var classCallCheck = function (instance, Constructor) { 86 | if (!(instance instanceof Constructor)) { 87 | throw new TypeError("Cannot call a class as a function"); 88 | } 89 | }; 90 | 91 | var createClass = function () { 92 | function defineProperties(target, props) { 93 | for (var i = 0; i < props.length; i++) { 94 | var descriptor = props[i]; 95 | descriptor.enumerable = descriptor.enumerable || false; 96 | descriptor.configurable = true; 97 | if ("value" in descriptor) descriptor.writable = true; 98 | Object.defineProperty(target, descriptor.key, descriptor); 99 | } 100 | } 101 | 102 | return function (Constructor, protoProps, staticProps) { 103 | if (protoProps) defineProperties(Constructor.prototype, protoProps); 104 | if (staticProps) defineProperties(Constructor, staticProps); 105 | return Constructor; 106 | }; 107 | }(); 108 | 109 | var slicedToArray = function () { 110 | function sliceIterator(arr, i) { 111 | var _arr = []; 112 | var _n = true; 113 | var _d = false; 114 | var _e = undefined; 115 | 116 | try { 117 | for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { 118 | _arr.push(_s.value); 119 | 120 | if (i && _arr.length === i) break; 121 | } 122 | } catch (err) { 123 | _d = true; 124 | _e = err; 125 | } finally { 126 | try { 127 | if (!_n && _i["return"]) _i["return"](); 128 | } finally { 129 | if (_d) throw _e; 130 | } 131 | } 132 | 133 | return _arr; 134 | } 135 | 136 | return function (arr, i) { 137 | if (Array.isArray(arr)) { 138 | return arr; 139 | } else if (Symbol.iterator in Object(arr)) { 140 | return sliceIterator(arr, i); 141 | } else { 142 | throw new TypeError("Invalid attempt to destructure non-iterable instance"); 143 | } 144 | }; 145 | }(); 146 | 147 | var noop = function noop() {}; 148 | 149 | var Promise$1 = function () { 150 | function Promise(fun) { 151 | classCallCheck(this, Promise); 152 | 153 | if (!(this instanceof Promise)) { 154 | throw TypeError('Calling a Promise constructor without new is forbidden'); 155 | } 156 | if (typeof fun !== 'function') { 157 | throw TypeError("Promise constructor's argument must be a function"); 158 | } 159 | this._state = 0; 160 | this._value = null; 161 | this._deferreds = null; 162 | if (fun === noop) return; 163 | doResolve(fun, this); 164 | } 165 | 166 | createClass(Promise, [{ 167 | key: 'then', 168 | value: function then(onFulfilled, onRejected) { 169 | var p = new Promise(noop); 170 | handle(this, new Handler(p, onFulfilled, onRejected)); 171 | return p; 172 | } 173 | }, { 174 | key: 'catch', 175 | value: function _catch(onRejected) { 176 | return this.then(null, onRejected); 177 | } 178 | }, { 179 | key: 'finally', 180 | value: function _finally(fun) { 181 | return this.then(function (value) { 182 | return Promise.resolve(fun()).then(function () { 183 | return value; 184 | }); 185 | }, function (reason) { 186 | return Promise.resolve(fun()).then(function () { 187 | throw reason; 188 | }); 189 | }); 190 | } 191 | }, { 192 | key: 'getValue', 193 | value: function getValue() { 194 | if (this._state === 3) { 195 | return this._value.getValue(); 196 | } 197 | return this._value; 198 | } 199 | }, { 200 | key: 'toString', 201 | value: function toString() { 202 | return '[object Promise]'; 203 | } 204 | }]); 205 | return Promise; 206 | }(); 207 | 208 | Promise$1._noop = noop; 209 | function doResolve(fun, promise) { 210 | var done = false; 211 | function _resolve(value) { 212 | if (done) return; 213 | done = true; 214 | resolve(promise, value); 215 | } 216 | function _reject(reason) { 217 | if (done) return; 218 | done = true; 219 | reject(promise, reason); 220 | } 221 | try { 222 | fun(_resolve, _reject); 223 | } catch (error) { 224 | _reject(error); 225 | } 226 | } 227 | function resolve(promise, newValue) { 228 | if (promise === newValue) { 229 | reject(promise, new TypeError('A promise cannot be resolved with itself')); 230 | return; 231 | } 232 | if (newValue && ((typeof newValue === 'undefined' ? 'undefined' : _typeof(newValue)) === 'object' || typeof newValue === 'function')) { 233 | var _getThen = getThen(newValue), 234 | _getThen2 = slicedToArray(_getThen, 2), 235 | isError = _getThen2[0], 236 | then = _getThen2[1]; 237 | 238 | if (isError) { 239 | reject(promise, then); 240 | return; 241 | } 242 | if (then === promise.then && newValue instanceof Promise$1) { 243 | promise._state = 3; 244 | promise._value = newValue; 245 | finale(promise); 246 | return; 247 | } else if (typeof then === 'function') { 248 | doResolve(then.bind(newValue), promise); 249 | return; 250 | } 251 | } 252 | promise._state = 1; 253 | promise._value = newValue; 254 | finale(promise); 255 | } 256 | function reject(promise, reason) { 257 | promise._state = 2; 258 | promise._value = reason; 259 | finale(promise); 260 | } 261 | function finale(promise) { 262 | if (promise._deferreds) { 263 | if (promise._deferreds.length === 1) { 264 | handle(promise, promise._deferreds[0]); 265 | promise._deferreds = null; 266 | return; 267 | } 268 | var _iteratorNormalCompletion = true; 269 | var _didIteratorError = false; 270 | var _iteratorError = undefined; 271 | 272 | try { 273 | for (var _iterator = promise._deferreds[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { 274 | var deferred = _step.value; 275 | 276 | handle(promise, deferred); 277 | } 278 | } catch (err) { 279 | _didIteratorError = true; 280 | _iteratorError = err; 281 | } finally { 282 | try { 283 | if (!_iteratorNormalCompletion && _iterator.return) { 284 | _iterator.return(); 285 | } 286 | } finally { 287 | if (_didIteratorError) { 288 | throw _iteratorError; 289 | } 290 | } 291 | } 292 | 293 | promise._deferreds = null; 294 | } 295 | } 296 | function handle(promise, deferred) { 297 | while (promise._state === 3) { 298 | promise = promise._value; 299 | } 300 | if (promise._state === 0) { 301 | promise._deferreds ? promise._deferreds.push(deferred) : promise._deferreds = [deferred]; 302 | return; 303 | } 304 | handleResolved(promise, deferred); 305 | } 306 | function handleResolved(promise, deferred) { 307 | ascb(function () { 308 | var isResolve = promise._state === 1; 309 | var callback = deferred[isResolve ? 'onFulfilled' : 'onRejected']; 310 | if (callback === null) { 311 | isResolve ? resolve(deferred.promise, promise._value) : reject(deferred.promise, promise._value); 312 | return; 313 | } 314 | try { 315 | var result = callback(promise._value); 316 | resolve(deferred.promise, result); 317 | } catch (error) { 318 | reject(deferred.promise, error); 319 | } 320 | }); 321 | } 322 | function getThen(value) { 323 | try { 324 | return [false, value.then]; 325 | } catch (error) { 326 | return [true, error]; 327 | } 328 | } 329 | function Handler(promise, onFulfilled, onRejected) { 330 | this.promise = promise; 331 | this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; 332 | this.onRejected = typeof onRejected === 'function' ? onRejected : null; 333 | } 334 | 335 | Promise$1.resolve = function (value) { 336 | if (value instanceof Promise$1) return value; 337 | function valueToPromise(val) { 338 | var p = new Promise$1(Promise$1._noop); 339 | p._state = 1; 340 | p._value = value; 341 | return p; 342 | } 343 | if ((typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' || typeof value === 'function') { 344 | try { 345 | var then = value.then; 346 | if (typeof then === 'function') { 347 | return new Promise$1(then.bind(value)); 348 | } 349 | } catch (error) { 350 | return new Promise$1(function (_, reject) { 351 | reject(error); 352 | }); 353 | } 354 | } 355 | return valueToPromise(value); 356 | }; 357 | Promise$1.reject = function (reason) { 358 | return new Promise$1(function (_, reject) { 359 | reject(reason); 360 | }); 361 | }; 362 | Promise$1.all = function (array) { 363 | !Array.isArray(array) && (array = Array.from(array)); 364 | return new Promise$1(function (resolve, reject) { 365 | if (array.length === 0) return resolve(array); 366 | var remaining = array.length; 367 | for (var i = 0; i < array.length; i++) { 368 | result(array[i], i); 369 | } 370 | function result(val, i) { 371 | if (!val || (typeof val === 'undefined' ? 'undefined' : _typeof(val)) !== 'object' && typeof val !== 'function') { 372 | array[i] = val; 373 | --remaining === 0 && resolve(array); 374 | return; 375 | } 376 | if (val instanceof Promise$1 && val.then === Promise$1.prototype.then) { 377 | while (val._state === 3) { 378 | val = val._value; 379 | } 380 | if (val._state === 1) return result(val._value, i); 381 | if (val._state === 2) reject(val._value); 382 | val.then(function (res) { 383 | return result(res, i); 384 | }, reject); 385 | return; 386 | } 387 | if (typeof val.then === 'function') { 388 | var p = new Promise$1(val.then.bind(val)); 389 | p.then(function (res) { 390 | return result(res, i); 391 | }, reject); 392 | return; 393 | } 394 | } 395 | }); 396 | }; 397 | Promise$1.race = function (array) { 398 | return new Promise$1(function (resolve, reject) { 399 | var _iteratorNormalCompletion = true; 400 | var _didIteratorError = false; 401 | var _iteratorError = undefined; 402 | 403 | try { 404 | for (var _iterator = array[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { 405 | var val = _step.value; 406 | 407 | Promise$1.resolve(val).then(resolve, reject); 408 | } 409 | } catch (err) { 410 | _didIteratorError = true; 411 | _iteratorError = err; 412 | } finally { 413 | try { 414 | if (!_iteratorNormalCompletion && _iterator.return) { 415 | _iterator.return(); 416 | } 417 | } finally { 418 | if (_didIteratorError) { 419 | throw _iteratorError; 420 | } 421 | } 422 | } 423 | }); 424 | }; 425 | 426 | return Promise$1; 427 | 428 | }))); 429 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Promise by taotao 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import promise from './src' 2 | 3 | window.Promise = promise 4 | 5 | function testGenerator () { 6 | const p = new Promise((resolve) => { 7 | setTimeout(() => resolve(`resolve promise`), 2000) 8 | }) 9 | 10 | function * f() { 11 | const res = yield p 12 | console.assert(res === 'resolve promise', '[res] is not the expected value') 13 | } 14 | 15 | const g = f() 16 | g.next().value.then(res => g.next(res)) 17 | } 18 | 19 | function testAsyncFun () { 20 | function resolve(val) { 21 | return new Promise(resolve => { 22 | setTimeout(() => { 23 | val++ 24 | resolve(val) 25 | }, 2000) 26 | }) 27 | } 28 | 29 | async function f() { 30 | let val = await resolve(10) 31 | let valTwo = await resolve(val++) 32 | 33 | console.assert(valTwo === 12, '[valTwo] is not the expected value') 34 | } 35 | f() 36 | } 37 | 38 | testGenerator() 39 | testAsyncFun() -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "es2015-promise", 3 | "version": "1.0.3", 4 | "description": "A basic promise implementation", 5 | "main": "dist/promise.common.js", 6 | "module": "dist/promise.esm.js", 7 | "scripts": { 8 | "start": "webpack-dev-server", 9 | "build": "node build.js", 10 | "test": "promises-aplus-tests test.js" 11 | }, 12 | "author": "taotao", 13 | "license": "ISC", 14 | "devDependencies": { 15 | "babel-core": "^6.26.3", 16 | "babel-preset-es2015-rollup": "^3.0.0", 17 | "promises-aplus-tests": "*", 18 | "rollup": "^0.62.0", 19 | "rollup-plugin-babel": "^3.0.7", 20 | "rollup-plugin-cleanup": "^3.0.0", 21 | "webpack": "^3.10.0", 22 | "webpack-dev-server": "^2.9.7" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "https://github.com/imtaotao/promise.git" 27 | }, 28 | "keywords":[ 29 | "promise", 30 | "es6", 31 | "es2015", 32 | "polyfill" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /src/async_callback.js: -------------------------------------------------------------------------------- 1 | const capacity = 1024 2 | const queue = [] 3 | let index = 0 4 | let flushing = false 5 | let requestFlush 6 | let BrowserMutationObserver 7 | 8 | const isNode = typeof module !== 'undefined' && module.exports 9 | 10 | // MutationObserver 11 | function createMutationObserverCallback (callback) { 12 | let toggle = 1 13 | const observer = new BrowserMutationObserver(callback) 14 | const node = document.createTextNode('') 15 | observer.observe(node, {characterData: true}) 16 | return () => { 17 | toggle = -toggle 18 | node.data = toggle 19 | } 20 | } 21 | 22 | // Timer 23 | function createTimerCallback (callback) { 24 | return () => { 25 | let t = setTimeout(handleTimer) 26 | 27 | // 由于 timeout 在 firefox 的 worker 线程中可能会出现 bug 28 | // 为了防止 timeout 不触发,用 interval 预防 29 | let i = setInterval(handleTimer, 50) 30 | function handleTimer () { 31 | clearTimeout(t) 32 | clearInterval(i) 33 | t = null 34 | i = null 35 | callback() 36 | } 37 | } 38 | } 39 | 40 | // node 41 | function setImmediateOrNexttick (callback) { 42 | return () => { 43 | /** 44 | * 由于没有办法真正添加为 microtask,即使在 node 中,promise task 会被添加到 nextTickQueue 45 | * 以下原因导致优先采用 setTmmediate 46 | * 1. 为了保证 promise 最好不能在 nextTick 前面被调用,nextTick 会在 poll 阶段之前被调用 47 | * 2. setTmmediate 会在 poll 阶段结束后被调用,而在 node 中,promise 应用大部分情况都是处理 io 48 | * 3. process.nextTick 递归会导致 event loop 被锁死,而 setImmediate 不会 49 | */ 50 | if (flushing && typeof setImmediate === 'function') { 51 | setImmediate(callback) 52 | } else { 53 | process.nextTick(callback) 54 | } 55 | } 56 | } 57 | 58 | if (isNode) { 59 | requestFlush = setImmediateOrNexttick(_requestFlush) 60 | } else { 61 | const scope = typeof global !== 'undefined' ? global : self 62 | BrowserMutationObserver = scope.MutationObserver || scope.WebKitMutationObserver 63 | 64 | // MutationObserver 会新建个 microtask,与 promise 最契合 65 | // 备用选择 timeout 66 | requestFlush = typeof BrowserMutationObserver === 'function' 67 | ? createMutationObserverCallback(_requestFlush) 68 | : createTimerCallback(_requestFlush) 69 | } 70 | 71 | function _requestFlush () { 72 | while (index < queue.length) { 73 | const currentIndex = index 74 | 75 | index++ 76 | queue[currentIndex].call() 77 | 78 | // 因为 task 里面可能继续添加队列,可能会产生一个无限长的队列,内存爆炸, 79 | // 所以如果大于限定的值,需要做处理 80 | if (index > capacity) { 81 | const newLength = queque.length - index 82 | // 把队列后面的 task 全部移到前面,然后把后面的删掉,从头继续开始便利调用 83 | for (let i = 0; i < newLength; i++) { 84 | queue[i] = queue[index + i] 85 | } 86 | 87 | queue.length -= index 88 | index = 0 89 | } 90 | } 91 | 92 | // 遍历完成后,清空 93 | queue.length = 0 94 | index = 0 95 | flushing = false 96 | } 97 | 98 | export default function ascb(task) { 99 | // 如果 queue 为空,会走 requestFlush,这个函数为一个异步任务 100 | // 而此方法为同步代码,所以等遍历 queue 的时候,队列里面是已经存在 task 了的 101 | // 这样做的好处,如果在异步回调执行之前(requestFlush)有多个 promise 102 | // 会在一次异步任务中被处理,而不是多次新建异步任务 103 | if (!queue.length) { 104 | requestFlush() 105 | flushing = true 106 | } 107 | 108 | queue[queue.length] = task 109 | } -------------------------------------------------------------------------------- /src/core.js: -------------------------------------------------------------------------------- 1 | // States: 0 - pending, 1 - fulfilled, 2 - rejected, 3 - another promise 2 | import ascb from './async_callback' 3 | const noop = () => {} 4 | 5 | export default class Promise { 6 | constructor (fun) { 7 | if (!(this instanceof Promise)) { 8 | throw TypeError('Calling a Promise constructor without new is forbidden') 9 | } 10 | if (typeof fun !== 'function') { 11 | throw TypeError("Promise constructor's argument must be a function") 12 | } 13 | 14 | this._state = 0 15 | this._value = null 16 | this._deferreds = null 17 | 18 | if (fun === noop) return 19 | doResolve(fun, this) 20 | } 21 | 22 | /** 23 | * 本来 prototype method 应该需要判断 this, 24 | * 如果 this 不符合要求应该 throw TypeError, 25 | * example: 26 | * const { then } = promise.then 27 | * then(f, r) // TypeError: ... 28 | */ 29 | then (onFulfilled, onRejected) { 30 | /** 31 | * 每次都会返回一个新的 promise 出去 32 | * 而当前上下文会有个 deferred 队列 33 | * 每个 deferred 都会保存这个 return 出去的 promise 和回调 34 | */ 35 | const p = new Promise(noop) 36 | handle(this, new Handler(p, onFulfilled, onRejected)) 37 | 38 | return p 39 | } 40 | 41 | catch (onRejected) { 42 | return this.then(null, onRejected) 43 | } 44 | 45 | finally (fun) { 46 | // 我们要保证 fun 不会收到任何参数(正常或者错误的值) 47 | // 用 Promise.resolve 包裹起来的原因是,fun 有可能返回的是一个 promise,我们要保证 finally 之后继续链式 48 | return this.then( 49 | value => Promise.resolve(fun()).then(() => value), 50 | reason => Promise.resolve(fun()).then(() => { throw reason }) 51 | ) 52 | } 53 | 54 | getValue () { 55 | if (this._state === 3) { 56 | return this._value.getValue() 57 | } 58 | 59 | return this._value 60 | } 61 | 62 | toString () { 63 | return '[object Promise]' 64 | } 65 | } 66 | 67 | Promise._noop = noop 68 | 69 | function doResolve (fun, promise) { 70 | let done = false 71 | 72 | // 立个 flag 保证不能重复调用 resolve 或者 reject 73 | // 因为 promise 的 state 一旦改变,就再也不可能改变了 74 | function _resolve (value) { 75 | if (done) return 76 | done = true 77 | resolve(promise, value) 78 | } 79 | 80 | function _reject (reason) { 81 | if (done) return 82 | done = true 83 | reject(promise, reason) 84 | } 85 | 86 | try { 87 | fun(_resolve, _reject) 88 | } catch (error) { 89 | _reject(error) 90 | } 91 | } 92 | 93 | function resolve (promise, newValue) { 94 | // 规范关于 resolve 的描述 https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure 95 | /** 96 | * newValue 有可能有以下几种情况 97 | * 1. 当前上下文自己,这种情况直接报个错 98 | * 2. object 或者 function 99 | * 这种情况需要去尝试那 newValue 的 then 方法 100 | * 如果拿不到,那就代表是个正常的对象或者函数 101 | * a. 如果 newValue 是一个新的 promise,需要根据新的 promise 的 state 来更改,此时当前 promise 的 state 为 3 102 | * b. 如果 newValue 是带 then 方法的对象({ then () {} }), 103 | * 需要把这个 then 方法当成 promise 的构造函数的参数进行调用(可以在浏览器控制台用原生 promise 的测试下) 104 | * 3. 其他的值,state 改为 1 105 | */ 106 | if (promise === newValue) { 107 | reject(promise, new TypeError('A promise cannot be resolved with itself')) 108 | return 109 | } 110 | 111 | // 如果 newValue 是个对象或者函数 112 | if ( 113 | newValue && 114 | (typeof newValue === 'object' || typeof newValue === 'function') 115 | ) { 116 | // 尝试去拿到 newValue 的 then 方法,如果失败 then 方法会是一个 error 117 | const [isError, then] = getThen(newValue) 118 | if (isError) { 119 | reject(promise, then) 120 | return 121 | } 122 | 123 | // 如果 newValue 是一个 Promise 实例的话,state 为 3 124 | if (then === promise.then && newValue instanceof Promise) { 125 | promise._state = 3 126 | promise._value = newValue 127 | finale(promise) 128 | return 129 | } else if (typeof then === 'function') { 130 | // 修复 then 方法内部的 this 指向 131 | doResolve(then.bind(newValue), promise) 132 | return 133 | } 134 | } 135 | 136 | promise._state = 1 137 | promise._value = newValue 138 | finale(promise) 139 | } 140 | 141 | function reject (promise, reason) { 142 | // 改变 state 和 value 143 | promise._state = 2 144 | promise._value = reason 145 | finale(promise) 146 | } 147 | 148 | function finale (promise) { 149 | /** 150 | * 1.如果当前 promise 没有 deferreds,代表 resolve 是通过同步进行调用的 151 | * 此时 promise 的 state 已经改变,value 也已经拿到,不需要做什么事情 152 | * 2.如果 deferreds 是存在的,就需要对每个 deferreds 进行处理 153 | */ 154 | if (promise._deferreds) { 155 | if (promise._deferreds.length === 1) { 156 | handle(promise, promise._deferreds[0]) 157 | promise._deferreds = null 158 | return 159 | } 160 | 161 | for (const deferred of promise._deferreds) { 162 | handle(promise, deferred) 163 | } 164 | promise._deferreds = null 165 | } 166 | } 167 | 168 | /** 169 | * handle 方法供两个接口调用,finale 和 then 170 | * 它也提供了两种功能,对传进来的 promise 添加 deferred 或者 调用 deferred 的回调 171 | */ 172 | function handle (promise, deferred) { 173 | /** 174 | * 如果 promise 的 state 为 3,拿到最底层的 promise 175 | * 因为需要把 deferred 转移到最底层的 promise 上 176 | * 这样当前上下文(promise)添加的 then 就会随着最底层的 promise 的 state 改变而改变 177 | */ 178 | while (promise._state === 3) { 179 | promise = promise._value 180 | } 181 | 182 | /** 183 | * 如果通过 then 方法走到这里,一般情况下 state 都为 0,除非 resolve 为同步代码 184 | * 如果通过 finale 方法走到这里,state 肯定 1 或者 2,出发经过了 while 循环,底层的 promise 状态还为 0 185 | */ 186 | if (promise._state === 0) { 187 | promise._deferreds 188 | ? promise._deferreds.push(deferred) 189 | : (promise._deferreds = [deferred]) 190 | return 191 | } 192 | 193 | /** 194 | * 1.如果是通过 finale 走到这里 195 | * 此时 promise.state 为 1 或者 2 196 | * 2.如果是通过 then 方法走到这里 197 | * 此时 promise.state 为 1 或者 2,promise.deferred 为 null 198 | * 但是可以直接用 handle 参数(promise, deferred)进行调用 199 | */ 200 | handleResolved(promise, deferred) 201 | } 202 | 203 | function handleResolved (promise, deferred) { 204 | // 异步调用,优先通过微任务调用 205 | ascb(() => { 206 | const isResolve = promise._state === 1 207 | const callback = deferred[isResolve ? 'onFulfilled' : 'onRejected'] 208 | 209 | // 如果当期 deferred 的回调为 null 就把当前 promise._value 为值继续往下找 210 | if (callback === null) { 211 | isResolve 212 | ? resolve(deferred.promise, promise._value) 213 | : reject(deferred.promise, promise._value) 214 | return 215 | } 216 | 217 | try { 218 | /** 219 | * 如果外面是 then 形式的链式调用,此时的 deferred.promise 应该是 220 | * { 221 | * deferred: [不为空的数组], 222 | * state: 0, 223 | * value: null, 224 | * } 225 | */ 226 | const result = callback(promise._value) 227 | 228 | /** 229 | * 经过 resolve 之后,deferred.promise 应该是 230 | * { 231 | * deferred: [不为空的数组], 232 | * state: 1 或者 2 或者 3, 233 | * value: xx 234 | * } 235 | * 然后走 finale 就可以完成链式操作了 236 | */ 237 | resolve(deferred.promise, result) 238 | } catch (error) { 239 | reject(deferred.promise, error) 240 | } 241 | }) 242 | } 243 | 244 | function getThen (value) { 245 | try { 246 | return [false, value.then] 247 | } catch (error) { 248 | return [true, error] 249 | } 250 | } 251 | 252 | // 一个 deferred 对象应该为 { promise, onFulfilled, onRejected } 253 | function Handler (promise, onFulfilled, onRejected) { 254 | this.promise = promise 255 | this.onFulfilled = typeof onFulfilled === 'function' 256 | ? onFulfilled 257 | : null 258 | this.onRejected = typeof onRejected === 'function' 259 | ? onRejected 260 | : null 261 | } -------------------------------------------------------------------------------- /src/es6_extensions.js: -------------------------------------------------------------------------------- 1 | import Promise from './core' 2 | 3 | Promise.resolve = function (value) { 4 | // 这样实现其实与 es6 规范描述的 promise.resolve 实现是一样的 5 | if (value instanceof Promise) return value 6 | 7 | function valueToPromise (val) { 8 | const p = new Promise(Promise._noop) 9 | p._state = 1 10 | p._value = value 11 | 12 | return p 13 | } 14 | 15 | if (typeof value === 'object' || typeof value === 'function') { 16 | try { 17 | // 如果 value 是一个包含 then 方法的对象 18 | const then = value.then 19 | if (typeof then === 'function') { 20 | return new Promise(then.bind(value)) 21 | } 22 | } catch (error) { 23 | return new Promise((_, reject) => { reject(error) }) 24 | } 25 | } 26 | 27 | return valueToPromise(value) 28 | } 29 | 30 | Promise.reject = function (reason) { 31 | return new Promise((_, reject) => { 32 | reject(reason) 33 | }) 34 | } 35 | 36 | Promise.all = function (array) { 37 | !Array.isArray(array) && (array = Array.from(array)) 38 | 39 | return new Promise((resolve, reject) => { 40 | if (array.length === 0) return resolve(array) 41 | 42 | let remaining = array.length 43 | for (let i = 0; i < array.length; i++) { 44 | result(array[i], i) 45 | } 46 | 47 | function result (val, i) { 48 | // 如果 val 是一个正常的值 49 | if (!val || (typeof val !== 'object' && typeof val !== 'function')) { 50 | array[i] = val 51 | --remaining === 0 && resolve(array) 52 | return 53 | } 54 | 55 | // 如果 val 是一个 promise 56 | if (val instanceof Promise && val.then === Promise.prototype.then) { 57 | // 如果是我们自己的 promise 实现,就可以直接根据状态来做 58 | while (val._state === 3) { 59 | val = val._value 60 | } 61 | 62 | if (val._state === 1) return result(val._value, i) 63 | // Promise.all 被拒绝并不影响每个单独的 promise 继续执行 64 | if (val._state === 2) reject(val._value) 65 | 66 | // 为了兼容其他的 promise 实现 67 | val.then(res => result(res, i), reject) 68 | return 69 | } 70 | 71 | // 如果 val 是一个包含 then 方法的对象 72 | if (typeof val.then === 'function') { 73 | const p = new Promise(val.then.bind(val)) 74 | p.then(res => result(res, i), reject) 75 | return 76 | } 77 | } 78 | }) 79 | } 80 | 81 | Promise.race = function (array) { 82 | return new Promise((resolve, reject) => { 83 | for (const val of array) { 84 | Promise.resolve(val).then(resolve, reject) 85 | } 86 | }) 87 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import './es6_extensions' 2 | import Promise from './core' 3 | 4 | export default Promise -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | const Promise = require('./dist/promise.min') 2 | 3 | exports.deferred = function () { 4 | let resolve, reject 5 | const promise = new Promise(function (_resolve, _reject) { 6 | resolve = _resolve 7 | reject = _reject 8 | }) 9 | return { 10 | promise, 11 | resolve, 12 | reject, 13 | } 14 | } 15 | 16 | exports.resolved = Promise.resolve 17 | exports.rejected = Promise.reject -------------------------------------------------------------------------------- /test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imtaotao/promise/6ad7ef7a29f19b857edadc229b6c10cb6d902bc3/test.png -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: './index.js', 3 | output: { 4 | path: 'bundle.js', 5 | }, 6 | devtool: '#cheap-module-eval-source-map', 7 | } --------------------------------------------------------------------------------