Promise 对象
概述
是异步编程的一种解决方案。
从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。
Promise 状态
状态的特点
Promise 异步操作有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。除了异步操作的结果,任何其他操作都无法改变这个状态。
Promise 对象只有:从 pending 变为 fulfilled 和从 pending 变为 rejected 的状态改变。只要处于 fulfilled 和 rejected ,状态就不会再变了即 resolved(已定型)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const p1 = new Promise(function(resolve,reject){ resolve('success1'); resolve('success2'); }); const p2 = new Promise(function(resolve,reject){ resolve('success3'); reject('reject'); }); p1.then(function(value){ console.log(value); }); p2.then(function(value){ console.log(value); });
|
状态的缺点
无法取消 Promise ,一旦新建它就会立即执行,无法中途取消。
如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
then 方法
then 方法接收两个函数作为参数,第一个参数是 Promise 执行成功时的回调,第二个参数是 Promise 执行失败时的回调,两个函数只会有一个被调用。
then 方法的特点
在 JavaScript 事件队列的当前运行完成之前,回调函数永远不会被调用。
1 2 3 4 5 6 7 8 9 10 11
| const p = new Promise(function(resolve,reject){ resolve('success'); }); p.then(function(value){ console.log(value); }); console.log('first');
|
通过 .then 形式添加的回调函数,不论什么时候,都会被调用。
通过多次调用 .then ,可以添加多个回调函数,它们会按照插入顺序并且独立运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const p = new Promise(function(resolve,reject){ resolve(1); }).then(function(value){ console.log(value); return value * 2; }).then(function(value){ console.log(value); }).then(function(value){ console.log(value); return Promise.resolve('resolve'); }).then(function(value){ console.log(value); return Promise.reject('reject'); }).then(function(value){ console.log('resolve:' + value); }, function(err) { console.log('reject:' + err); });
|
then 方法将返回一个 resolved 或 rejected 状态的 Promise 对象用于链式调用,且 Promise 对象的值就是这个返回值。
then 方法注意点
简便的 Promise 链式编程最好保持扁平化,不要嵌套 Promise。
注意总是返回或终止 Promise 链。
1 2 3 4 5
| const p1 = new Promise(function(resolve,reject){ resolve(1); }).then(function(result) { p2(result).then(newResult => p3(newResult)); }).then(() => p4());
|
创建新 Promise 但忘记返回它时,对应链条被打破,导致 p4 会与 p2 和 p3 同时进行。
大多数浏览器中不能终止的 Promise 链里的 rejection,建议后面都跟上 .catch(error => console.log(error));
手写 Promise
Promise 的调用方式,new Promise(executor), executor 两个参数,resolve,reject。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| class Promise { constructor(executor) { const resolve = () => {} const reject = () => {} executor(resolve, rejcet) } }
const STATUS = { PENDING: 'PENDING', FUFILLED: 'FUFILLED', REJECTED: 'REJECTED' } class Promise { constructor(executor) { this.status = STATUS.PENDING; this.value = undefined; this.reason = undefined; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; const resolve = (val) => { if (this.status == STATUS.PENDING) { this.status = STATUS.FUFILLED; this.value = val; this.onResolvedCallbacks.forEach(fn=>fn()); } } const reject = (reason) => { if (this.status == STATUS.PENDING) { this.status = STATUS.REJECTED; this.reason = reason; this.onRejectedCallbacks.forEach(fn=>fn()); } } try { executor(resolve, reject); } catch (e) { reject(e) } } then(onFulfilled, onRejected) { let promise2 = new Promise((resolve, reject) => { if (this.status === STATUS.FUFILLED) { try { let x = onFulfilled(this.value); resolve(x); } catch (e) { reject(e); } } if (this.status === STATUS.REJECTED) { try { let x = onRejected(this.reason); resolve(x); } catch (e) { reject(e); } } if (this.status === STATUS.PENDING) { this.onResolvedCallbacks.push(() => { try { let x = onFulfilled(this.value); resolve(x); } catch (e) { reject(e); } }) this.onRejectedCallbacks.push(() => { try { let x = onRejected(this.reason); resolve(x); } catch (e) { reject(e); }
}) } })
return promise2; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
| const STATUS = { PENDING: 'PENDING', FUFILLED: 'FUFILLED', REJECTED: 'REJECTED' }
function resolvePromise(x, promise2, resolve, reject) { if (promise2 == x) { return reject(new TypeError('出错了')) } if ((typeof x === 'object' && x !== null) || typeof x === 'function') { let called; try { let then = x.then; if (typeof then == 'function') { then.call(x, function(y) { if (called) return called = true; resolvePromise(y, promise2, resolve, reject); }, function(r) { if (called) return called = true; reject(r); }) } else { resolve(x); } } catch (e) { if (called) return called = true; reject(e); } } else { resolve(x); } } class Promise { constructor(executor) { this.status = STATUS.PENDING; this.value = undefined; this.reason = undefined; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; const resolve = (val) => { if(val instanceof Promise){ return val.then(resolve,reject) }
if (this.status == STATUS.PENDING) { this.status = STATUS.FUFILLED; this.value = val; this.onResolvedCallbacks.forEach(fn => fn()); } } const reject = (reason) => { if (this.status == STATUS.PENDING) { this.status = STATUS.REJECTED; this.reason = reason; this.onRejectedCallbacks.forEach(fn => fn()); } } try { executor(resolve, reject); } catch (e) { reject(e) } } then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x onRejected = typeof onRejected === 'function'? onRejected: err=> {throw err} let promise2 = new Promise((resolve, reject) => { if (this.status === STATUS.FUFILLED) { setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(x, promise2, resolve, reject) } catch (e) { reject(e); } }, 0); } if (this.status === STATUS.REJECTED) { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(x, promise2, resolve, reject) } catch (e) { reject(e); } }, 0); } if (this.status === STATUS.PENDING) { this.onResolvedCallbacks.push(() => { setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(x, promise2, resolve, reject) } catch (e) { reject(e); } }, 0); }) this.onRejectedCallbacks.push(() => { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(x, promise2, resolve, reject) } catch (e) { reject(e); } }, 0);
}) } }); return promise2; } }
|