├── README.md ├── .gitignore ├── .idea └── vcs.xml ├── package.json ├── test └── index.js ├── prototype ├── promise_async_2.js ├── promise_1.js ├── promise_withCatchError_4.js ├── promise_chain_3.js ├── promise_transmit_value_5.js ├── promise_async_then_6.js ├── promise_nextTick_7.js ├── promise_without_called_8.js ├── promise_with_called_9.js └── promise_final_10.js ├── testPromise.js ├── lib └── promise.js ├── How to implement a Promise.md └── How to implement a Promise_copy.md /README.md: -------------------------------------------------------------------------------- 1 | # JSPI 2 | Just a simple Promise/A+ implementation 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | /node_modules 3 | /.happypack 4 | .idea 5 | *.log 6 | npm-debug.log 7 | npm-debug.log* 8 | 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jspi", 3 | "version": "1.0.0", 4 | "description": "Just a simple Promise/A+ implementation", 5 | "main": "lib/promise", 6 | "scripts": { 7 | "test": "mocha" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/yonglijia/JSPI.git" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/yonglijia/JSPI/issues" 17 | }, 18 | "homepage": "https://github.com/yonglijia/JSPI#readme", 19 | "devDependencies": { 20 | "promises-aplus-tests": "^2.1.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var Promise = require('../lib/promise'); 2 | var promisesAplusTests = require("promises-aplus-tests"); 3 | 4 | var adapter = { 5 | resolved: Promise.resolve, 6 | rejected: Promise.reject, 7 | deferred: function () { 8 | var resolve, reject; 9 | var promise = new Promise(function () { 10 | resolve = arguments[0]; 11 | reject = arguments[1]; 12 | }); 13 | return { 14 | promise: promise, 15 | resolve: resolve, 16 | reject: reject 17 | }; 18 | } 19 | }; 20 | 21 | describe("Promises/A+ Tests", function () { 22 | promisesAplusTests.mocha(adapter); 23 | }); -------------------------------------------------------------------------------- /prototype/promise_async_2.js: -------------------------------------------------------------------------------- 1 | function Promise(executor) { 2 | 3 | let _this = this 4 | _this.status = 'pending' 5 | _this.value = null 6 | _this.reason = null 7 | 8 | //-----------add-------------- 9 | _this.onFulfilledCallbacks=[] 10 | _this.onRejectedCallbacks=[] 11 | 12 | function resolve(value) { 13 | if (_this.status === 'pending') { 14 | _this.status = 'fulfilled' 15 | _this.value = value 16 | //-----------add-------------- 17 | _this.onFulfilledCallbacks.forEach(function (fn) { 18 | fn() 19 | }) 20 | 21 | } 22 | } 23 | 24 | function reject(reason) { 25 | if (_this.status === 'pending') { 26 | _this.status = 'rejected' 27 | _this.reason = reason 28 | 29 | //-----------add-------------- 30 | _this.onRejectedCallbacks.forEach(function (fn) { 31 | fn() 32 | }) 33 | } 34 | } 35 | 36 | executor(function (value) { 37 | resolve(value) 38 | }, function (reason) { 39 | reject(reason) 40 | }) 41 | } 42 | 43 | Promise.prototype.then = function (onFulfilled, onRejected) { 44 | 45 | let _this = this 46 | 47 | if (_this.status == 'fulfilled') { 48 | onFulfilled(_this.value) 49 | } 50 | 51 | if (_this.status == 'rejected') { 52 | onRejected(_this.reason) 53 | } 54 | 55 | //-----------add------------ 56 | if (_this.status == 'pending') { 57 | 58 | _this.onFulfilledCallbacks.push(function () { 59 | onFulfilled(_this.value) 60 | }) 61 | _this.onRejectedCallbacks.push(function () { 62 | onRejected(_this.reason) 63 | }) 64 | } 65 | }; 66 | 67 | //-------------test code-------------- 68 | 69 | (function (type) { 70 | return [() => { 71 | }, () => { 72 | 73 | var promise1 = new Promise((resolve, reject) => { 74 | setTimeout(function () { 75 | //resolve('test simplePromise resolve') 76 | reject('test simplePromise reject') 77 | }, 1000) 78 | }) 79 | 80 | promise1.then(function (value) { 81 | console.log('success:', value) 82 | }, function (reason) { 83 | console.log('failed:', reason) 84 | }) 85 | 86 | }][type] 87 | })(1)() 88 | 89 | -------------------------------------------------------------------------------- /prototype/promise_1.js: -------------------------------------------------------------------------------- 1 | function Promise(executor) { 2 | 3 | let _this = this 4 | _this.status = 'pending' 5 | _this.value = null 6 | _this.reason = null 7 | 8 | function resolve(value) { 9 | if (_this.status === 'pending') { 10 | _this.status = 'fulfilled' 11 | _this.value = value 12 | } 13 | } 14 | 15 | function reject(reason) { 16 | if (_this.status === 'pending') { 17 | _this.status = 'rejected' 18 | _this.reason = reason 19 | } 20 | } 21 | 22 | executor(function (value) { 23 | resolve(value) 24 | }, function (reason) { 25 | reject(reason) 26 | }) 27 | } 28 | 29 | Promise.prototype.then = function (onFulfilled, onRejected) { 30 | 31 | let _this = this 32 | 33 | if (_this.status == 'fulfilled') { 34 | onFulfilled(_this.value) 35 | } 36 | 37 | if (_this.status == 'rejected') { 38 | onRejected(_this.reason) 39 | } 40 | }; 41 | 42 | 43 | //-------------test code-------------- 44 | 45 | (function (type) { 46 | return [() => { 47 | }, () => { 48 | //-------------------normal 49 | var promise1 = new Promise((resolve, reject) => { 50 | resolve('test simplePromise resolve') 51 | //reject('test simplePromise reject') 52 | }) 53 | 54 | promise1.then(function (value) { 55 | console.log('success:', value) 56 | }, function (reason) { 57 | console.log('failed:', reason) 58 | }) 59 | 60 | }, () => { 61 | //Q-------------------without Async 62 | var promise2 = new Promise((resolve, reject) => { 63 | setTimeout(function () { 64 | resolve('test simplePromise resolve') 65 | }, 100) 66 | }) 67 | promise2.then(function (value) { 68 | console.log('success:', value) 69 | }, function (reason) { 70 | console.log('failed:', reason) 71 | }) 72 | 73 | }, () => { 74 | //Q-------------------without Chain 75 | var promise3 = new Promise((resolve, reject) => { 76 | resolve('test simplePromise resolve') 77 | }) 78 | promise3.then(function (value) { 79 | console.log('success:', value) 80 | }, function (reason) { 81 | console.log('failed:', reason) 82 | }).then(function (value) { 83 | console.log('success:', value) 84 | }, function (reason) { 85 | console.log('failed:', reason) 86 | }) 87 | }][type] 88 | })(1)() 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /prototype/promise_withCatchError_4.js: -------------------------------------------------------------------------------- 1 | function Promise(executor) { 2 | //add ---- 3 | if (!(this instanceof Promise)) { 4 | return new Promise(executor); 5 | } 6 | 7 | //add 8 | if (typeof executor !== 'function') { 9 | throw new TypeError('Promise executor is not a function'); 10 | } 11 | 12 | let _this = this 13 | _this.status = 'pending' 14 | _this.value = null 15 | _this.reason = null 16 | _this.onFulfilledCallbacks=[] 17 | _this.onRejectedCallbacks=[] 18 | 19 | function resolve(value) { 20 | if(_this.status === 'pending'){ 21 | _this.status = 'fulfilled' 22 | _this.value = value 23 | _this.onFulfilledCallbacks.forEach(function(fn){ 24 | fn() 25 | }) 26 | } 27 | } 28 | 29 | function reject(reason) { 30 | if(_this.status === 'pending'){ 31 | _this.status = 'rejected' 32 | _this.reason = reason 33 | _this.onRejectedCallbacks.forEach(function(fn){ 34 | fn() 35 | }) 36 | } 37 | } 38 | //add 39 | try{ 40 | executor(function (value) { 41 | resolve(value) 42 | }, function (reason) { 43 | reject(reason) 44 | }) 45 | }catch(e){ 46 | reject(e) 47 | } 48 | 49 | } 50 | 51 | Promise.prototype.then = function (onFulfilled, onRejected) { 52 | 53 | let _this = this 54 | let newPromise 55 | if (_this.status == 'fulfilled') { 56 | newPromise = new Promise(function(resolve,reject){ 57 | //add 58 | try{ 59 | let x = onFulfilled(_this.value) 60 | resolve(x) 61 | }catch(e){ 62 | reject(e) 63 | } 64 | 65 | }) 66 | } 67 | 68 | if (_this.status == 'rejected') { 69 | newPromise = new Promise(function(resolve,reject){ 70 | //add 71 | try{ 72 | let x = onRejected(_this.reason) 73 | resolve(x) 74 | }catch(e){ 75 | reject(e) 76 | } 77 | 78 | }) 79 | } 80 | 81 | if (_this.status == 'pending'){ 82 | newPromise = new Promise(function(resolve,reject){ 83 | 84 | _this.onFulfilledCallbacks.push(function(){ 85 | //add 86 | try{ 87 | let x = onFulfilled(_this.value) 88 | resolve(x) 89 | }catch(e){ 90 | reject(e) 91 | } 92 | }) 93 | 94 | _this.onRejectedCallbacks.push(function(){ 95 | //add 96 | try{ 97 | let x = onRejected(_this.reason) 98 | resolve(x) 99 | }catch(e){ 100 | reject(e) 101 | } 102 | }) 103 | 104 | }) 105 | } 106 | return newPromise 107 | }; 108 | 109 | //-------------test code-------------- 110 | (function(type){ 111 | 112 | return [()=>{ 113 | },()=>{ 114 | //----------normal catch error 115 | let promise1 = new Promise((resolve,reject) => { 116 | throw new Error('error') 117 | }) 118 | promise1.then((value)=>{ 119 | console.log('success:',value) 120 | },(reason)=>{ 121 | console.log('reject:',reason) 122 | }) 123 | },()=>{ 124 | //2can't transmit value 125 | new Promise((resolve,reject)=>{ 126 | resolve(1) 127 | }).then().then().then((value)=>{ 128 | console.log(value) 129 | },(reason)=>{console.log(reason)}) 130 | }][type] 131 | })(``)() 132 | 133 | module.exports = Promise -------------------------------------------------------------------------------- /testPromise.js: -------------------------------------------------------------------------------- 1 | // var SPromise = require('./PPT1_simplePromise_withcatchError_4') 2 | // 3 | // new SPromise(resolve => { 4 | // console.log(1); 5 | // resolve(3); 6 | // new SPromise((resolve) => { 7 | // resolve() 8 | // }).then(() => console.log(4)) 9 | // }).then(num => { 10 | // console.log(num) 11 | // }); 12 | // 13 | // console.log(2) 14 | // setTimeout(()=>{ 15 | // console.log(5) 16 | // }) 17 | // new SPromise(resolve => { 18 | // console.log(1); 19 | // resolve(3); 20 | // new SPromise((resolve) => { 21 | // resolve() 22 | // }).then(() => console.log(4)) 23 | // }).then(num => { 24 | // console.log(num) 25 | // }); 26 | // 27 | // console.log(2) 28 | 29 | 30 | // new Promise((resolve,reject)=>{ 31 | // resolve(new Promise((resolve,reject)=>{ 32 | // resolve(1) 33 | // })) 34 | // }).then((value)=>{ 35 | // console.log(value) 36 | // },(reason)=>{ 37 | // console.log(reason) 38 | // }) 39 | 40 | //--------testCode----------- 41 | 42 | (function (type) { 43 | 44 | return [() => { 45 | }, ()=>{ 46 | 47 | //5.2 48 | var promise2 = new Promise((resolve,reject) => { 49 | setTimeout(function(){ 50 | resolve('test simplePromise resolve') 51 | },100) 52 | }) 53 | 54 | setTimeout(() => { 55 | promise2.then(function(value){ 56 | console.log('success:',value) 57 | },function(reason){ 58 | console.log('failed:',reason) 59 | }) 60 | 61 | console.log('end') 62 | }, 200); 63 | 64 | },() => { 65 | //6.2 //primary 66 | setTimeout(() => { 67 | console.log(5) 68 | }) 69 | new Promise(resolve => { 70 | console.log(1); 71 | resolve(3); 72 | new Promise((resolve) => { 73 | resolve() 74 | }).then(() => console.log(4)) 75 | }).then(num => { 76 | console.log(num) 77 | }); 78 | 79 | console.log(2) 80 | 81 | }, () => { 82 | //6.settimeout 83 | var Promise = require('./prototype/promise_async_then_6') 84 | 85 | setTimeout(() => { 86 | console.log(5) 87 | }) 88 | new Promise(resolve => { 89 | console.log(1); 90 | resolve(3); 91 | new Promise((resolve) => { 92 | resolve() 93 | }).then(() => console.log(4)) 94 | }).then(num => { 95 | console.log(num) 96 | }); 97 | 98 | console.log(2) 99 | 100 | }, () => { 101 | //3.nextTick 102 | var Promise = require('./prototype/promise_nextTick_7') 103 | 104 | setTimeout(() => { 105 | console.log(5) 106 | }) 107 | new Promise(resolve => { 108 | console.log(1); 109 | resolve(3); 110 | new Promise((resolve) => { 111 | resolve() 112 | }).then(() => console.log(4)) 113 | }).then(num => { 114 | console.log(num) 115 | }); 116 | 117 | console.log(2) 118 | 119 | }, () => { 120 | //4.transmit value: 6 not work 121 | var Promise = require('./prototype/promise_nextTick_7') 122 | 123 | var p = new Promise(resolve=> { 124 | resolve(3); 125 | }) 126 | p.then().then().then((value) => { 127 | console.log(value) 128 | }); 129 | },() => { 130 | //5.transmit value: 7 ok 131 | var Promise = require('./prototype/promise_transmit_value_5') 132 | 133 | var p = new Promise(resolve => { 134 | resolve(3); 135 | }) 136 | p.then().then().then((value) => { 137 | console.log(value) 138 | }); 139 | }][type] 140 | 141 | }(2)()) -------------------------------------------------------------------------------- /prototype/promise_chain_3.js: -------------------------------------------------------------------------------- 1 | function Promise(executor) { 2 | 3 | let _this = this 4 | 5 | _this.status = 'pending' 6 | _this.value = null 7 | _this.reason = null 8 | _this.onFulfilledCallbacks=[] 9 | _this.onRejectedCallbacks=[] 10 | 11 | 12 | function resolve(value) { 13 | if(_this.status === 'pending'){ 14 | _this.status = 'fulfilled' 15 | _this.value = value 16 | _this.onFulfilledCallbacks.forEach(function(fn){ 17 | fn() 18 | }) 19 | 20 | } 21 | } 22 | 23 | function reject(reason) { 24 | if(_this.status === 'pending'){ 25 | _this.status = 'rejected' 26 | _this.reason = reason 27 | _this.onRejectedCallbacks.forEach(function(fn){ 28 | fn() 29 | }) 30 | } 31 | } 32 | 33 | executor(function (value) { 34 | resolve(value) 35 | }, function (reason) { 36 | reject(reason) 37 | }) 38 | } 39 | 40 | Promise.prototype.then = function (onFulfilled, onRejected) { 41 | 42 | let _this = this 43 | let newPromise 44 | if (_this.status == 'fulfilled') { 45 | //add 46 | newPromise = new Promise(function(resolve,reject){ 47 | let x = onFulfilled(_this.value) 48 | resolve(x) 49 | }) 50 | } 51 | 52 | //不论 promise1 被 reject 还是被 resolve 时 newPromise 都会被 resolve,只有出现异常时才会被 rejected 53 | if (_this.status == 'rejected') { 54 | //add 55 | newPromise = new Promise(function(resolve,reject){ 56 | let x = onRejected(_this.reason) 57 | resolve(x) //为什么rejected了还resolve呢? 58 | }) 59 | } 60 | 61 | if (_this.status == 'pending'){ 62 | //add 63 | newPromise = new Promise(function(resolve,reject){ 64 | _this.onFulfilledCallbacks.push(function(){ 65 | let x = onFulfilled(_this.value) 66 | resolve(x) 67 | }) 68 | 69 | _this.onRejectedCallbacks.push(function(){ 70 | let x = onRejected(_this.reason) 71 | resolve(x) 72 | }) 73 | }) 74 | } 75 | return newPromise 76 | }; 77 | 78 | 79 | //-------------test code-------------- 80 | 81 | (function(type){ 82 | 83 | return [()=>{},()=>{ 84 | //-------------------normal 85 | let promise1 = new Promise((resolve,reject) => { 86 | resolve('test simplePromise resolve') 87 | }) 88 | 89 | promise1.then(function(value){ 90 | console.log('success:',value) 91 | return 'success next' 92 | },function(reason){ 93 | console.log('failed:',reason) 94 | }).then(function(value){ 95 | console.log('success:',value) 96 | },function(reason){ 97 | console.log('failed:',reason) 98 | }) 99 | },()=>{ 100 | //-------------------normal reject 101 | let promise2 = new Promise((resolve,reject) => { 102 | reject('test simplePromise reject') 103 | }) 104 | 105 | promise2.then(function(value){ 106 | console.log('success:',value) 107 | },function(reason){ 108 | console.log('failed:',reason) 109 | return 'failed next' 110 | }).then(function(value){ 111 | console.log('success:',value) 112 | },function(reason){ 113 | console.log('failed:',reason) 114 | }) 115 | },()=>{ 116 | //Q-----------------can't catch error 117 | let promise3 = new Promise((resolve,reject) => { 118 | throw new Error('error') 119 | }) 120 | promise3.then((value)=>{ 121 | console.log('success:',value) 122 | },(reason)=>{ 123 | console.log('reject:',reason) 124 | }) 125 | }][type] 126 | })(3)() 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /prototype/promise_transmit_value_5.js: -------------------------------------------------------------------------------- 1 | function Promise(executor) { 2 | if (!(this instanceof Promise)) { 3 | return new Promise(executor); 4 | } 5 | 6 | if (typeof executor !== 'function') { 7 | throw new TypeError('Promise executor is not a function'); 8 | } 9 | 10 | let _this = this 11 | _this.status = 'pending' 12 | _this.value = null 13 | _this.reason = null 14 | _this.onFulfilledCallbacks=[] 15 | _this.onRejectedCallbacks=[] 16 | 17 | function resolve(value) { 18 | if(_this.status === 'pending'){ 19 | _this.status = 'fulfilled' 20 | _this.value = value 21 | _this.onFulfilledCallbacks.forEach(function(fn){ 22 | fn() 23 | }) 24 | } 25 | } 26 | 27 | function reject(reason) { 28 | if(_this.status === 'pending'){ 29 | _this.status = 'rejected' 30 | _this.reason = reason 31 | _this.onRejectedCallbacks.forEach(function(fn){ 32 | fn() 33 | }) 34 | } 35 | } 36 | 37 | try{ 38 | executor(function (value) { 39 | resolve(value) 40 | }, function (reason) { 41 | reject(reason) 42 | }) 43 | }catch(e){ 44 | reject(e) 45 | } 46 | 47 | } 48 | 49 | Promise.prototype.then = function (onFulfilled, onRejected) { 50 | 51 | onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) { 52 | return value 53 | } 54 | onRejected = typeof onRejected === 'function' ? onRejected : function (err) { 55 | throw err 56 | } 57 | 58 | let _this = this 59 | let newPromise 60 | if (_this.status == 'fulfilled') { 61 | newPromise = new Promise(function(resolve,reject){ 62 | //add 63 | try{ 64 | let x = onFulfilled(_this.value) 65 | resolve(x) 66 | }catch(e){ 67 | reject(e) 68 | } 69 | 70 | }) 71 | } 72 | 73 | if (_this.status == 'rejected') { 74 | newPromise = new Promise(function(resolve,reject){ 75 | //add 76 | try{ 77 | let x = onRejected(_this.reason) 78 | resolve(x) 79 | }catch(e){ 80 | reject(e) 81 | } 82 | 83 | }) 84 | } 85 | 86 | if (_this.status == 'pending'){ 87 | newPromise = new Promise(function(resolve,reject){ 88 | 89 | _this.onFulfilledCallbacks.push(function(){ 90 | //add 91 | try{ 92 | let x = onFulfilled(_this.value) 93 | resolve(x) 94 | }catch(e){ 95 | reject(e) 96 | } 97 | }) 98 | 99 | _this.onRejectedCallbacks.push(function(){ 100 | //add 101 | try{ 102 | let x = onRejected(_this.reason) 103 | resolve(x) 104 | }catch(e){ 105 | reject(e) 106 | } 107 | }) 108 | 109 | }) 110 | } 111 | return newPromise 112 | }; 113 | 114 | //-------------test code-------------- 115 | (function(type){ 116 | 117 | return [()=>{ 118 | },()=>{ 119 | //--------normal 120 | new Promise((resolve,reject)=>{ 121 | resolve(1) 122 | }).then().then().then((value)=>{ 123 | console.log(value) 124 | },(reason)=>{console.log(reason)}) 125 | },()=>{ 126 | 127 | //2 Unhandle situation when sync call onFulfill and onReject 128 | 129 | var promise2 = new Promise((resolve,reject) => { 130 | setTimeout(function(){ 131 | resolve('test simplePromise resolve') 132 | },100) 133 | }) 134 | 135 | setTimeout(() => { 136 | promise2.then(function(value){ 137 | console.log('success:',value) 138 | },function(reason){ 139 | console.log('failed:',reason) 140 | }) 141 | 142 | console.log('end') 143 | }, 200); 144 | 145 | // var promise2 = new Promise((resolve,reject) => { 146 | // resolve('test simplePromise resolve') 147 | // }) 148 | // 149 | // promise2.then(function(value){ 150 | // console.log('success:',value) 151 | // },function(reason){ 152 | // console.log('failed:',reason) 153 | // }) 154 | 155 | // console.log('end') 156 | //5.2 157 | 158 | }][type] 159 | })(2)() 160 | 161 | module.exports = Promise -------------------------------------------------------------------------------- /prototype/promise_async_then_6.js: -------------------------------------------------------------------------------- 1 | function Promise(executor) { 2 | 3 | if (!(this instanceof Promise)) { 4 | return new Promise(executor); 5 | } 6 | 7 | if (typeof executor !== 'function') { 8 | throw new TypeError('Promise executor is not a function'); 9 | } 10 | 11 | let _this = this 12 | _this.status = 'pending' 13 | _this.value = null 14 | _this.reason = null 15 | _this.onRejectedCallbacks=[] 16 | _this.onResolvedCallbacks=[] 17 | 18 | function resolve(value) { 19 | if(_this.status === 'pending'){ 20 | _this.status = 'resolved' 21 | _this.value = value 22 | _this.onResolvedCallbacks.forEach(function(fn){ 23 | fn() 24 | }) 25 | } 26 | } 27 | 28 | function reject(reason) { 29 | if(_this.status === 'pending'){ 30 | _this.status = 'rejected' 31 | _this.reason = reason 32 | _this.onRejectedCallbacks.forEach(function(fn){ 33 | fn() 34 | }) 35 | } 36 | } 37 | try{ 38 | executor(function (value) { 39 | resolve(value) 40 | }, function (reason) { 41 | reject(reason) 42 | }) 43 | }catch(e){ 44 | reject(e) 45 | } 46 | 47 | } 48 | 49 | Promise.prototype.then = function (onFulfilled, onRejected) { 50 | 51 | onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) { 52 | return value 53 | } 54 | onRejected = typeof onRejected === 'function' ? onRejected : function (err) { 55 | throw err 56 | } 57 | let _this = this 58 | let newPromise 59 | if (_this.status == 'resolved') { 60 | 61 | newPromise = new Promise(function(resolve,reject){ 62 | //add 63 | setTimeout(function () { 64 | try{ 65 | let x = onFulfilled(_this.value) 66 | resolve(x) 67 | }catch(e){ 68 | reject(e) 69 | } 70 | }) 71 | 72 | }) 73 | } 74 | 75 | if (_this.status == 'rejected') { 76 | newPromise = new Promise(function(resolve,reject){ 77 | //add 78 | setTimeout(function () { 79 | try{ 80 | let x = onRejected(_this.reason) 81 | resolve(x) 82 | }catch(e){ 83 | reject(e) 84 | } 85 | }) 86 | 87 | }) 88 | } 89 | 90 | if (_this.status == 'pending'){ 91 | newPromise = new Promise(function(resolve,reject){ 92 | //add 93 | _this.onResolvedCallbacks.push(function(){ 94 | setTimeout(function () { 95 | try { 96 | let x = onFulfilled(_this.value) 97 | resolve(x) 98 | } catch (e) { 99 | reject(e) 100 | } 101 | }) 102 | }) 103 | //add 104 | _this.onRejectedCallbacks.push(function(){ 105 | setTimeout(function () { 106 | try { 107 | let x = onFulfilled(_this.value) 108 | resolve(x) 109 | } catch (e) { 110 | reject(e) 111 | } 112 | }) 113 | }) 114 | 115 | }) 116 | } 117 | return newPromise 118 | }; 119 | 120 | 121 | //-------------test code-------------- 122 | (function (type) { 123 | 124 | return [() => { 125 | },()=>{ 126 | //-----------normal 127 | var promise2 = new Promise((resolve,reject) => { 128 | setTimeout(function(){ 129 | resolve('test simplePromise resolve') 130 | },100) 131 | }) 132 | 133 | setTimeout(() => { 134 | promise2.then(function(value){ 135 | console.log('success:',value) 136 | },function(reason){ 137 | console.log('failed:',reason) 138 | }) 139 | 140 | console.log('end') 141 | }, 200); 142 | }, () => { 143 | //Q-----------------setTimeout problem 144 | setTimeout(() => { 145 | console.log(5) 146 | }) 147 | new Promise(resolve => { 148 | console.log(1); 149 | resolve(3); 150 | new Promise((resolve) => { 151 | resolve() 152 | }).then(() => console.log(4)) 153 | }).then(num => { 154 | console.log(num) 155 | }); 156 | 157 | console.log(2) 158 | //6.2 testPromise 159 | }][type] 160 | 161 | }(2)()) 162 | 163 | module.exports = Promise -------------------------------------------------------------------------------- /prototype/promise_nextTick_7.js: -------------------------------------------------------------------------------- 1 | function Promise(executor) { 2 | if (!(this instanceof Promise)) { 3 | return new Promise(executor); 4 | } 5 | 6 | if (typeof executor !== 'function') { 7 | throw new TypeError('Promise executor is not a function'); 8 | } 9 | let _this = this 10 | _this.status = 'pending' 11 | _this.value = null 12 | _this.reason = null 13 | _this.onRejectedCallbacks = [] 14 | _this.onResolvedCallbacks = [] 15 | 16 | function resolve(value) { 17 | if (_this.status === 'pending') { 18 | _this.status = 'resolved' 19 | _this.value = value 20 | _this.onResolvedCallbacks.forEach(function (fn) { 21 | fn() 22 | }) 23 | } 24 | 25 | } 26 | 27 | function reject(reason) { 28 | if (_this.status === 'pending') { 29 | _this.status = 'rejected' 30 | _this.reason = reason 31 | _this.onRejectedCallbacks.forEach(function (fn) { 32 | fn() 33 | }) 34 | } 35 | } 36 | 37 | try { 38 | executor(function (value) { 39 | resolve(value) 40 | }, function (reason) { 41 | reject(reason) 42 | }) 43 | } catch (e) { 44 | reject(e) 45 | } 46 | 47 | } 48 | 49 | Promise.prototype.then = function (onFulfilled, onRejected) { 50 | onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) { 51 | return value 52 | } 53 | onRejected = typeof onRejected === 'function' ? onRejected : function (err) { 54 | throw err 55 | } 56 | 57 | let _this = this 58 | let newPromise 59 | if (_this.status == 'resolved') { 60 | newPromise = new Promise(function (resolve, reject) { 61 | //add 62 | process.nextTick(function () { 63 | try { 64 | let x = onFulfilled(_this.value) 65 | resolve(x) 66 | } catch (e) { 67 | reject(e) 68 | } 69 | }) 70 | }) 71 | } 72 | 73 | if (_this.status == 'rejected') { 74 | newPromise = new Promise(function (resolve, reject) { 75 | //add 76 | process.nextTick(function () { 77 | try { 78 | let x = onRejected(_this.reason) 79 | resolve(x) 80 | } catch (e) { 81 | reject(e) 82 | } 83 | }) 84 | 85 | }) 86 | } 87 | 88 | if (_this.status == 'pending') { 89 | newPromise = new Promise(function (resolve, reject) { 90 | //add 91 | _this.onResolvedCallbacks.push(function(){ 92 | process.nextTick(function () { 93 | try { 94 | let x = onFulfilled(_this.value) 95 | resolve(x) 96 | } catch (e) { 97 | reject(e) 98 | } 99 | }) 100 | }) 101 | //add 102 | _this.onRejectedCallbacks.push(function(){ 103 | process.nextTick(function () { 104 | try { 105 | let x = onFulfilled(_this.value) 106 | resolve(x) 107 | } catch (e) { 108 | reject(e) 109 | } 110 | }) 111 | }) 112 | 113 | }) 114 | } 115 | return newPromise 116 | }; 117 | //-------------test code-------------- 118 | (function (type) { 119 | 120 | return [() => { 121 | },()=>{ 122 | //-----------------normal 123 | setTimeout(() => { 124 | console.log(5) 125 | }) 126 | new Promise(resolve => { 127 | console.log(1); 128 | resolve(3); 129 | new Promise((resolve) => { 130 | resolve() 131 | }).then(() => console.log(4)) 132 | }).then(num => { 133 | console.log(num) 134 | }); 135 | 136 | console.log(2) 137 | }, () => { 138 | //Q--------------- 139 | new Promise((resolve, reject) => { 140 | resolve(new Promise((resolve) => { 141 | resolve(1) 142 | })) 143 | }).then((value) => { 144 | console.log('success1:', value) 145 | }, (reason) => { 146 | console.log('failed1:', reason) 147 | }) 148 | 149 | //Q--------------thenable 150 | new Promise((resolve, reject) => { 151 | resolve({ 152 | then: (resolve,reject)=>{ 153 | resolve(1) 154 | } 155 | }) 156 | }).then((value) => { 157 | console.log('success2:', value) 158 | }, (reason) => { 159 | console.log('failed2:', reason) 160 | }) 161 | 162 | //问题:resolve是自己,thenable,或者是promise,thenable 163 | 164 | }][type] 165 | 166 | }(2)()) 167 | module.exports = Promise -------------------------------------------------------------------------------- /prototype/promise_without_called_8.js: -------------------------------------------------------------------------------- 1 | 2 | function Promise(executor) { 3 | if (!(this instanceof Promise)) { 4 | return new Promise(executor); 5 | } 6 | 7 | if (typeof executor !== 'function') { 8 | throw new TypeError('Promise executor is not a function'); 9 | } 10 | 11 | let _this = this 12 | _this.status = 'pending' 13 | _this.value = null 14 | _this.reason = null 15 | _this.onRejectedCallbacks = [] 16 | _this.onResolvedCallbacks = [] 17 | 18 | function resolve(value) { 19 | resolvePromise(_this,value,fulfill,reject) 20 | } 21 | 22 | function fulfill(value){ //只接收普通值 23 | if (_this.status === 'pending') { 24 | _this.status = 'resolved' 25 | _this.value = value 26 | _this.onResolvedCallbacks.forEach(function (fn) { 27 | fn() 28 | }) 29 | } 30 | } 31 | 32 | function reject(reason) { 33 | if (_this.status === 'pending') { 34 | _this.status = 'rejected' 35 | _this.reason = reason 36 | _this.onRejectedCallbacks.forEach(function (fn) { 37 | fn() 38 | }) 39 | } 40 | } 41 | 42 | 43 | 44 | try { 45 | executor(function (value) { 46 | resolve(value) 47 | }, function (reason) { 48 | reject(reason) 49 | }) 50 | } catch (e) { 51 | reject(e) 52 | } 53 | 54 | } 55 | 56 | 57 | function resolvePromise(promise,x,fulfill,reject) { 58 | 59 | if (promise === x) {//2.3.1 传进来的x与当前promise相同,报错 60 | return reject(new TypeError('循环引用了')) 61 | } 62 | //2.3.2 63 | if (x instanceof Promise) { 64 | //2.3.2.1 65 | if (x.status === 'pending') { //x状态还未改变,返回的下一个promise的resove的接收的值y不确定,对其递归处理 66 | x.then(function(y) { 67 | resolvePromise(promise, y, fulfill, reject) 68 | }, reject) 69 | } else { 70 | //2.3.2.2 2.3.2.3 71 | //状态确定,如果fulfill那传进来的肯定是普通值,如果reject直接处理,不管你抛出来的是什么东东 72 | x.then(fulfill, reject) 73 | } 74 | return 75 | } 76 | fulfill(x) 77 | } 78 | 79 | Promise.prototype.then = function (onFulfilled, onRejected) { 80 | 81 | onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) { 82 | return value 83 | } 84 | onRejected = typeof onRejected === 'function' ? onRejected : function (err) { 85 | throw err 86 | } 87 | 88 | let _this = this 89 | let newPromise 90 | if (_this.status === 'resolved') { 91 | newPromise = new Promise(function (resolve, reject) { 92 | process.nextTick(function () { 93 | try { 94 | let x = onFulfilled(_this.value) 95 | resolve(x) 96 | } catch (e) { 97 | reject(e) 98 | } 99 | }) 100 | }) 101 | } 102 | 103 | if (_this.status === 'rejected') { 104 | newPromise = new Promise(function (resolve, reject) { 105 | process.nextTick(function () { 106 | try { 107 | let x = onRejected(_this.reason) 108 | resolve(x) 109 | } catch (e) { 110 | reject(e) 111 | } 112 | }) 113 | }) 114 | } 115 | 116 | if (_this.status === 'pending') { 117 | newPromise = new Promise(function (resolve, reject) { 118 | 119 | _this.onResolvedCallbacks.push(function () { 120 | process.nextTick(function () { 121 | try { 122 | let x = onFulfilled(_this.value) 123 | resolve(x) 124 | } catch (e) { 125 | reject(e) 126 | } 127 | }) 128 | 129 | }) 130 | 131 | 132 | _this.onRejectedCallbacks.push(function () { 133 | process.nextTick(function () { 134 | try { 135 | let x = onRejected(_this.reason) 136 | resolve(x) 137 | } catch (e) { 138 | reject(e) 139 | } 140 | }) 141 | }) 142 | 143 | }) 144 | } 145 | return newPromise 146 | }; 147 | 148 | //-------------test code-------------- 149 | (function (type) { 150 | 151 | return [() => {}, ()=>{ 152 | //--------------- normal 153 | new Promise((resolve, reject) => { 154 | resolve(new Promise((resolve) => { 155 | resolve(1) 156 | })) 157 | }).then((value) => { 158 | console.log('success1:', value) 159 | }, (reason) => { 160 | console.log('failed1:', reason) 161 | }) 162 | 163 | }, ()=>{ 164 | 165 | //Q--------------withoutcalled 166 | new Promise((resolve, reject) => { 167 | resolve(new Promise((resolve) => { 168 | resolve(1) 169 | })) 170 | reject('error') 171 | }).then((value) => { 172 | console.log('success:', value) 173 | }, (reason) => { 174 | console.log('failed:', reason) 175 | }) 176 | 177 | },][type] 178 | 179 | }(1)()) 180 | 181 | 182 | module.exports = Promise -------------------------------------------------------------------------------- /prototype/promise_with_called_9.js: -------------------------------------------------------------------------------- 1 | 2 | function Promise(executor) { 3 | if (!(this instanceof Promise)) { 4 | return new Promise(executor); 5 | } 6 | 7 | if (typeof executor !== 'function') { 8 | throw new TypeError('Promise executor is not a function'); 9 | } 10 | 11 | let _this = this 12 | _this.status = 'pending' 13 | _this.value = null 14 | _this.reason = null 15 | _this.onRejectedCallbacks = [] 16 | _this.onResolvedCallbacks = [] 17 | 18 | function resolve(value) { 19 | resolvePromise(_this,value,fulfill,reject) 20 | } 21 | 22 | function fulfill(value){ //只接收普通值 23 | if (_this.status === 'pending') { 24 | _this.status = 'resolved' 25 | _this.value = value 26 | _this.onResolvedCallbacks.forEach(function (fn) { 27 | fn() 28 | }) 29 | } 30 | } 31 | 32 | function reject(reason) { 33 | if (_this.status === 'pending') { 34 | _this.status = 'rejected' 35 | _this.reason = reason 36 | _this.onRejectedCallbacks.forEach(function (fn) { 37 | fn() 38 | }) 39 | } 40 | } 41 | 42 | 43 | 44 | try { 45 | let called = false 46 | executor(function (value) { 47 | if(called) return 48 | called = true 49 | resolve(value) 50 | }, function (reason) { 51 | if(called) return 52 | called = true 53 | reject(reason) 54 | }) 55 | } catch (e) { 56 | reject(e) 57 | } 58 | 59 | } 60 | 61 | function resolvePromise(promise,x,fulfill,reject) { 62 | 63 | if (promise === x) {//2.3.1 传进来的x与当前promise相同,报错 64 | return reject(new TypeError('循环引用了')) 65 | } 66 | //2.3.2 67 | if (x instanceof Promise) { 68 | //2.3.2.1 69 | if (x.status === 'pending') { //x状态还未改变,返回的下一个promise的resove的接收的值y不确定,对其递归处理 70 | x.then(function(y) { 71 | resolvePromise(promise, y, fulfill, reject) 72 | }, reject) 73 | } else { 74 | //2.3.2.2 2.3.2.3 75 | //状态确定,如果fulfill那传进来的肯定是普通值,如果reject直接处理,不管你抛出来的是什么东东 76 | x.then(fulfill, reject) 77 | } 78 | return 79 | } 80 | fulfill(x) 81 | } 82 | 83 | Promise.prototype.then = function (onFulfilled, onRejected) { 84 | 85 | onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) { 86 | return value 87 | } 88 | onRejected = typeof onRejected === 'function' ? onRejected : function (err) { 89 | throw err 90 | } 91 | 92 | let _this = this 93 | let newPromise 94 | if (_this.status === 'resolved') { 95 | newPromise = new Promise(function (resolve, reject) { 96 | process.nextTick(function () { 97 | try { 98 | let x = onFulfilled(_this.value) 99 | resolve(x) 100 | } catch (e) { 101 | reject(e) 102 | } 103 | }) 104 | }) 105 | } 106 | 107 | if (_this.status === 'rejected') { 108 | newPromise = new Promise(function (resolve, reject) { 109 | process.nextTick(function () { 110 | try { 111 | let x = onRejected(_this.reason) 112 | resolve(x) 113 | } catch (e) { 114 | reject(e) 115 | } 116 | }) 117 | }) 118 | } 119 | 120 | if (_this.status === 'pending') { 121 | newPromise = new Promise(function (resolve, reject) { 122 | 123 | _this.onResolvedCallbacks.push(function () { 124 | process.nextTick(function () { 125 | try { 126 | let x = onFulfilled(_this.value) 127 | resolve(x) 128 | } catch (e) { 129 | reject(e) 130 | } 131 | }) 132 | 133 | }) 134 | 135 | 136 | _this.onRejectedCallbacks.push(function () { 137 | process.nextTick(function () { 138 | try { 139 | let x = onRejected(_this.reason) 140 | resolve(x) 141 | } catch (e) { 142 | reject(e) 143 | } 144 | }) 145 | }) 146 | 147 | }) 148 | } 149 | return newPromise 150 | }; 151 | 152 | 153 | //-------------test code-------------- 154 | (function (type) { 155 | 156 | return [() => {}, () => { 157 | //---------normal 158 | new Promise((resolve, reject) => { 159 | resolve(new Promise((resolve) => { 160 | resolve(1) 161 | })) 162 | reject('error') 163 | }).then((value) => { 164 | console.log('success:', value) 165 | }, (reason) => { 166 | console.log('failed:', reason) 167 | }) 168 | }, () => { 169 | //Q---------can't resolve thenable 170 | new Promise((resolve, reject) => { 171 | resolve({ 172 | then: (resolve,reject)=>{ 173 | resolve(1) 174 | } 175 | }) 176 | }).then((value) => { 177 | console.log('success:', value) 178 | }, (reason) => { 179 | console.log('failed:', reason) 180 | }) 181 | }][type] 182 | 183 | }(1)()) 184 | 185 | module.exports = Promise -------------------------------------------------------------------------------- /prototype/promise_final_10.js: -------------------------------------------------------------------------------- 1 | function Promise(executor) { 2 | if (!(this instanceof Promise)) { 3 | return new Promise(executor); 4 | } 5 | 6 | if (typeof executor !== 'function') { 7 | throw new TypeError('Promise executor is not a function'); 8 | } 9 | 10 | let _this = this 11 | _this.status = 'pending' 12 | _this.value = null 13 | _this.reason = null 14 | _this.onRejectedCallbacks = [] 15 | _this.onResolvedCallbacks = [] 16 | 17 | function resolve(value) { 18 | resolvePromise(_this,value,fulfill,reject) 19 | } 20 | 21 | function fulfill(value){ //只接收普通值 22 | if (_this.status === 'pending') { 23 | _this.status = 'resolved' 24 | _this.value = value 25 | _this.onResolvedCallbacks.forEach(function (fn) { 26 | fn() 27 | }) 28 | } 29 | } 30 | 31 | function reject(reason) { 32 | if (_this.status === 'pending') { 33 | _this.status = 'rejected' 34 | _this.reason = reason 35 | _this.onRejectedCallbacks.forEach(function (fn) { 36 | fn() 37 | }) 38 | } 39 | } 40 | 41 | 42 | 43 | try { 44 | let called = false 45 | executor(function (value) { 46 | if(called) return 47 | called = true 48 | resolve(value) 49 | }, function (reason) { 50 | if(called) return 51 | called = true 52 | reject(reason) 53 | }) 54 | } catch (e) { 55 | reject(e) 56 | } 57 | 58 | } 59 | 60 | function resolvePromise(promise,x,fulfill,reject) { 61 | 62 | if (promise === x) {//2.3.1 传进来的x与当前promise相同,报错 63 | return reject(new TypeError('循环引用了')) 64 | } 65 | 66 | //2.3.2 x如果是一个promise 67 | if (x instanceof Promise) { 68 | //2.3.2.1 69 | if (x.status === 'pending') { //x状态还未改变,返回的下一个promise的resove的接收的值y不确定,对其递归处理 70 | x.then(function(y) { 71 | resolvePromise(promise, y, fulfill, reject) 72 | },reject) 73 | } else { 74 | //2.3.2.2 , 2.3.2.3 75 | //状态确定,如果fulfill那传进来的肯定是普通值,如果reject直接处理,不管你抛出来的是什么东东 76 | x.then(fulfill, reject) 77 | } 78 | return; 79 | } 80 | let called = false; 81 | //2.3.3 82 | //x 是一个thenable 83 | if(x !== null && (typeof x === 'object' || typeof x === 'function')){ 84 | try { 85 | //2.3.3.1 86 | let then = x.then; 87 | if (typeof then === 'function') {//2.3.3.3 {then:: (resolve,reject)=>{resolve(1)}}} 88 | then.call(x,(y)=>{ 89 | if (called) return 90 | called = true 91 | resolvePromise(promise,y,fulfill,reject) 92 | },(err)=>{ 93 | if (called) return 94 | called = true 95 | reject(err) 96 | }) 97 | }else{//2.3.3.2 x: {then:1},是一个带then属性的普通值 98 | fulfill(x) 99 | } 100 | }catch(e){//2.3.3.2 可以参见上面说的异常情况2 101 | if (called) return 102 | called = true; 103 | reject(e); 104 | } 105 | }else{//2.3.3.4,x是一个普通值 106 | fulfill(x) 107 | } 108 | } 109 | Promise.prototype.then = function (onFulfilled, onRejected) { 110 | 111 | onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) { 112 | return value 113 | } 114 | onRejected = typeof onRejected === 'function' ? onRejected : function (err) { 115 | throw err 116 | } 117 | 118 | let _this = this 119 | let newPromise 120 | if (_this.status === 'resolved') { 121 | newPromise = new Promise(function (resolve, reject) { 122 | process.nextTick(function () { 123 | try { 124 | let x = onFulfilled(_this.value) 125 | resolve(x) 126 | } catch (e) { 127 | reject(e) 128 | } 129 | }) 130 | }) 131 | } 132 | 133 | if (_this.status === 'rejected') { 134 | newPromise = new Promise(function (resolve, reject) { 135 | process.nextTick(function () { 136 | try { 137 | let x = onRejected(_this.reason) 138 | resolve(x) 139 | } catch (e) { 140 | reject(e) 141 | } 142 | }) 143 | }) 144 | } 145 | 146 | if (_this.status === 'pending') { 147 | newPromise = new Promise(function (resolve, reject) { 148 | 149 | _this.onResolvedCallbacks.push(function () { 150 | process.nextTick(function () { 151 | try { 152 | let x = onFulfilled(_this.value) 153 | resolve(x) 154 | } catch (e) { 155 | reject(e) 156 | } 157 | }) 158 | 159 | }) 160 | 161 | 162 | _this.onRejectedCallbacks.push(function () { 163 | process.nextTick(function () { 164 | try { 165 | let x = onRejected(_this.reason) 166 | resolve(x) 167 | } catch (e) { 168 | reject(e) 169 | } 170 | }) 171 | }) 172 | 173 | }) 174 | } 175 | return newPromise 176 | } 177 | 178 | Promise.deferred = Promise.defer = function () { 179 | var dfd = {} 180 | dfd.promise = new Promise(function (resolve, reject) { 181 | dfd.resolve = resolve 182 | dfd.reject = reject 183 | }) 184 | return dfd 185 | }; 186 | 187 | //-------------test code-------------- 188 | (function (type) { 189 | 190 | return [() => {}, () => { 191 | 192 | new Promise((resolve, reject) => { 193 | resolve({ 194 | then: (resolve,reject)=>{ 195 | resolve(1) 196 | } 197 | }) 198 | }).then((value) => { 199 | console.log('success:', value) 200 | }, (reason) => { 201 | console.log('failed:', reason) 202 | }) 203 | 204 | }][type] 205 | 206 | }(1)()) 207 | 208 | module.exports = Promise -------------------------------------------------------------------------------- /lib/promise.js: -------------------------------------------------------------------------------- 1 | var asyncCall = (process && process.nextTick) || setImmediate || function (callback) { 2 | if (typeof callback !== 'function') 3 | throw new TypeError('callback is not a function'); 4 | var Mutation = window.MutationObserver || window.WebKitMutationObserver; 5 | var observer = new Mutation(callback); 6 | var element = document.createTextNode(''); 7 | observer.observe(element, { 8 | characterData: true 9 | }); 10 | element.data = 1; 11 | } || function (callback) { 12 | if (typeof callback !== 'function') 13 | throw new TypeError('callback is not a function'); 14 | setTimeout(callback, 0); 15 | }; 16 | 17 | function Promise(executor) { 18 | if (!(this instanceof Promise)) { 19 | return new Promise(executor); 20 | } 21 | 22 | if (typeof executor !== 'function') { 23 | throw new TypeError('Promise executor is not a function'); 24 | } 25 | 26 | let _this = this 27 | _this.status = 'pending' 28 | _this.value = null 29 | _this.reason = null 30 | _this.onRejectedCallbacks = [] 31 | _this.onResolvedCallbacks = [] 32 | 33 | function resolve(value) { 34 | resolvePromise(_this,value,fulfill,reject) 35 | } 36 | 37 | function fulfill(value){ //只接收普通值 38 | if (_this.status === 'pending') { 39 | _this.status = 'resolved' 40 | _this.value = value 41 | _this.onResolvedCallbacks.forEach(function (fn) { 42 | fn() 43 | }) 44 | } 45 | } 46 | 47 | function reject(reason) { 48 | if (_this.status === 'pending') { 49 | _this.status = 'rejected' 50 | _this.reason = reason 51 | _this.onRejectedCallbacks.forEach(function (fn) { 52 | fn() 53 | }) 54 | } 55 | } 56 | 57 | 58 | try { 59 | let called = false 60 | executor(function (value) { 61 | if(called) return 62 | called = true 63 | resolve(value) 64 | }, function (reason) { 65 | if(called) return 66 | called = true 67 | reject(reason) 68 | }) 69 | } catch (e) { 70 | reject(e) 71 | } 72 | 73 | } 74 | 75 | function resolvePromise(promise,x,fulfill,reject) { 76 | 77 | if (promise === x) {//2.3.1 传进来的x与当前promise相同,报错 78 | return reject(new TypeError('循环引用了')) 79 | } 80 | 81 | //2.3.2 x如果是一个promise 82 | if (x instanceof Promise) { 83 | //2.3.2.1 84 | if (x.status === 'pending') { //x状态还未改变,返回的下一个promise的resove的接收的值y不确定,对其递归处理 85 | x.then(function(y) { 86 | resolvePromise(promise, y, fulfill, reject) 87 | },reject) 88 | } else { 89 | //2.3.2.2 , 2.3.2.3 90 | //状态确定,如果fulfill那传进来的肯定是普通值,如果reject直接处理,不管你抛出来的是什么东东 91 | x.then(fulfill, reject) 92 | } 93 | return; 94 | } 95 | let called = false; 96 | //2.3.3 97 | //x 是一个thenable 98 | if(x !== null && (typeof x === 'object' || typeof x === 'function')){ 99 | try { 100 | //2.3.3.1 101 | let then = x.then; 102 | if (typeof then === 'function') {//2.3.3.3 {then:: (resolve,reject)=>{resolve(1)}}} 103 | then.call(x,(y)=>{ 104 | if (called) return 105 | called = true 106 | resolvePromise(promise,y,fulfill,reject) 107 | },(err)=>{ 108 | if (called) return 109 | called = true 110 | reject(err) 111 | }) 112 | }else{//2.3.3.2 x: {then:1},是一个带then属性的普通值 113 | fulfill(x) 114 | } 115 | }catch(e){//2.3.3.2 可以参见上面说的异常情况2 116 | if (called) return 117 | called = true; 118 | reject(e); 119 | } 120 | }else{//2.3.3.4,x是一个普通值 121 | fulfill(x) 122 | } 123 | } 124 | 125 | Promise.prototype.then = function (onFulfilled, onRejected) { 126 | 127 | onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) { 128 | return value 129 | } 130 | onRejected = typeof onRejected === 'function' ? onRejected : function (err) { 131 | throw err 132 | } 133 | 134 | let _this = this 135 | let newPromise 136 | if (_this.status === 'resolved') { 137 | newPromise = new Promise(function (resolve, reject) { 138 | asyncCall(function () { 139 | try { 140 | let x = onFulfilled(_this.value) 141 | resolve(x) 142 | } catch (e) { 143 | console.log(e) 144 | reject(e) 145 | } 146 | }) 147 | }) 148 | } 149 | 150 | if (_this.status === 'rejected') { 151 | newPromise = new Promise(function (resolve, reject) { 152 | asyncCall(function () { 153 | try { 154 | let x = onRejected(_this.reason) 155 | resolve(x) 156 | } catch (e) { 157 | reject(e) 158 | } 159 | }) 160 | }) 161 | } 162 | 163 | if (_this.status === 'pending') { 164 | newPromise = new Promise(function (resolve, reject) { 165 | 166 | _this.onResolvedCallbacks.push(function () { 167 | asyncCall(function () { 168 | try { 169 | let x = onFulfilled(_this.value) 170 | resolve(x) 171 | } catch (e) { 172 | reject(e) 173 | } 174 | }) 175 | 176 | }) 177 | 178 | 179 | _this.onRejectedCallbacks.push(function () { 180 | asyncCall(function () { 181 | try { 182 | let x = onRejected(_this.reason) 183 | resolve(x) 184 | } catch (e) { 185 | reject(e) 186 | } 187 | }) 188 | }) 189 | 190 | }) 191 | } 192 | return newPromise 193 | } 194 | 195 | Promise.prototype.catch = function(callback){ 196 | return this.then(null,callback) 197 | } 198 | Promise.resolve = function(value){ 199 | return new Promise(function(resolve,reject){ 200 | resolve(value); 201 | }) 202 | } 203 | Promise.reject = function(value){ 204 | return new Promise(function(resolve,reject){ 205 | reject(value); 206 | }) 207 | } 208 | Promise.race = function(promise){ 209 | return new Promise(function (resolve,reject){ 210 | for(var i = 0;i{ 11 | getDataB(a,(b)=>{ 12 | getDataC(b,(c)=>{ 13 | console.log(c) 14 | })) 15 | }) 16 | }) 17 | ``` 18 | 19 | getDataA、getDataB、getDataC都是异步的。getDataB依赖getDataA的结果,getDataC依赖getDataB的结果,三者通过回调的方式来处理依赖关系,可以看到如果依赖关系层级多的话,层级就会越来越深,最终形成`回调地狱`。 20 | 21 | 如果采用Promise的方式,我们可以将一个个过程抽象成Promise,然后将其链式调用,优雅的避开了多层回调的问题。 22 | 23 | ```javascript 24 | const getDataA = function(){ 25 | return new Promise((resolve,reject)=>{ 26 | resolve(a) 27 | }) 28 | } 29 | const getDataB = function(a){ 30 | return new Promise((resolve,reject)=>{ 31 | resolve(b) 32 | }) 33 | } 34 | 35 | const getDataC = function(b){ 36 | return new Promise((resolve,reject)=>{ 37 | resolve(c) 38 | }) 39 | } 40 | getDataA().then(getDataB).then(getDataC).then((c)=>{ 41 | console.log(c) 42 | }) 43 | ``` 44 | 45 | 对比一下,下面的代码是不是更清晰一些,更容易理清楚每一个过程的依赖关系。 46 | 47 | 我们不禁好奇为什么通过这种方式就能解决异步回调?每个Promise里面做了什么?then为什么能链式调用? 48 | 49 | 带着这些疑惑,我们来研究下Promise的原理。从一个Promise雏形开始,通过打补丁的方式,一点一点的实现一个符合规范的Promise。 50 | 51 | ## PromiseA+规范 52 | 53 | 首先来看下规范,[https://promisesaplus.com](https://promisesaplus.com)。如果单纯的看这个规范,容易看的晕头转向,我简单总结了下它的核心点,也就四个: 54 | 55 | 1.Promise是一个状态机,有三种状态`pending`,`fulfilled`,`rejected`。只能从`pending`状态,转换为`fulfilled`或者`rejected`,不可逆转且无第三种状态转换方式。 56 | 57 | 2.必须提供一个then方法用以处理当前值:终值和据因。then接收两个参数onFufilled,onRejected分别处理fulfilled和rejected的结果。 58 | 59 | 3.then方法必须返回一个新的Promise , 便于链式调用。 60 | 61 | 4.Promise中必须包含一个`resolve`方法,能够接受各种类型的值,将其处理成普通值`fulfilled`或者直接`rejected` 62 | 63 | ## Promise雏形 64 | 65 | 根据上面总结的四点,先来实现一个promise的雏形: 66 | 67 | ```javascript 68 | function Promise(executor) { 69 | 70 | let _this = this 71 | _this.status = 'pending' 72 | _this.value = null 73 | _this.reason = null 74 | 75 | function resolve(value) { 76 | if (_this.status === 'pending') { 77 | _this.status = 'fulfilled' 78 | _this.value = value 79 | } 80 | } 81 | 82 | function reject(reason) { 83 | if (_this.status === 'pending') { 84 | _this.status = 'rejected' 85 | _this.reason = reason 86 | } 87 | } 88 | //暂时先不要问为什么这么写,文末分解 89 | executor(function (value) { 90 | resolve(value) 91 | }, function (reason) { 92 | reject(reason) 93 | },) 94 | } 95 | 96 | Promise.prototype.then = function (onFulfilled, onRejected) { 97 | let _this = this 98 | 99 | if (_this.status == 'fulfilled') { 100 | onFulfilled(_this.value) 101 | } 102 | 103 | if (_this.status == 'rejected') { 104 | onRejected(_this.reason) 105 | } 106 | } 107 | ``` 108 | 109 | 上面的代码中,Promise里面的东西是很健全的:有改变Promise装态的两个方法resolve(确切的说,这个方法应该叫fulfill,fulfill和resolve是概念是不一样的,后面会解释原因)和reject,有管理状态的status,保存fulfilled状态值的value和rejected拒因的reason,还包含了一个then方法用来处理fulfilled的值和rejected的据因。 110 | 111 | 试验一下效果: 112 | 113 | ```javascript 114 | var promise = new Promise((resolve, reject) => { 115 | resolve('test simplePromise resolve') 116 | }) 117 | 118 | promise.then(function (value) { 119 | console.log('success:', value) 120 | }, function (reason) { 121 | console.log('failed:', reason) 122 | }) 123 | ``` 124 | 125 | 结果:`success:test simplePromise resolve`,基本什么问题,有点promise的样子。 126 | 127 | 来个问题测试一下,`Q1` 128 | 129 | ```javascript 130 | var promise = new Promise((resolve, reject) => { 131 | setTimeout(function () { 132 | resolve('test simplePromise resolve') 133 | }, 100) 134 | }) 135 | promise.then(function (value) { 136 | console.log('success:', value) 137 | }, function (reason) { 138 | console.log('failed:', reason) 139 | }) 140 | ``` 141 | 142 | 你会发现,什么反应都没有。从这里可以看出,它还不能处理异步的情况。 143 | 144 | 不能处理异步,那就不能称为Promise。要支持异步,then方法里面的参数就不能立即执行。既然不能立即执行,那就必须找地方先保存起来! 145 | 146 | ### 支持异步 147 | 148 | 在Promise构造函数中添加两个回调数组 149 | 150 | ```javascript 151 | _this.onFulfilledCallbacks = [] 152 | _this.onRejectedCallbacks = [] 153 | ``` 154 | 155 | 在then方法中添加 156 | 157 | ```javascript 158 | if (_this.status == 'pending') { 159 | _this.onFulfilledCallbacks.push(function () { 160 | onFulfilled(_this.value) 161 | }) 162 | _this.onRejectedCallbacks.push(function () { 163 | onRejected(_this.reason) 164 | }) 165 | } 166 | ``` 167 | 168 | 在resolve和reject中调用 169 | 170 | ```javascript 171 | function resolve(value) { 172 | if (_this.status === 'pending') { 173 | _this.status = 'fulfilled' 174 | _this.value = value 175 | _this.onFulfilledCallbacks.forEach(function (fn) { 176 | fn() 177 | }) 178 | } 179 | } 180 | 181 | function reject(reason) { 182 | if (_this.status === 'pending') { 183 | _this.status = 'rejected' 184 | _this.reason = reason 185 | _this.onRejectedCallbacks.forEach(function (fn) { 186 | fn() 187 | }) 188 | } 189 | } 190 | ``` 191 | 192 | 再用`Q1`测试,就没有问题了。 193 | 194 | 下面来用`Q2`测试一下 195 | 196 | ```javascript 197 | var promise = new Promise((resolve, reject) => { 198 | resolve('test simplePromise fulfilled') 199 | }) 200 | promise.then(function (value) { 201 | console.log('success:', value) 202 | }, function (reason) { 203 | console.log('failed:', reason) 204 | }).then(function (value) { 205 | console.log('success:', value) 206 | }, function (reason) { 207 | console.log('failed:', reason) 208 | }) 209 | ``` 210 | 211 | 结果是:`TypeError: Cannot read property 'then' of undefined`。这个结果表明,目前的Promise还不能解决链式调用的问题。 212 | 213 | ### 链式调用 214 | 215 | 要实现链式调用的功能,首先想到是then方法返回this。如果返回this的话,会有什么问题呢? 216 | 217 | 想象一下,经过第一个then方法之后,Promise的状态改变了,而状态改变一次就不能更改了,继续使用`this.then`,那它传入的`onFulfilled`,` onRejected`都无法执行,后面链式调用再多的then都不会执行。所以这条路行不通。 218 | 219 | 所以我们只有通过返回一个新的promise,为什么呢?因为新的Promise有then方法,可以实现链式调用。 220 | 221 | 继续对Promise的then进行改造 222 | 223 | ```javascript 224 | let newPromise 225 | if (_this.status == 'fulfilled') { 226 | newPromise = new Promise(function(resolve,reject){ 227 | let x = onFulfilled(_this.value) 228 | resolve(x) 229 | }) 230 | } 231 | 232 | if (_this.status == 'rejected') { 233 | newPromise = new Promise(function(resolve,reject){ 234 | let x = onRejected(_this.reason) 235 | resolve(x) 236 | }) 237 | } 238 | 239 | if (_this.status == 'pending'){ 240 | newPromise = new Promise(function(resolve,reject){ 241 | _this.onFulfilledCallbacks.push(function(){ 242 | let x = onFulfilled(_this.value) 243 | resolve(x) 244 | }) 245 | 246 | _this.onRejectedCallbacks.push(function(){ 247 | let x = onRejected(_this.reason) 248 | resolve(x) 249 | }) 250 | }) 251 | } 252 | return newPromise 253 | ``` 254 | 255 | > 这里需要解释一下:newPromise的状态不能因为上一个promise被reject了,也reject;也就是说上一个promise无论被 reject 还是被 resolve , newPromise 都会被 resolve,只有newPromise出现异常时才会被 reject。 256 | 257 | 再用`Q2`测试一下,发现链式调用的问题就解决了。 258 | 259 | 再来看下 `Q3` 260 | 261 | ```javascript 262 | let promise = new Promise((resolve,reject) => { 263 | throw new Error('error') 264 | }) 265 | promise.then((value)=>{ 266 | console.log('success:',value) 267 | },(reason)=>{ 268 | console.log('reject:',reason) 269 | }) 270 | ``` 271 | 272 | 结果:`Error: error…`,报了一堆错误。也就是说现在的Promise还不够健壮,还没有错误处理机制,在发生错误的时候,手足无措,不知道到底该`resove`还是`reject`。 273 | 274 | ### 异常处理 275 | 276 | 构造函数中 277 | 278 | ```javascript 279 | if (!(this instanceof Promise)) { 280 | return new Promise(executor); 281 | } 282 | 283 | if (typeof executor !== 'function') { 284 | throw new TypeError('Promise executor is not a function'); 285 | } 286 | 287 | try{ 288 | executor(function (value) { 289 | resolve(value) 290 | }, function (reason) { 291 | reject(reason) 292 | }) 293 | }catch(e){ 294 | reject(e) 295 | } 296 | ``` 297 | 298 | then方法中 299 | 300 | ```javascript 301 | if (_this.status == 'fulfilled') { 302 | newPromise = new Promise(function(resolve,reject){ 303 | try{ 304 | let x = onFulfilled(_this.value) 305 | resolve(x) 306 | }catch(e){ 307 | reject(e) 308 | } 309 | }) 310 | } 311 | 312 | if (_this.status == 'rejected') { 313 | newPromise = new Promise(function(resolve,reject){ 314 | try{ 315 | let x = onRejected(_this.reason) 316 | resolve(x) 317 | }catch(e){ 318 | reject(e) 319 | } 320 | 321 | }) 322 | } 323 | 324 | if (_this.status == 'pending'){ 325 | newPromise = new Promise(function(resolve,reject){ 326 | _this.onFulfilledCallbacks.push(function(){ 327 | try{ 328 | let x = onFulfilled(_this.value) 329 | resolve(x) 330 | }catch(e){ 331 | reject(e) 332 | } 333 | }) 334 | _this.onRejectedCallbacks.push(function(){ //add 335 | try{ 336 | let x = onRejected(_this.reason) 337 | resolve(x) 338 | }catch(e){ 339 | reject(e) 340 | } 341 | }) 342 | 343 | }) 344 | } 345 | ``` 346 | 347 | 再来看上面`Q3`的结果:`reject: Error: error`,程序并不会崩溃。 348 | 349 | 再来看下`Q4` 350 | 351 | ```javascript 352 | new Promise((resolve,reject)=>{ 353 | resolve(1) 354 | }).then().then().then((value)=>{ 355 | console.log('resolve',value) 356 | },(reason)=>{ 357 | console.log('reject',reason) 358 | }) 359 | ``` 360 | 361 | 结果:`reject TypeError: onRejected is not a function`,不应该打印`resolve 1`?按照原生的Promise,不是应该往下传递`1`吗?`1`哪里去了,毫无疑问,丢了!丢了,那我们就想办法把它找回来。 362 | 363 | ### 值穿透 364 | 365 | 先来解释一下为什么:第一个then里面没有传参,导致` onFulfilled`就取不到,下面的代码 366 | 367 | ```javascript 368 | if (_this.status == 'fulfilled') { 369 | newPromise = new Promise(function(resolve,reject){ 370 | try{ 371 | let x = onFulfilled(_this.value) 372 | resolve(x) 373 | }catch(e){ 374 | reject(e) 375 | } 376 | }) 377 | } 378 | ``` 379 | 380 | 中的` let x = onFulfilled(_this.value)`就会报错,会被捕获,然后`reject(e)`,第二个then里面同样没有传参 381 | 382 | ```javascript 383 | if (_this.status == 'rejected') { 384 | newPromise = new Promise(function(resolve,reject){ 385 | try{ 386 | let x = onRejected(_this.reason) 387 | resolve(x) 388 | }catch(e){ 389 | reject(e) 390 | } 391 | }) 392 | } 393 | ``` 394 | 395 | `onRejected`也获取不到,同样`reject(e)`。传到最后一个,`reject TypeError: onRejected is not a function`。毫无疑问,当初`resolve`的`1`丢了。 396 | 397 | 我们可不可以这样:当你在`then`不传参数的时候,我们就给`onFulfilled`和`onRejected`默认一个方法,最起码不会造成`onFulfilled`和`onRejected`找不到的情况。既然默认一个方法了,实现值穿透就好办了,让它只做一件事情:给它传什么参数,它就返回什么参数。 398 | 399 | 我们可以在then方法中添加 400 | 401 | ```javascript 402 | onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) { 403 | return value 404 | } 405 | onRejected = typeof onRejected === 'function' ? onRejected : function (err) { 406 | throw err 407 | } 408 | ``` 409 | 410 | 再来试验下`Q4`,结果就是`resolve 1`了。 411 | 412 | 回头看下,我们已经在这个`Promise`的雏形上打了不少补丁了:`异步调用`,`链式调用` ,`异常处理`,`值穿透`。但是这还远远不够,比如,下面的情况又没办法解决了。 413 | 414 | `Q5` 415 | 416 | ```javascript 417 | var promise = new Promise((resolve,reject) => { 418 | setTimeout(function(){ 419 | resolve('test simplePromise resolve') 420 | },100) 421 | }) 422 | 423 | setTimeout(() => { 424 | promise.then(function(value){ 425 | console.log('success:',value) 426 | },function(reason){ 427 | console.log('failed:',reason) 428 | }) 429 | console.log('end') 430 | }, 200); 431 | ``` 432 | 433 | 结果: 434 | 435 | ```javascript 436 | success: test simplePromise resolve 437 | end 438 | ``` 439 | 440 | 是不是感觉哪里不对劲?从上面可以分析出,then方法中的传入的`onFulfilled`和`onRejected`方法执行时机不正确,他应该在`console.log('end')`之后执行,也就是说它应该是异步执行。这一点也正是规范`2.2.4`规定的。`setTimeout`可以模拟异步执行,先用`setTimeout`改造一下试试。 441 | 442 | ### 异步调用then回调函数 443 | 444 | then方法中 445 | 446 | ```javascript 447 | newPromise = new Promise(function(resolve,reject){ 448 | setTimeout(function () { 449 | try{ 450 | let x = onFulfilled(_this.value) 451 | resolve(x) 452 | }catch(e){ 453 | reject(e) 454 | } 455 | }) 456 | }) 457 | 458 | newPromise = new Promise(function(resolve,reject){ 459 | setTimeout(function () { 460 | try{ 461 | let x = onRejected(_this.reason) 462 | resolve(x) 463 | }catch(e){ 464 | reject(e) 465 | } 466 | }) 467 | }) 468 | 469 | newPromise = new Promise(function(resolve,reject){ 470 | _this.onResolvedCallbacks.push(function(){ 471 | setTimeout(function () { 472 | try { 473 | let x = onFulfilled(_this.value) 474 | resolve(x) 475 | } catch (e) { 476 | reject(e) 477 | } 478 | }) 479 | }) 480 | _this.onRejectedCallbacks.push(function(){ 481 | setTimeout(function () { 482 | try { 483 | let x = onFulfilled(_this.value) 484 | resolve(x) 485 | } catch (e) { 486 | reject(e) 487 | } 488 | }) 489 | }) 490 | 491 | }) 492 | ``` 493 | 494 | 验证`Q5`发现,打印出的结果是符合我们预期的。 495 | 496 | ```javascript 497 | end 498 | success: test simplePromise resolve 499 | ``` 500 | 501 | 那是不是说我们这样改造一下就结束了呢?再来看下`Q6` 502 | 503 | ```javascript 504 | setTimeout(()=>{ 505 | console.log(5) 506 | }) 507 | new Promise((resolve,reject)=>{ 508 | console.log(1) 509 | resolve(3) 510 | Promise.resolve(4).then((value)=>{ 511 | console.log(value) 512 | }) 513 | }).then((value)=>{ 514 | console.log(value) 515 | }) 516 | console.log(2) 517 | ``` 518 | 519 | 结果:`1,2,5,4,3`。 520 | 521 | 用原生的验证下,发现结果是`1,2,4,3,5`,原生的结果不一致! 522 | 523 | 要想弄清楚这是怎么回事,这要从`JS`的执行机制娓娓道来! 524 | 525 | ### JS的执行机制 526 | 527 | 上图说话: 528 | 529 | ![20180704153067134448578.png](http://ody1t82mr.bkt.clouddn.com/20180704153067134448578.png) 530 | 531 | 从上图中可以看出,`JS`的事件循环是先执行宏任务,再执行微任务。每一次执行一个宏任务,都去查看微任务队列,如果有微任务,就执行所有的微任务队列。 532 | 533 | 宏任务和微任务的分类如下: 534 | 535 | - **macro-task:** `script(整体代码)`, `setTimeout`, `setInterval`, `setImmediate`, `I/O`,` UI rendering` 536 | - **micro-task:** `process.nextTick`, `Promise(原生 Promise)`, `Object.observe`, `MutationObserver` 537 | 538 | 根据这个分类,`Q6`代码的执行过程可以表示为下图 539 | 540 | ![20180815153430430964513.png](http://ody1t82mr.bkt.clouddn.com/20180815153430430964513.png) 541 | 542 | 整个流程如下:先执行宏任务中的主体代码,遇到`setTimeout`,它属于宏任务,放到宏任务队列里面,然后执行Promise构造函数,打印`1`。Promise是个微任务,首先会把其内部Promise的`console.log(4)`放到微任务队列中,然后将其本身的微任务`console.log(3)`放到微任务队列里面。继续执行主体代码打印`2`。当前宏任务执行完毕,执行微任务队列,打印`4`,`3`。微任务队列为空,继续执行宏任务,打印`5`,整个流程结束,结果`12435`。 543 | 544 | 梳理完上面的流程,我们发现使用setTimeout宏任务来异步执行then回调方法是不合适的,应该把`setTimeout`替换成微任务方法,比如`process.nextTick`(推荐使用**immediate**这个库),你可以去验证下,结果肯定是`12435`了。浏览器环境可以使用`MutationObserver`。我封装了下`asyncCall` 545 | 546 | ```javascript 547 | var asyncCall = (process && process.nextTick) || setImmediate || function (callback) { 548 | if (typeof callback !== 'function') 549 | throw new TypeError('callback is not a function'); 550 | var Mutation = window.MutationObserver || window.WebKitMutationObserver; 551 | var observer = new Mutation(callback); 552 | var element = document.createTextNode(''); 553 | observer.observe(element, { 554 | characterData: true 555 | }); 556 | element.data = 1; 557 | } || function (callback) { 558 | if (typeof callback !== 'function') 559 | throw new TypeError('callback is not a function'); 560 | setTimeout(callback, 0); 561 | }; 562 | ``` 563 | 564 | 这里简单起见,先用`process.nextTick`代替`setTimeout`,优化下代码。到此为止,我们写的`promise`才是真正的异步调用`then`回调函数。 565 | 566 | 继续来看下一个问题,`Q7` 567 | 568 | ```javascript 569 | new Promise((resolve, reject) => { 570 | resolve(new Promise((resolve) => { 571 | resolve(1) 572 | })) 573 | }).then((value) => { 574 | console.log('success:', value) 575 | }, (reason) => { 576 | console.log('failed:', reason) 577 | }) 578 | ``` 579 | 580 | 结果: 581 | 582 | ```javascript 583 | success: Promise { 584 | status: 'resolved', 585 | value: 1, 586 | reason: null, 587 | onRejectedCallbacks: [], 588 | onResolvedCallbacks: [] } 589 | ``` 590 | 591 | 使用原生的Promise运行,结果是 592 | 593 | ``` 594 | success: 1 595 | ``` 596 | 597 | 也就是说,`resolve`目前只能处理普通值(普通值就是除`promise`对象和`thenable`对象以外的基本数据类型)的参数。如果是一个`promise`对象,我们无法正确处理,也就是对传入进来的`resolve` 的参数没有进行任何处理。 598 | 599 | 不只是`promise`的对象无法处理,对于下面的参数,当前的`promise`雏形一样无法处理: 600 | 601 | 1. 当前`promise` 602 | 2. thenable对象(长的很像`promise`的对象,具有`then`方法) 603 | 604 | ```javascript 605 | new Promise((resolve, reject) => { 606 | resolve({ 607 | then: (resolve,reject)=>{ 608 | resolve(1) 609 | } 610 | }) 611 | }).then((value) => { 612 | console.log('success:', value) 613 | }, (reason) => { 614 | console.log('failed:', reason) 615 | }) 616 | ``` 617 | 618 | 3. 一调用就出错的thenable 619 | 620 | ```javascript 621 | let promise = {} 622 | Object.defineProperty(promise,'then',{ 623 | value: function(){ 624 | throw new Error('出错了吧') 625 | } 626 | }) 627 | ``` 628 | 629 | 4. 调用了resolve,又调用了reject 630 | 631 | ```javascript 632 | new Promise((resolve, reject) => { 633 | resolve(new Promise((resolve) => { 634 | resolve(1) 635 | })) 636 | reject('error') 637 | }).then((value) => { 638 | console.log('success:', value) 639 | }, (reason) => { 640 | console.log('failed:', reason) 641 | }) 642 | ``` 643 | 644 | 等等。 645 | 646 | 要兼容这些类型,还得先理解下上面总结的第四个核心点 647 | 648 | > 4.Promise中必须包含一个resolve方法,能够接受各种类型的值,将其处理成普通值fulfilled或者直接rejected 649 | 650 | 其实这些规范都考虑到了。下面我们按照规范2.3,一点点的写出一个复合规范的`resolve`。 651 | 652 | ### resolvePromise 653 | 654 | 首先改造`Promise`构造函数中的`resolve`,将原来的`resolve`改成`fulfill`方法。 655 | 656 | ```javascript 657 | function resolve(value) { 658 | resolvePromise(_this,value,fulfill,reject) 659 | } 660 | 661 | function fulfill(value){ //只接受普通值,不接受promise和thenable 662 | if (_this.status === 'pending') { 663 | _this.status = 'fulfilled' 664 | _this.value = value 665 | _this.onResolvedCallbacks.forEach(function (fn) { 666 | fn() 667 | }) 668 | } 669 | } 670 | ``` 671 | 672 | 现在你应该能明白上面提到的`fulfill`和`resolve`不是一个概念了吧! 673 | 674 | > fulfill只是一个Promise状态改变器。状态改变后,使用传进来的普通值,调用回调数组里面的回调函数。 675 | > 676 | > resolve是将传进来的数据,处理成一个普通值,并根据处理的情况,决定调用`fulfill`还是`reject`来改变状态。 677 | 678 | 根据规范来完善resolvePromise: 679 | 680 | ```javascript 681 | function resolvePromise(promise,x,fulfill,reject) { 682 | 683 | if (promise === x) {//2.3.1 传进来的x与当前promise相同,报错 684 | return reject(new TypeError('循环引用了')) 685 | } 686 | 687 | //2.3.2 x如果是一个promise 688 | if (x instanceof Promise) { 689 | //2.3.2.1 690 | if (x.status === 'pending') { //x状态还未改变,返回的下一个promise的resove的接收的值y不确定,对其递归处理 691 | x.then(function(y) { 692 | resolvePromise(promise, y, fulfill, reject) 693 | },reject) 694 | } else { 695 | //2.3.2.2 , 2.3.2.3 696 | //状态确定,如果fulfill那传进来的肯定是普通值,如果reject直接处理,不管你抛出来的是什么 697 | x.then(fulfill, reject) 698 | } 699 | return; 700 | } 701 | fulfill(x) 702 | } 703 | ``` 704 | 705 | 先写到这里,验证下`Q7`,发现结果是正确:`success: 1`。 706 | 707 | 再来折腾下,用现在的promise来执行另一个问题:`Q8` 708 | 709 | ```javascript 710 | const B = new Promise((resolve) => { 711 | resolve(1) 712 | }) 713 | const A = new Promise((resolve, reject) => { 714 | resolve(B) 715 | reject('error') 716 | }) 717 | A.then((value) => { 718 | console.log('success:', value) 719 | }, (reason) => { 720 | console.log('failed:', reason) 721 | }) 722 | ``` 723 | 724 | 结果是:`failed:error`。 725 | 726 | 根据总结的第一点 727 | 728 | > Promise是一个状态机,有三种状态`pending`,`fulfilled`,`rejected`。只能从`pending`状态,转换为`fulfilled`或者`rejected`,不可逆转且无第三种状态转换方式 729 | 730 | 这是不正确的,也就是说我们写的Promise状态控制有问题。 731 | 732 | 先来看下为什么会有问题:根据上面的resolvePromise处理逻辑,A的resolve接收的参数是一个promise B,会去递归调用B的`then`方法。将B的then中的onFulfilled放到微任务队列中。继续执行A中的`reject('error')`,A的状态从pending更新为reject。执行A的then方法,将onRejected方法放到微任务对列中。当前微任务中包含了两个任务,一个是B的then方法中onFulfilled,一个是A的then方法中的onRejected。B的then方法中onFulfilled会去调用A的fulfill方法,但是A的状态已经改变了,无法执行fulfill的操作。执行下一个微任务A的onRejected,打印了`failed:error`。 733 | 734 | 从这个问题可以看出,我们写的promise,resolve和reject的调用并不阻塞promise状态的更新。 735 | 736 | 标准只规定了,状态改变一次就不能改变了,并没有规定resolve和reject的调用,要阻塞状态更新。虽然并没有这么硬性规定,但是大家都是这么理解的,比如你可以运行浏览器中、node中promise以及第三方bluebird,Q,lie库等,都是resolve,reject调用的时候,会阻塞promise状态更新。也就是说在调用了resolve以后,promise的状态不能被再次调用reject的时候改变直到resolve的过程结束。调用reject再调用resolve也是一样的。这也符合常理,不能调用了resolve,再去调用reject,就乱套了 。 737 | 738 | #### Promise状态阻塞更新 739 | 740 | 我们可以通过在Promise的构造函数中添加called变量的方式,来阻塞状态更新。 741 | 742 | ```javascript 743 | try { 744 | let called = false 745 | executor(function (value) { 746 | if(called) return 747 | called = true 748 | resolve(value) 749 | }, function (reason) { 750 | if(called) return 751 | called = true 752 | reject(reason) 753 | }) 754 | } catch (e) { 755 | console.log(e) 756 | reject(e) 757 | } 758 | ``` 759 | 760 | 再次运行`Q8`,结果:`success:1`。 761 | 762 | 继续完善`resolvePromise`,来处理下`thenable`的情况 763 | 764 | #### Handle thenable 765 | 766 | ```javascript 767 | function resolvePromise(promise,x,fulfill,reject) { 768 | if (promise === x) {//2.3.1 传进来的x与当前promise相同,报错 769 | return reject(new TypeError('循环引用了')) 770 | } 771 | //2.3.2 x如果是一个promise 772 | if (x instanceof Promise) { 773 | //2.3.2.1 774 | if (x.status === 'pending') { //x状态还未改变,返回的下一个promise的resove的接收的值y不确定,对其递归处理 775 | x.then(function(y) { 776 | resolvePromise(promise, y, fulfill, reject) 777 | },reject) 778 | } else { 779 | //2.3.2.2 , 2.3.2.3 780 | //状态确定,如果fulfill那传进来的肯定是普通值,如果reject直接处理,不管你抛出来的是什么东东 781 | x.then(fulfill, reject) 782 | } 783 | return; 784 | } 785 | let called = false; 786 | //2.3.3 787 | //x 是一个thenable 788 | if(x !== null && (typeof x === 'object' || typeof x === 'function')){ 789 | try { 790 | //2.3.3.1 791 | let then = x.then; 792 | if (typeof then === 'function') {//2.3.3.3 {then:: (resolve,reject)=>{resolve(1)}}} 793 | then.call(x,(y)=>{ 794 | if (called) return 795 | called = true 796 | resolvePromise(promise,y,fulfill,reject) 797 | },(err)=>{ 798 | if (called) return 799 | called = true 800 | reject(err) 801 | }) 802 | }else{//2.3.3.2 x: {then:1},是一个带then属性的普通值 803 | fulfill(x) 804 | } 805 | }catch(e){//2.3.3.2 可以参见上面说的异常情况2 806 | if (called) return 807 | called = true; 808 | reject(e); 809 | } 810 | }else{//2.3.3.4,x是一个普通值 811 | fulfill(x) 812 | } 813 | } 814 | ``` 815 | 816 | 上面的注释已经很详细了,包括了规范规定的所有异常处理。 817 | 818 | 这里有个疑点需要重点解释一下,我们看到上述代码中出现 819 | 820 | ```javascript 821 | if (called) return 822 | called = true 823 | ``` 824 | 825 | called变量是干嘛的?我们不是在Promise构造函数中刚加了这个变量吗?这里的变量和我们刚才添加的有什么不一样呢? 826 | 827 | 这个要通过下面的例子来进行解释 828 | 829 | ```javascript 830 | new Promise((resolve,reject)=>{ 831 | resolve({ 832 | then:(onFulfilled,onRejected)=>{ 833 | onFulfilled(new Promise((resolve1)=>{ 834 | setTimeout(()=>{ 835 | resolve1(456) 836 | },1000) 837 | })) 838 | onRejected(789) 839 | } 840 | }) 841 | }).then((value)=>{ 842 | console.log('success:',value) 843 | },(reason)=>{ 844 | console.log('reject:',reason) 845 | }) 846 | ``` 847 | 848 | 其实上面代码就类似于 849 | 850 | ```javascript 851 | new Promise((resolve,reject)=>{ 852 | resolve(new Promise((resolve,reject)=>{ 853 | resolve(new Promise((resolve1)=>{ 854 | setTimeout(()=>{ 855 | resolve1(456) 856 | },1000) 857 | })) 858 | reject(789) 859 | }) 860 | }).then((value)=>{ 861 | console.log('success:',value) 862 | },(reason)=>{ 863 | console.log('reject:',reason) 864 | }) 865 | ``` 866 | 867 | 我们通过上面的代码中可以看出,`thenable`其实就是一个没有状态阻塞更新机制的`promise`。这里的called就相当于是为了防止调用了resolve又调用了reject的情况下Promise状态更新乱套的问题。 868 | 869 | ## 完整代码 870 | 871 | ```javascript 872 | function Promise(executor) { 873 | if (!(this instanceof Promise)) { 874 | return new Promise(executor); 875 | } 876 | 877 | if (typeof executor !== 'function') { 878 | throw new TypeError('Promise executor is not a function'); 879 | } 880 | 881 | let _this = this 882 | _this.status = 'pending' 883 | _this.value = null 884 | _this.reason = null 885 | _this.onRejectedCallbacks = [] 886 | _this.onResolvedCallbacks = [] 887 | 888 | function resolve(value) { 889 | resolvePromise(_this,value,fulfill,reject) 890 | } 891 | 892 | function fulfill(value){ //只接收普通值 893 | if (_this.status === 'pending') { 894 | _this.status = 'fulfilled' 895 | _this.value = value 896 | _this.onResolvedCallbacks.forEach(function (fn) { 897 | fn() 898 | }) 899 | } 900 | } 901 | 902 | function reject(reason) { 903 | if (_this.status === 'pending') { 904 | _this.status = 'rejected' 905 | _this.reason = reason 906 | _this.onRejectedCallbacks.forEach(function (fn) { 907 | fn() 908 | }) 909 | } 910 | } 911 | 912 | try { 913 | let called = false 914 | executor(function (value) { 915 | if(called) return 916 | called = true 917 | resolve(value) 918 | }, function (reason) { 919 | if(called) return 920 | called = true 921 | reject(reason) 922 | }) 923 | } catch (e) { 924 | reject(e) 925 | } 926 | 927 | } 928 | 929 | function resolvePromise(promise,x,fulfill,reject) { 930 | 931 | if (promise === x) {//2.3.1 传进来的x与当前promise相同,报错 932 | return reject(new TypeError('循环引用了')) 933 | } 934 | 935 | //2.3.2 x如果是一个promise 936 | if (x instanceof Promise) { 937 | //2.3.2.1 938 | if (x.status === 'pending') { //x状态还未改变,返回的下一个promise的resove的接收的值y不确定,对其递归处理 939 | x.then(function(y) { 940 | resolvePromise(promise, y, fulfill, reject) 941 | },reject) 942 | } else { 943 | //2.3.2.2 , 2.3.2.3 944 | //状态确定,如果fulfill那传进来的肯定是普通值,如果reject直接处理,不管你抛出来的是什么东东 945 | x.then(fulfill, reject) 946 | } 947 | return; 948 | } 949 | let called = false; 950 | //2.3.3 951 | //x 是一个thenable 952 | if(x !== null && (typeof x === 'object' || typeof x === 'function')){ 953 | try { 954 | //2.3.3.1 955 | let then = x.then; 956 | if (typeof then === 'function') {//2.3.3.3 {then:: (resolve,reject)=>{resolve(1)}}} 957 | then.call(x,(y)=>{ 958 | if (called) return 959 | called = true 960 | resolvePromise(promise,y,fulfill,reject) 961 | },(err)=>{ 962 | if (called) return 963 | called = true 964 | reject(err) 965 | }) 966 | }else{//2.3.3.2 x: {then:1},是一个带then属性的普通值 967 | fulfill(x) 968 | } 969 | }catch(e){//2.3.3.2 可以参见上面说的异常情况2 970 | if (called) return 971 | called = true; 972 | reject(e); 973 | } 974 | }else{//2.3.3.4,x是一个普通值 975 | fulfill(x) 976 | } 977 | } 978 | Promise.prototype.then = function (onFulfilled, onRejected) { 979 | onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) { 980 | return value 981 | } 982 | onRejected = typeof onRejected === 'function' ? onRejected : function (err) { 983 | throw err 984 | } 985 | let _this = this 986 | let newPromise 987 | if (_this.status === 'fulfilled') { 988 | newPromise = new Promise(function (resolve, reject) { 989 | process.nextTick(function () { 990 | try { 991 | let x = onFulfilled(_this.value) 992 | resolve(x) 993 | } catch (e) { 994 | reject(e) 995 | } 996 | }) 997 | }) 998 | } 999 | 1000 | if (_this.status === 'rejected') { 1001 | newPromise = new Promise(function (resolve, reject) { 1002 | process.nextTick(function () { 1003 | try { 1004 | let x = onRejected(_this.reason) 1005 | resolve(x) 1006 | } catch (e) { 1007 | reject(e) 1008 | } 1009 | }) 1010 | }) 1011 | } 1012 | 1013 | if (_this.status === 'pending') { 1014 | newPromise = new Promise(function (resolve, reject) { 1015 | 1016 | _this.onResolvedCallbacks.push(function () { 1017 | process.nextTick(function () { 1018 | try { 1019 | let x = onFulfilled(_this.value) 1020 | resolve(x) 1021 | } catch (e) { 1022 | reject(e) 1023 | } 1024 | }) 1025 | 1026 | }) 1027 | _this.onRejectedCallbacks.push(function () { 1028 | process.nextTick(function () { 1029 | try { 1030 | let x = onRejected(_this.reason) 1031 | resolve(x) 1032 | } catch (e) { 1033 | reject(e) 1034 | } 1035 | }) 1036 | }) 1037 | 1038 | }) 1039 | } 1040 | return newPromise 1041 | } 1042 | module.exports = Promise 1043 | ``` 1044 | 1045 | 1046 | 1047 | ## 测试 1048 | 1049 | 首先你要暴露一个接口: 1050 | 1051 | ```javascript 1052 | Promise.deferred = Promise.defer = function () { 1053 | var dfd = {} 1054 | dfd.promise = new Promise(function (resolve, reject) { 1055 | dfd.resolve = resolve 1056 | dfd.reject = reject 1057 | }) 1058 | return dfd 1059 | } 1060 | ``` 1061 | 1062 | 使用`promises-aplus-tests`这个库,具体使用方法,请移步它的github去查看,不详细介绍了。 1063 | 1064 | ``` 1065 | npm install promises-aplus-tests 1066 | promises-aplus-tests myPromise.js 1067 | ``` 1068 | 1069 | 测试通过: 1070 | 1071 | ![20180528152746135184276.png](http://ody1t82mr.bkt.clouddn.com/20180528152746135184276.png) 1072 | 1073 | ## 其他方法 1074 | 1075 | ```javascript 1076 | Promise.prototype.catch = function(callback){ 1077 | return this.then(null,callback) 1078 | } 1079 | Promise.resolve = function(value){ //返回一个promise 1080 | return new Promise(function(resolve,reject){ 1081 | resolve(value); 1082 | }) 1083 | } 1084 | Promise.reject = function(value){//返回一个promise 1085 | return new Promise(function(resolve,reject){ 1086 | reject(value); 1087 | }) 1088 | } 1089 | Promise.race = function(promise){//只要有一个成功了就resolve,有一个失败了就reject 1090 | return new Promise(function (resolve,reject){ 1091 | for(var i = 0;i{ 11 | console.log('then:',value) 12 | }).catch((error)=>{ 13 | console.log('catch:',error) 14 | }) 15 | ``` 16 | 17 | ```javascript 18 | new Promise((resolve, reject) => { 19 | resolve({ 20 | then:(onFulfilled,onRejected)=>{ 21 | onFulfilled(new Promise((resolve1)=>{ 22 | setTimeout(()=>{ 23 | resolve1(456) 24 | },1000) 25 | })) 26 | onRejected(789) 27 | } 28 | }) 29 | }).then((value) => { 30 | console.log('fulfilled:', value) 31 | }, (reason) => { 32 | console.log('rejected:', reason) 33 | }) 34 | ``` 35 | 36 | 以上每个问题的答案是什么?为什么?是不是有点懵逼?你还敢说你熟悉吗?(如果你很清楚,那恭喜你,下面的你不用看了,这里已经不适合你了) 37 | 38 | 要正确解释上面的问题,你需要充分的了解JS的运行机制以及Promise的实现原理。本文的目的就是让你明白Promise到底是个什么东西。 39 | 40 | 下面,我们从一个Promise雏形开始,通过打补丁的方式,一步一步实现一个完整的Promise。Promise的原理,顺其自然就明白了! 41 | 42 | ## PromiseA+规范 43 | 44 | 首先来看下规范,[https://promisesaplus.com](https://promisesaplus.com)。是不是顿时又懵逼了,这么长,这都是啥和啥啊。。。 45 | 46 | 别怕,我简单总结了下它的核心点,也就四个: 47 | 48 | 1.Promise是一个状态机,有三种状态pending,fulfilled,rejected。只能从pending状态,转换为fulfilled或者rejected,不可逆转且无第三种状态转换方式。 49 | 50 | 2.必须提供一个then方法用以处理当前值:终值和据因。then接收两个参数onFufilled,onRejected分别处理fulfilled和reject的结果。 51 | 52 | 3.then方法必须返回一个新的Promise , 便于链式调用。 53 | 54 | 4.Promise中必须包含一个resolve方法,能够接受各种类型的值,将其处理成普通值fulfilled或者直接rejected 55 | 56 | ## Promise雏形 57 | 58 | 先来实现一个promise的雏形: 59 | 60 | ```javascript 61 | function Promise(executor) { 62 | 63 | let _this = this 64 | _this.status = 'pending' 65 | _this.value = null 66 | _this.reason = null 67 | 68 | function resolve(value) { 69 | if (_this.status === 'pending') { 70 | _this.status = 'fulfilled' 71 | _this.value = value 72 | } 73 | } 74 | 75 | function reject(reason) { 76 | if (_this.status === 'pending') { 77 | _this.status = 'rejected' 78 | _this.reason = reason 79 | } 80 | } 81 | //暂时不要问为什么写那么啰嗦,山人自有妙计,文末分解 82 | executor(function (value) { 83 | resolve(value) 84 | }, function (reason) { 85 | reject(reason) 86 | },) 87 | } 88 | 89 | Promise.prototype.then = function (onFulfilled, onRejected) { 90 | let _this = this 91 | 92 | if (_this.status == 'fulfilled') { 93 | onFulfilled(_this.value) 94 | } 95 | 96 | if (_this.status == 'rejected') { 97 | onRejected(_this.reason) 98 | } 99 | } 100 | ``` 101 | 102 | 从上面的代码中,promise里面的东西是很健全的:有改变promise装态的两个方法resolve(确切的说,这个方法应该叫fulfill,fulfill和resolve是概念是不一样的,后面会解释原因)和reject,有管理状态的status,保存fulfilled状态值的value和rejected拒因的reason,还包含了一个then方法用来处理fulfilled的值和rejected的据因(还有一行`小注释`)。 103 | 104 | 试验一下效果: 105 | 106 | ```javascript 107 | var promise = new Promise((resolve, reject) => { 108 | resolve('test simplePromise resolve') 109 | }) 110 | 111 | promise.then(function (value) { 112 | console.log('success:', value) 113 | }, function (reason) { 114 | console.log('failed:', reason) 115 | }) 116 | ``` 117 | 118 | 妥妥的`success:test simplePromise resolve`,基本什么问题,有点promise的样子。 119 | 120 | 来个问题测试一下,`Q1` 121 | 122 | ```javascript 123 | var promise = new Promise((resolve, reject) => { 124 | setTimeout(function () { 125 | resolve('test simplePromise resolve') 126 | }, 100) 127 | }) 128 | promise.then(function (value) { 129 | console.log('success:', value) 130 | }, function (reason) { 131 | console.log('failed:', reason) 132 | }) 133 | ``` 134 | 135 | 你会发现,结果什么反应都没有。。。 136 | 137 | 从这里可以看出,它还不能处理异步的情况。不能处理异步,那叫什么Promise。要支持异步,then方法里面的参数就不能立即执行。既然不能立即执行,那就必须找地方,先保存起来! 138 | 139 | ### 支持异步 140 | 141 | 在Promise构造函数中添加两个回调数组 142 | 143 | ```javascript 144 | _this.onFulfilledCallbacks = [] 145 | _this.onRejectedCallbacks = [] 146 | ``` 147 | 148 | 在then方法中添加 149 | 150 | ```javascript 151 | if (_this.status == 'pending') { 152 | _this.onFulfilledCallbacks.push(function () { 153 | onFulfilled(_this.value) 154 | }) 155 | _this.onRejectedCallbacks.push(function () { 156 | onRejected(_this.reason) 157 | }) 158 | } 159 | ``` 160 | 161 | 在resolve和reject中调用 162 | 163 | ```javascript 164 | function resolve(value) { 165 | if (_this.status === 'pending') { 166 | _this.status = 'fulfilled' 167 | _this.value = value 168 | _this.onFulfilledCallbacks.forEach(function (fn) { 169 | fn() 170 | }) 171 | } 172 | } 173 | 174 | function reject(reason) { 175 | if (_this.status === 'pending') { 176 | _this.status = 'rejected' 177 | _this.reason = reason 178 | _this.onRejectedCallbacks.forEach(function (fn) { 179 | fn() 180 | }) 181 | } 182 | } 183 | ``` 184 | 185 | 好了,这样就实现了异步调用,用`Q1`测试是没有问题的。增量代码:[https://github.com/yonglijia/JSPI/blob/master/prototype/promise_async_2.js](https://github.com/yonglijia/JSPI/blob/master/prototype/promise_async_2.js) 186 | 187 | 来继续下一个话题,`Q2` 188 | 189 | ```javascript 190 | var promise = new Promise((resolve, reject) => { 191 | resolve('test simplePromise fulfilled') 192 | }) 193 | promise.then(function (value) { 194 | console.log('success:', value) 195 | }, function (reason) { 196 | console.log('failed:', reason) 197 | }).then(function (value) { 198 | console.log('success:', value) 199 | }, function (reason) { 200 | console.log('failed:', reason) 201 | }) 202 | ``` 203 | 204 | 结果是:`TypeError: Cannot read property 'then' of undefined`,说好的链式调用呢。。。 205 | 206 | ### 链式调用 207 | 208 | 实现链式调用,首先想到就是then方法返回this。返回this有什么问题呢,你经过第一个then方法之后,你的状态改变了,那你继续使用this.then,Promise转态改变一次就不能更改了,所以它传入的参数都无法执行,后面跟再多的then都不会执行,这显然不行。所以我们只有通过返回一个新的promise,为啥呢,promise有then方法啊!!! 209 | 210 | 继续打补丁,对then方法进行改造。 211 | 212 | ```javascript 213 | let newPromise 214 | if (_this.status == 'fulfilled') { 215 | newPromise = new Promise(function(resolve,reject){ 216 | let x = onFulfilled(_this.value) 217 | resolve(x) 218 | }) 219 | } 220 | 221 | if (_this.status == 'rejected') { 222 | newPromise = new Promise(function(resolve,reject){ 223 | let x = onRejected(_this.reason) 224 | resolve(x) 225 | }) 226 | } 227 | 228 | if (_this.status == 'pending'){ 229 | newPromise = new Promise(function(resolve,reject){ 230 | _this.onFulfilledCallbacks.push(function(){ 231 | let x = onFulfilled(_this.value) 232 | resolve(x) 233 | }) 234 | 235 | _this.onRejectedCallbacks.push(function(){ 236 | let x = onRejected(_this.reason) 237 | resolve(x) 238 | }) 239 | }) 240 | } 241 | return newPromise 242 | ``` 243 | 244 | > 这里需要解释一下:newPromise的状态不能因为上一个promise被reject了,而更改newPromise的状态;也就是说上一个promise无论被 reject 还是被 resolve , newPromise 都会被 resolve,只有出现异常时才会被 rejecte。 245 | 246 | `Q2`链式调用的问题也就解决了。增量代码:[https://github.com/yonglijia/JSPI/blob/master/prototype/promise_chain_3.js](https://github.com/yonglijia/JSPI/blob/master/prototype/promise_chain_3.js)。 247 | 248 | 那,抛个错误玩玩?看下 `Q3` 249 | 250 | ```javascript 251 | let promise = new Promise((resolve,reject) => { 252 | throw new Error('error') 253 | }) 254 | promise.then((value)=>{ 255 | console.log('success:',value) 256 | },(reason)=>{ 257 | console.log('reject:',reason) 258 | }) 259 | ``` 260 | 261 | 结果:`Error: error.....`,一堆错误,还是太年轻,经不起一点挫折啊。。。 262 | 263 | ### 异常处理 264 | 265 | 来吧,继续打补丁,加上错误处理 266 | 267 | ```javascript 268 | if (!(this instanceof Promise)) { 269 | return new Promise(executor); 270 | } 271 | 272 | if (typeof executor !== 'function') { 273 | throw new TypeError('Promise executor is not a function'); 274 | } 275 | 276 | try{ 277 | executor(function (value) { 278 | resolve(value) 279 | }, function (reason) { 280 | reject(reason) 281 | }) 282 | }catch(e){ 283 | reject(e) 284 | } 285 | ``` 286 | 287 | then方法中 288 | 289 | ```javascript 290 | if (_this.status == 'fulfilled') { 291 | newPromise = new Promise(function(resolve,reject){ 292 | try{ 293 | let x = onFulfilled(_this.value) 294 | resolve(x) 295 | }catch(e){ 296 | reject(e) 297 | } 298 | 299 | }) 300 | } 301 | 302 | if (_this.status == 'rejected') { 303 | newPromise = new Promise(function(resolve,reject){ 304 | try{ 305 | let x = onRejected(_this.reason) 306 | resolve(x) 307 | }catch(e){ 308 | reject(e) 309 | } 310 | 311 | }) 312 | } 313 | 314 | if (_this.status == 'pending'){ 315 | newPromise = new Promise(function(resolve,reject){ 316 | _this.onFulfilledCallbacks.push(function(){ 317 | try{ 318 | let x = onFulfilled(_this.value) 319 | resolve(x) 320 | }catch(e){ 321 | reject(e) 322 | } 323 | }) 324 | _this.onRejectedCallbacks.push(function(){ //add 325 | try{ 326 | let x = onRejected(_this.reason) 327 | resolve(x) 328 | }catch(e){ 329 | reject(e) 330 | } 331 | }) 332 | 333 | }) 334 | } 335 | ``` 336 | 337 | 再来看上面的结果:`reject: Error: error`,程序并不会崩溃,处理异常就是这么淡定!增量代码:[https://github.com/yonglijia/JSPI/blob/master/prototype/promise_withCatchError_4.js](https://github.com/yonglijia/JSPI/blob/master/prototype/promise_withCatchError_4.js) 338 | 339 | > 人生从来都不是一帆风顺的,有一个坑,就会有更多坑! 340 | 341 | 比如下面的问题跑个试试,`Q4` 342 | 343 | ```javascript 344 | new Promise((resolve,reject)=>{ 345 | resolve(1) 346 | }).then().then().then((value)=>{ 347 | console.log(value) 348 | },(reason)=>{console.log(reason)}) 349 | ``` 350 | 351 | 结果:`TypeError: onRejected is not a function`,怎么又变成了车祸现场了?按照原生的Promise,不是应该打印`1`吗?`1`哪里去了,毫无疑问,丢了!丢了,那就把它找回来。 352 | 353 | ### 值穿透 354 | 355 | 先来解释一下为什么:then里面啥也不传,也就是说,onFulfilled就取不到,下面的代码 356 | 357 | ```javascript 358 | newPromise = new Promise(function (resolve, reject) { 359 | process.nextTick(function () { 360 | try { 361 | let x = onFulfilled(_this.value) 362 | resolve(x) 363 | } catch (e) { 364 | reject(e) 365 | } 366 | }) 367 | }) 368 | ``` 369 | 370 | 中的` let x = onFulfilled(_this.value)`就会报错,会被捕获,然后`reject(e)`,下一个then啥也不传,同样的结果,传到最后一个,被捕获了。 371 | 372 | 毫无疑问`1`丢了,我们可不可以这样:既然你不传,我们就给你默认一个方法,这样不至于造成车祸现场。我们还想值穿透怎么办?使这个方法啥也不干,就只干一件事,给它啥,它吐出来啥! 373 | 374 | 在then方法中,添加 375 | 376 | ```javascript 377 | onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) { 378 | return value 379 | } 380 | onRejected = typeof onRejected === 'function' ? onRejected : function (err) { 381 | throw err 382 | } 383 | ``` 384 | 385 | 搞定!增量代码:[https://github.com/yonglijia/JSPI/blob/master/prototype/promise_transmit_value_5.js](https://github.com/yonglijia/JSPI/blob/master/prototype/promise_transmit_value_5.js) 386 | 387 | 我们解决了不少问题了:`异步调用`,`链式调用` ,`异常处理`,`值穿透`,是不是可以休息会了?当然不行! 388 | 389 | > 人生本来就不太平,就是一个接一个的坎! 390 | 391 | 比如,下面的情况又没办法解决了。。。 392 | 393 | `Q5` 394 | 395 | ```javascript 396 | var promise = new Promise((resolve,reject) => { 397 | setTimeout(function(){ 398 | resolve('test simplePromise resolve') 399 | },100) 400 | }) 401 | 402 | setTimeout(() => { 403 | promise.then(function(value){ 404 | console.log('success:',value) 405 | },function(reason){ 406 | console.log('failed:',reason) 407 | }) 408 | console.log('end') 409 | }, 200); 410 | ``` 411 | 412 | 结果: 413 | 414 | ```javascript 415 | success: test simplePromise resolve 416 | end 417 | ``` 418 | 419 | 是不是感觉哪里不对劲?是的,为什么`end`会在`success`之后打印?Promise不就是来解决异步的吗? 420 | 421 | 从上面可以分析出,then方法中的传入的onFulfilled和onRejected方法执行时机不正确。它应该是异步执行。这一点也正是规范2.2.4规定的,不清楚的可以看下规范。 422 | 423 | 那既然异步执行,那就再套一层马甲呗,setTimeout不就可以模拟异步执行嘛,那就用setTimeout改造一下试试。 424 | 425 | ### 异步调用then回调函数 426 | 427 | ```javascript 428 | newPromise = new Promise(function(resolve,reject){ 429 | setTimeout(function () { 430 | try{ 431 | let x = onFulfilled(_this.value) 432 | resolve(x) 433 | }catch(e){ 434 | reject(e) 435 | } 436 | }) 437 | 438 | }) 439 | 440 | newPromise = new Promise(function(resolve,reject){ 441 | setTimeout(function () { 442 | try{ 443 | let x = onRejected(_this.reason) 444 | resolve(x) 445 | }catch(e){ 446 | reject(e) 447 | } 448 | }) 449 | 450 | }) 451 | 452 | newPromise = new Promise(function(resolve,reject){ 453 | _this.onResolvedCallbacks.push(function(){ 454 | setTimeout(function () { 455 | try { 456 | let x = onFulfilled(_this.value) 457 | resolve(x) 458 | } catch (e) { 459 | reject(e) 460 | } 461 | }) 462 | }) 463 | _this.onRejectedCallbacks.push(function(){ 464 | setTimeout(function () { 465 | try { 466 | let x = onFulfilled(_this.value) 467 | resolve(x) 468 | } catch (e) { 469 | reject(e) 470 | } 471 | }) 472 | }) 473 | 474 | }) 475 | ``` 476 | 477 | 验证`Q5`发现,打印出的结果是符合我们预期的。增量代码:[https://github.com/yonglijia/JSPI/blob/master/prototype/promise_async_then_6.js](https://github.com/yonglijia/JSPI/blob/master/prototype/promise_async_then_6.js)。那再验证下其他的异步情况 478 | 479 | `Q6` 480 | 481 | ```javascript 482 | setTimeout(()=>{ 483 | console.log(5) 484 | }) 485 | new Promise((resolve,reject)=>{ 486 | console.log(1) 487 | resolve(3) 488 | Promise.resolve(4).then((value)=>{ 489 | console.log(value) 490 | }) 491 | }).then((value)=>{ 492 | console.log(value) 493 | }) 494 | console.log(2) 495 | ``` 496 | 497 | 结果:`1,2,5,4,3`。 498 | 499 | 用原生的验证下,发现结果是`1,2,4,3,5`,那我们的为什么和原生的结果不一样呢?这要从JS的执行机制娓娓道来! 500 | 501 | 上图说话: 502 | 503 | ![](http://ody1t82mr.bkt.clouddn.com/2018-05-25-15272186536477.jpg) 504 | 505 | 从上图中可以看出,js的事件循环是先执行宏任务,再执行微任务。每一次执行一个宏任务,都去查看微任务队列,如果有微任务,就执行所有的微任务队列。(这里不细说,详细可参考:[https://juejin.im/post/59e85eebf265da430d571f89](https://juejin.im/post/59e85eebf265da430d571f89)) 506 | 507 | 宏任务和微任务的分类如下: 508 | 509 | - **macro-task:** script(整体代码), `setTimeout`, `setInterval`, `setImmediate`, I/O, UI rendering 510 | - **micro-task:** `process.nextTick`, `Promise`(原生 Promise),`Object.observe`, `MutationObserver` 511 | 512 | 根据这个分类,`Q6`的执行过程可以表示为 513 | 514 | ![](http://ody1t82mr.bkt.clouddn.com/2018-05-25-15272204338046.jpg) 515 | 516 | 整个流程如下:先执行宏任务中的整体代码,遇到setTimeout,它属于宏任务,放到宏任务队列里面,然后执行Promise构造函数,打印1。Promise是个微任务,会把构造函数内部Promise的`console.log(4)`放到微任务中,然后外面Promise中的`console.log(3)`。继续执行打印2。当前宏任务执行完毕,执行微任务队列,打印4,3。微任务队列为空,继续执行宏任务,打印5,整个流程结束,结果`12435`。 517 | 518 | 梳理完上面的流程,再看我们的代码就能明白,使用setTimeout宏任务来异步执行then回调方法是不太合适的,应该把setTimeout替换成微任务方法(推荐使用**immediate**这个库),比如process.nextTick,你可以去验证下,结果肯定是`12435`了。浏览器环境可以使用`MutationObserver`。我封装了下asyncCall 519 | 520 | ```javascript 521 | var asyncCall = (process && process.nextTick) || setImmediate || function (callback) { 522 | if (typeof callback !== 'function') 523 | throw new TypeError('callback is not a function'); 524 | var Mutation = window.MutationObserver || window.WebKitMutationObserver; 525 | var observer = new Mutation(callback); 526 | var element = document.createTextNode(''); 527 | observer.observe(element, { 528 | characterData: true 529 | }); 530 | element.data = 1; 531 | } || function (callback) { 532 | if (typeof callback !== 'function') 533 | throw new TypeError('callback is not a function'); 534 | setTimeout(callback, 0); 535 | }; 536 | ``` 537 | 538 | 这里简单起见,就先用process.nextTick来改造。增量代码:[https://github.com/yonglijia/JSPI/blob/master/prototype/promise_nextTick_7.js](https://github.com/yonglijia/JSPI/blob/master/prototype/promise_nextTick_7.js) 539 | 540 | 下面来看下一个问题: 541 | 542 | `Q7` 543 | 544 | ```javascript 545 | new Promise((resolve, reject) => { 546 | resolve(new Promise((resolve) => { 547 | resolve(1) 548 | })) 549 | }).then((value) => { 550 | console.log('success:', value) 551 | }, (reason) => { 552 | console.log('failed:', reason) 553 | }) 554 | ``` 555 | 556 | 结果: 557 | 558 | ```javascript 559 | success: Promise { 560 | status: 'resolved', 561 | value: 1, 562 | reason: null, 563 | onRejectedCallbacks: [], 564 | onResolvedCallbacks: [] } 565 | ``` 566 | 567 | 使用原生的Promise运行,结果是 568 | 569 | ``` 570 | success: 1 571 | ``` 572 | 573 | 我们无法正确处理,写的不符合规范,也就是对传入进来的值没有进行任何处理。对于下面的异常情况,我们一样无法处理: 574 | 575 | 1. 传进来的是当前promise 576 | 2. thenable 577 | 578 | ```javascript 579 | new Promise((resolve, reject) => { 580 | resolve({ 581 | then: (resolve,reject)=>{ 582 | resolve(1) 583 | } 584 | }) 585 | }).then((value) => { 586 | console.log('success:', value) 587 | }, (reason) => { 588 | console.log('failed:', reason) 589 | }) 590 | ``` 591 | 592 | 3. 或者一调用就出错的thenable 593 | 594 | ```javascript 595 | let promise = {} 596 | Object.defineProperty(promise,'then',{ 597 | value: function(){ 598 | throw new Error('出错了吧') 599 | } 600 | }) 601 | ``` 602 | 603 | 4. 调用了resolve,又调用了reject 604 | 605 | ```javascript 606 | new Promise((resolve, reject) => { 607 | resolve(new Promise((resolve) => { 608 | resolve(1) 609 | })) 610 | reject('error') 611 | }).then((value) => { 612 | console.log('success:', value) 613 | }, (reason) => { 614 | console.log('failed:', reason) 615 | }) 616 | ``` 617 | 618 | 等等。 619 | 620 | 其实这些规范都考虑到了。 621 | 622 | 要解决这个问题,还得先理解下上面我们总结的第四个核心点 623 | 624 | > 4.Promise中必须包含一个resolve方法,能够接受各种类型的值,将其处理成普通值fulfilled或者直接rejected 625 | 626 | 这也是规范里面2.3规定的。下面我们来一点点的按照规范,写出一个复合规范的resolve。 627 | 628 | ### resolvePromise 629 | 630 | 首先改造Promise构造函数中的resolve 631 | 632 | ```javascript 633 | function resolve(value) { 634 | resolvePromise(_this,value,fulfill,reject) 635 | } 636 | 637 | function fulfill(value){ //只接受普通值,不接受promise和thenable 638 | if (_this.status === 'pending') { 639 | _this.status = 'resolved' 640 | _this.value = value 641 | _this.onResolvedCallbacks.forEach(function (fn) { 642 | fn() 643 | }) 644 | } 645 | } 646 | ``` 647 | 648 | 将原来的resolve改成fulfill方法。现在你应该能明白上面提到的fulfill和resolve不是一个概念了吧! 649 | 650 | > fulfill只是一个状态改变器并且在改变完状态之后使用传进来的普通值,调用回调数组里面的回调函数。 651 | > 652 | > resolve是将传进来的数据,处理成一个普通值,并根据处理的情况,决定是否fulfill还是reject。 653 | 654 | 来完善resolvePromise: 655 | 656 | ```javascript 657 | function resolvePromise(promise,x,fulfill,reject) { 658 | 659 | if (promise === x) {//2.3.1 传进来的x与当前promise相同,报错 660 | return reject(new TypeError('循环引用了')) 661 | } 662 | 663 | //2.3.2 x如果是一个promise 664 | if (x instanceof Promise) { 665 | //2.3.2.1 666 | if (x.status === 'pending') { //x状态还未改变,返回的下一个promise的resove的接收的值y不确定,对其递归处理 667 | x.then(function(y) { 668 | resolvePromise(promise, y, fulfill, reject) 669 | },reject) 670 | } else { 671 | //2.3.2.2 , 2.3.2.3 672 | //状态确定,如果fulfill那传进来的肯定是普通值,如果reject直接处理,不管你抛出来的是什么东东 673 | x.then(fulfill, reject) 674 | } 675 | return; 676 | } 677 | fulfill(x) 678 | } 679 | ``` 680 | 681 | 我们先写到这里,验证下`Q7`,发现结果是正确的,`success: 1`。增量代码:[https://github.com/yonglijia/JSPI/blob/master/prototype/promise_without_called_8.js](https://github.com/yonglijia/JSPI/blob/master/prototype/promise_without_called_8.js) 682 | 683 | 再来折腾下,用现在的promise来执行另一个问题:`Q8` 684 | 685 | ```javascript 686 | new Promise((resolve, reject) => { 687 | resolve(new Promise((resolve) => { 688 | resolve(1) 689 | })) 690 | reject('error') 691 | }).then((value) => { 692 | console.log('success:', value) 693 | }, (reason) => { 694 | console.log('failed:', reason) 695 | }) 696 | ``` 697 | 698 | 发现结果是:`failed:error`。为什么我们写的promise的状态不可控? 699 | 700 | 事实上是这样的,根据上面的resolvePromise,发现resolve接收的参数是一个promise,会去递归调用它的then方法,我们知道,then方法中包含微任务。然后就先执行了reject('error'),这个执行完毕,promise的状态从pending更新为reject。执行then方法,将onRejected方法放到微任务对列中。当resolve的微任务执行的时候,状态已经改变了,无法执行fulfill的操作。执行下一个微任务onRejected,打印了`failed:error`。 701 | 702 | 从这个问题可以看出,我们写的promise,resolve和reject的调用并不阻塞promise状态的更新。 703 | 704 | 标准只规定了,状态改变一次就不能改变了,并没有规定resolve和reject的调用,要阻塞状态更新。虽然并没有这么硬性规定,但是大家都是这么理解的,比如你可以运行浏览器,node原生promise以及第三方bluebird,Q,lie库等,都是resolve,reject调用的时候,会阻塞另一个方法的状态更新。这也符合常理,不能调用了resolve,再去调用reject,就乱套了 。 705 | 706 | #### Promise状态阻塞更新 707 | 708 | 我们可以通过在Promise的构造函数中添加called变量的方式,来阻塞状态更新(从这里可以看出,在文章开头加的那个注释的意思了吧)。 709 | 710 | ```javascript 711 | try { 712 | let called = false 713 | executor(function (value) { 714 | if(called) return 715 | called = true 716 | resolve(value) 717 | }, function (reason) { 718 | if(called) return 719 | called = true 720 | reject(reason) 721 | }) 722 | } catch (e) { 723 | console.log(e) 724 | reject(e) 725 | } 726 | ``` 727 | 728 | 再次运行`Q8`,结果:`success:1`。 增量代码:[https://github.com/yonglijia/JSPI/blob/master/prototype/promise_with_called_9.js](https://github.com/yonglijia/JSPI/blob/master/prototype/promise_with_called_9.js) 729 | 730 | 我们继续完善`resolvePromise`,来处理下`thenable`的情况 731 | 732 | #### Handle thenable 733 | 734 | ```javascript 735 | function resolvePromise(promise,x,fulfill,reject) { 736 | 737 | if (promise === x) {//2.3.1 传进来的x与当前promise相同,报错 738 | return reject(new TypeError('循环引用了')) 739 | } 740 | //2.3.2 x如果是一个promise 741 | if (x instanceof Promise) { 742 | //2.3.2.1 743 | if (x.status === 'pending') { //x状态还未改变,返回的下一个promise的resove的接收的值y不确定,对其递归处理 744 | x.then(function(y) { 745 | resolvePromise(promise, y, fulfill, reject) 746 | },reject) 747 | } else { 748 | //2.3.2.2 , 2.3.2.3 749 | //状态确定,如果fulfill那传进来的肯定是普通值,如果reject直接处理,不管你抛出来的是什么东东 750 | x.then(fulfill, reject) 751 | } 752 | return; 753 | } 754 | let called = false; 755 | //2.3.3 756 | //x 是一个thenable 757 | if(x !== null && (typeof x === 'object' || typeof x === 'function')){ 758 | try { 759 | //2.3.3.1 760 | let then = x.then; 761 | if (typeof then === 'function') {//2.3.3.3 {then:: (resolve,reject)=>{resolve(1)}}} 762 | then.call(x,(y)=>{ 763 | if (called) return 764 | called = true 765 | resolvePromise(promise,y,fulfill,reject) 766 | },(err)=>{ 767 | if (called) return 768 | called = true 769 | reject(err) 770 | }) 771 | }else{//2.3.3.2 x: {then:1},是一个带then属性的普通值 772 | fulfill(x) 773 | } 774 | }catch(e){//2.3.3.2 可以参见上面说的异常情况2 775 | if (called) return 776 | called = true; 777 | reject(e); 778 | } 779 | }else{//2.3.3.4,x是一个普通值 780 | fulfill(x) 781 | } 782 | } 783 | ``` 784 | 785 | 上面的注释已经很详细了,包括了规范规定的所有异常处理。 786 | 787 | 这里有个疑点需要重点解释一下,我们看到上述代码中出现 788 | 789 | ```javascript 790 | if (called) return 791 | called = true 792 | ``` 793 | 794 | called变量是干嘛的?我们不是刚加了这个变量吗?这里的变量和我们刚才添加的有什么不一样呢? 795 | 796 | 这个要通过下面的例子来进行解释 797 | 798 | ```javascript 799 | new Promise((resolve,reject)=>{ 800 | resolve({ 801 | then:(onFulfilled,onRejected)=>{ 802 | onFulfilled(new Promise((resolve1)=>{ 803 | setTimeout(()=>{ 804 | resolve1(456) 805 | },1000) 806 | })) 807 | onRejected(789) 808 | } 809 | }) 810 | }).then((value)=>{ 811 | console.log('success:',value) 812 | },(reason)=>{ 813 | console.log('reject:',reason) 814 | }) 815 | ``` 816 | 817 | 其实上面代码就类似于 818 | 819 | ```javascript 820 | new Promise((resolve,reject)=>{ 821 | resolve(new Promise((resolve,reject)=>{ 822 | resolve(new Promise((resolve1)=>{ 823 | setTimeout(()=>{ 824 | resolve1(456) 825 | },1000) 826 | })) 827 | reject(789) 828 | }) 829 | }).then((value)=>{ 830 | console.log('success:',value) 831 | },(reason)=>{ 832 | console.log('reject:',reason) 833 | }) 834 | ``` 835 | 836 | 我们通过上面的代码中可以看出,`thenable`其实就是一个没有状态阻塞更新机制的`promise`。这里的called就相当于是为了防止调用了resolve又调用了reject乱套的问题。 837 | 838 | ## 完整代码 839 | 840 | ```javascript 841 | function Promise(executor) { 842 | if (!(this instanceof Promise)) { 843 | return new Promise(executor); 844 | } 845 | 846 | if (typeof executor !== 'function') { 847 | throw new TypeError('Promise executor is not a function'); 848 | } 849 | 850 | let _this = this 851 | _this.status = 'pending' 852 | _this.value = null 853 | _this.reason = null 854 | _this.onRejectedCallbacks = [] 855 | _this.onResolvedCallbacks = [] 856 | 857 | function resolve(value) { 858 | resolvePromise(_this,value,fulfill,reject) 859 | } 860 | 861 | function fulfill(value){ //只接收普通值 862 | if (_this.status === 'pending') { 863 | _this.status = 'resolved' 864 | _this.value = value 865 | _this.onResolvedCallbacks.forEach(function (fn) { 866 | fn() 867 | }) 868 | } 869 | } 870 | 871 | function reject(reason) { 872 | if (_this.status === 'pending') { 873 | _this.status = 'rejected' 874 | _this.reason = reason 875 | _this.onRejectedCallbacks.forEach(function (fn) { 876 | fn() 877 | }) 878 | } 879 | } 880 | 881 | try { 882 | let called = false 883 | executor(function (value) { 884 | if(called) return 885 | called = true 886 | resolve(value) 887 | }, function (reason) { 888 | if(called) return 889 | called = true 890 | reject(reason) 891 | }) 892 | } catch (e) { 893 | reject(e) 894 | } 895 | 896 | } 897 | 898 | function resolvePromise(promise,x,fulfill,reject) { 899 | 900 | if (promise === x) {//2.3.1 传进来的x与当前promise相同,报错 901 | return reject(new TypeError('循环引用了')) 902 | } 903 | 904 | //2.3.2 x如果是一个promise 905 | if (x instanceof Promise) { 906 | //2.3.2.1 907 | if (x.status === 'pending') { //x状态还未改变,返回的下一个promise的resove的接收的值y不确定,对其递归处理 908 | x.then(function(y) { 909 | resolvePromise(promise, y, fulfill, reject) 910 | },reject) 911 | } else { 912 | //2.3.2.2 , 2.3.2.3 913 | //状态确定,如果fulfill那传进来的肯定是普通值,如果reject直接处理,不管你抛出来的是什么东东 914 | x.then(fulfill, reject) 915 | } 916 | return; 917 | } 918 | let called = false; 919 | //2.3.3 920 | //x 是一个thenable 921 | if(x !== null && (typeof x === 'object' || typeof x === 'function')){ 922 | try { 923 | //2.3.3.1 924 | let then = x.then; 925 | if (typeof then === 'function') {//2.3.3.3 {then:: (resolve,reject)=>{resolve(1)}}} 926 | then.call(x,(y)=>{ 927 | if (called) return 928 | called = true 929 | resolvePromise(promise,y,fulfill,reject) 930 | },(err)=>{ 931 | if (called) return 932 | called = true 933 | reject(err) 934 | }) 935 | }else{//2.3.3.2 x: {then:1},是一个带then属性的普通值 936 | fulfill(x) 937 | } 938 | }catch(e){//2.3.3.2 可以参见上面说的异常情况2 939 | if (called) return 940 | called = true; 941 | reject(e); 942 | } 943 | }else{//2.3.3.4,x是一个普通值 944 | fulfill(x) 945 | } 946 | } 947 | Promise.prototype.then = function (onFulfilled, onRejected) { 948 | onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) { 949 | return value 950 | } 951 | onRejected = typeof onRejected === 'function' ? onRejected : function (err) { 952 | throw err 953 | } 954 | let _this = this 955 | let newPromise 956 | if (_this.status === 'resolved') { 957 | newPromise = new Promise(function (resolve, reject) { 958 | process.nextTick(function () { 959 | try { 960 | let x = onFulfilled(_this.value) 961 | resolve(x) 962 | } catch (e) { 963 | reject(e) 964 | } 965 | }) 966 | }) 967 | } 968 | 969 | if (_this.status === 'rejected') { 970 | newPromise = new Promise(function (resolve, reject) { 971 | process.nextTick(function () { 972 | try { 973 | let x = onRejected(_this.reason) 974 | resolve(x) 975 | } catch (e) { 976 | reject(e) 977 | } 978 | }) 979 | }) 980 | } 981 | 982 | if (_this.status === 'pending') { 983 | newPromise = new Promise(function (resolve, reject) { 984 | 985 | _this.onResolvedCallbacks.push(function () { 986 | process.nextTick(function () { 987 | try { 988 | let x = onFulfilled(_this.value) 989 | resolve(x) 990 | } catch (e) { 991 | reject(e) 992 | } 993 | }) 994 | 995 | }) 996 | _this.onRejectedCallbacks.push(function () { 997 | process.nextTick(function () { 998 | try { 999 | let x = onRejected(_this.reason) 1000 | resolve(x) 1001 | } catch (e) { 1002 | reject(e) 1003 | } 1004 | }) 1005 | }) 1006 | 1007 | }) 1008 | } 1009 | return newPromise 1010 | } 1011 | module.exports = Promise 1012 | ``` 1013 | 1014 | 1015 | 1016 | ## 测试 1017 | 1018 | 首先你要暴露一个接口: 1019 | 1020 | ```javascript 1021 | Promise.deferred = Promise.defer = function () { 1022 | var dfd = {} 1023 | dfd.promise = new Promise(function (resolve, reject) { 1024 | dfd.resolve = resolve 1025 | dfd.reject = reject 1026 | }) 1027 | return dfd 1028 | } 1029 | ``` 1030 | 1031 | 使用`promises-aplus-tests`这个库,具体使用方法,请移步它的github去查看吧,不详细介绍了。 1032 | 1033 | ``` 1034 | npm install promises-aplus-tests 1035 | promises-aplus-tests myPromise.js 1036 | ``` 1037 | 1038 | 测试通过: 1039 | 1040 | ![20180528152746135184276.png](http://ody1t82mr.bkt.clouddn.com/20180528152746135184276.png) 1041 | 1042 | 完整代码:[https://github.com/yonglijia/JSPI/blob/master/prototype/promise_final_10.js](https://github.com/yonglijia/JSPI/blob/master/prototype/promise_final_10.js) 1043 | 1044 | ## 其他方法 1045 | 1046 | ```javascript 1047 | Promise.prototype.catch = function(callback){ 1048 | return this.then(null,callback) 1049 | } 1050 | Promise.resolve = function(value){ //返回一个promise 1051 | return new Promise(function(resolve,reject){ 1052 | resolve(value); 1053 | }) 1054 | } 1055 | Promise.reject = function(value){//返回一个promise 1056 | return new Promise(function(resolve,reject){ 1057 | reject(value); 1058 | }) 1059 | } 1060 | Promise.race = function(promise){//只要有一个成功了就resolve,有一个失败了就reject 1061 | return new Promise(function (resolve,reject){ 1062 | for(var i = 0;i