我必须羞耻地承认,我可以谈话的对象数量越来越少了。 —— 维特根斯坦
前面我们写过一篇 Promise 基础,这让我们知道了怎么用 Promise。
但是仅仅知道怎么用就够了嘛?我们还需要知道怎么自行封装一个 Promise。以及如何应付面试(●’◡’●)。
面试
由于我的代码实现过程真的又臭又长,先把面试的写了。
回调函数分为 同步回调函数 和 异步回调函数
同步回调函数会立即执行,不会放入回调队列中,例如 [].forEach(回调函数)、promise 的 excutor 函数
异步回调函数不会立即执行,会放入到回调队列中,例如 setTimeout(回调函数, 0)、ajax 回调、promise 的成功或失败的回调
具体说说 Promise 是什么
- Promise 是 js 中进行异步编程的新的解决方案(旧的是纯回调)
- 语法上来说:Promise 是一个构造函数
- 功能上来说:promise 对象用来封装一个异步操作并可以获取结果
promise 比 纯回调好在哪里(优点)
promise.then() 返回的新 promise 的结果状态由什么决定
1. 简单表达:由 then() 指定的回调函数执行的结果决定
2. 详细表达:
1. 如果抛出异常,新 promise 变为 rejected,reason 为抛出的异常
2. 如果返回的是非 promise 的任意值,新 promise 变为 resolved,value 为返回的值
3. 如果返回的是另一个新的 promise,此 promise 的结果就会成为新 promise 的结果
promise 异常传透
- 当使用 promise 的 then 链式调用时,可以在最后指定失败的调用
- 前面任何操作出了异常,都会传到最后失败的回调中处理
中断 promise 链
- 问题:当使用 promise 的 then 链式调用时,在中间中断,不再调用后面的回调函数
- 解决方法:在回调函数中返回一个 pending 状态的 promise 对象
return new Promise(()=>{})
只要没有执行 resolve 或者 reject 就会保持 pending
代码实现
首先,我们需要先定义一个大的框架。
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
| (function(window) {
function Promise(excutor) {}
Promise.prototype.then = function(onResolved, onRejected) {};
Promise.prototype.catch = function(onRejected) {};
Promise.resolve = function(value) {};
Promise.reject = function(reason) {};
Promise.all = function(promises) {};
Promise.race = function(promises) {};
window.Promise = Promise; })(window);
|
实现执行器
我们的执行器要做的事情有三件。
- 修改状态
- 保存结果
- 保存回调
所以我们首先需要定义三个属性。
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
|
function Promise(excutor) { this.status = "pending"; this.data = undefined; this.callbacks = [];
function resolve(value) { if (this.status === "pending") return; this.status = "fulfilled"; this.data = value; if (this.callbacks.length > 0) { setTimeout(() => { this.callbacks.forEach(callbacksObj => { callbacksObj.onResolved(); }); }, 0); } } function reject(reason) { if (this.status === "pending") return; this.status = "rejected"; this.data = reason; if (this.callbacks.length > 0) { setTimeout(() => { this.callbacks.forEach(callbacksObj => { callbacksObj.onRejected(reason); }); }, 0); } }
try { excutor(resolve, reject); } catch (error) { reject(error); } }
|
这时候我们就可以创建一个 promise 对象了
1 2 3 4 5 6
| const p = new Promise((resolve, reject) => { setTimeout(() => { resolve("操作成功"); }, 500); });
|
发现什么没有?创建 promise 对象时是不是报错了?
这里涉及到一个 this 指向的问题,如果函数是直接执行,则 this 指向 window,所以这里我们要么使用箭头函数,要么绑定 this,要么存储 Promise 的 this。实现方法很多,随意发挥叭。在 Promise 中,之后都会存储 this。后面就不在这样声明了。
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
| function Promise(excutor) { const _this = this; _this.status = "pending"; _this.data = undefined; _this.callbacks = [];
function resolve(value) { if (_this.status !== "pending") return; _this.status = "fulfilled"; _this.data = value; if (_this.callbacks.length > 0) { setTimeout(() => { _this.callbacks.forEach(callbacksObj => { callbacksObj.onResolved(value); }); }, 0); } } function reject(reason) { if (_this.status !== "pending") return; _this.status = "rejected"; _this.data = reason; if (_this.callbacks.length > 0) { setTimeout(() => { _this.callbacks.forEach(callbacksObj => { callbacksObj.onRejected(reason); }); }, 0); } }
try { excutor(resolve, reject); } catch (error) { reject(error); } }
|
这样就可以给自己 new 一个对象啦,开不开心呀(●’◡’●)
实现 then()
可是光能创建一个 promise 对象是没有任何意义的,我们还需要能够得到执行结果才行呀。
then 方法就是用来获取 promise 对象执行结果的方式,也是整个 Promise 构造函数的核心。
先来个简单的,就只能够放入回调函数的就可以了。
1 2 3 4
| Promise.prototype.then = function(onResolved, onRejected) { const _this = this; _this.callbacks.push({ onResolved, onRejected }); };
|
我们只要在构造器中添加这段代码就可以实现最简单的回调了,试试看吧。
可是这样还是不够啊,我们这里实现的是获取结果比我们添加回调函数晚的例子,那如果我们需要立即执行并获取结果呢?
1 2 3 4 5 6 7 8 9 10 11
| const p = new Promise((resolve, reject) => { resolve("操作成功"); }).then( value => { console.log(value); }, reason => { console.log(reason); } );
|
执行不了吧,因为我们在执行 resolve 的时候,回调函数还没有被放入 callbacks 中去,所以无法顺利执行回调。
那我们要怎么改呢?
俗话说 if else 大法好
。不过这里我们确实需要用到判断。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Promise.prototype.then = function(onResolved, onRejected) { const _this = this;
if (_this.status === "pending") { _this.callbacks.push({ onResolved, onRejected }); } else if (_this.status === "fulfilled") { onResolved(_this.data); } else { onRejected(_this.data); } };
|
按照 Promise/A+ 规范,我们需要返回一个新的 promise 对象。
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
| Promise.prototype.then = function(onResolved, onRejected) { const _this = this; return new Promise((resolve, reject) => { if (_this.status === "pending") { _this.callbacks.push({ onResolved, onRejected }); } else if (_this.status === "fulfilled") { const result = onResolved(_this.data); if (result instanceof Promise) { result.then(resolve, reject); } else { resolve(result); } } else { const result = onRejected(_this.data); if (result instanceof Promise) { result.then(resolve, reject); } else { resolve(result); } } }); };
|
写到这里,是不是觉得没有问题了呢,我带着疑惑测试了下面这两个 promise。
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
| const p1 = new Promise((resolve, reject) => { resolve("操作成功"); }); p1.then( value => { return 1; }, reason => { console.log(reason); } ) .then( value => { console.log(value); return value; }, reason => { console.log(reason); return reason + 1; } ) .then( value => { console.log("success:", value); }, reason => { console.log("error: ", reason); } );
const p2 = new Promise((resolve, reject) => { resolve("操作成功"); }); p2.then( value => { return new Promise((resolve, reject) => { reject(1234); }); }, reason => { console.log(reason); } ) .then( value => { console.log(value); return value; }, reason => { console.log(reason); return reason + 1; } ) .then( value => { console.log("success:", value); }, reason => { console.log("error: ", reason); } );
|
然后发现没有问题,哇我的天,是不是成功了。
然后我就去测试了一个延时成功的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const p = new Promise((resolve, reject) => { setTimeout(() => { resolve("操作成功"); }, 1000); }); p.then( value => { console.log(value); return new Promise((resolve, reject) => { reject(1234); }); }, reason => { console.log(reason); } ).then( value => { console.log("success:", value); }, reason => { console.log("error: ", reason); } );
|
延时一秒之后输入 操作成功
,然后再无其他显示,通过不断的调试,发现问题出在了
1
| _this.callbacks.push({ onResolved, onRejected });
|
这一行,为什么呢,因为我们仅仅只是传入一个新的 promise 到 callbacks 中去,而我们还需要有结果啊,结果。
于是我们需要更改这一部分代码。
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
| Promise.prototype.then = function(onResolved, onRejected) { const _this = this; return new Promise((resolve, reject) => { if (_this.status === "pending") { _this.callbacks.push({ onResolved() { try { const result = onResolved(_this.data); if (result instanceof Promise) { result.then(resolve, reject); } else { resolve(result); } } catch (error) { reject(error); } }, onRejected() { try { const result = onRejected(_this.data); if (result instanceof Promise) { result.then(resolve, reject); } else { resolve(result); } } catch (error) { reject(error); } } }); } else if (_this.status === "fulfilled") {
setTimeout(() => { try { const result = onResolved(_this.data); if (result instanceof Promise) { result.then(resolve, reject); } else { resolve(result); } } catch (error) { reject(error); } }, 0); } else { setTimeout(() => { try { const result = onRejected(_this.data); if (result instanceof Promise) { result.then(resolve, reject); } else { resolve(result); } } catch (error) { reject(error); } }, 0); } }); };
|
然后再次通过上面三个测试,大成功。
实现 catch()
相较之下,catch() 的实现就要简单得很多了。
1 2 3 4 5 6 7 8
|
Promise.prototype.catch = function(onRejected) { return this.then(undefined, onRejected); };
|
实现 resolve()
1 2 3 4 5 6 7 8 9 10 11 12 13
|
Promise.resolve = function(value) { return new Promise((resolve, reject) => { if (value instanceof Promise) { value.then(resolve, reject); } else { resolve(value); } }); };
|
实现 reject()
1 2 3 4 5 6 7 8 9
|
Promise.reject = function(reason) { return new Promise((resolve, reject) => { reject(reason); }); };
|
实现 all()
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
|
Promise.all = function(promises) { const values = new Array(promises.length); let resolvedCount = 0; return new Promise((resolve, reject) => { promises.forEach((p, index) => { Promise.resolve(p).then( value => { resolvedCount++; values[index] = value; if (resolvedCount === promises.length) { resolve(values); } }, error => { reject(error); } ); }); }); };
|
实现 race()
1 2 3 4 5 6 7 8 9 10 11
|
Promise.race = function(promises) { return new Promise((resolve, reject) => { promises.forEach(p => { Promise.resolve(p).then(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 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
| (function (window) { const PENDING = 'pending' const FULFILLED = 'fulfilled' const REJECTED = 'rejected'
function Promise(excutor) { const _this = this _this.status = PENDING _this.data = undefined _this.callbacks = []
function resolve(value) { if (_this.status !== PENDING) return _this.status = FULFILLED _this.data = value if (_this.callbacks.length > 0) { setTimeout(() => { _this.callbacks.forEach(callbacksObj => { callbacksObj.onResolved(value) }) }, 0); } }
function reject(reason) { if (_this.status !== PENDING) return _this.status = REJECTED _this.data = reason if (_this.callbacks.length > 0) { setTimeout(() => { _this.callbacks.forEach(callbacksObj => { callbacksObj.onRejected(reason) }) }, 0); } }
try { excutor(resolve, reject) } catch (error) { reject(error) } } Promise.prototype.then = function (onResolved, onRejected) { onResolved = typeof onResolved === 'function' ? onResolved : value => value onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason} const _this = this return new Promise((resolve, reject) => { function handle(callback) { try { const result = callback(_this.data); if (result instanceof Promise) { result.then(resolve, reject); } else { resolve(result); } } catch (error) { reject(error); } } if (_this.status === PENDING) { _this.callbacks.push({ onResolved() { handle(onResolved) }, onRejected() { handle(onRejected) } }) } else if (_this.status === FULFILLED) { setTimeout(() => { handle(onResolved) }, 0); } else { setTimeout(() => { handle(onRejected) }, 0); } }) }; Promise.prototype.catch = function (onRejected) { return this.then(undefined, onRejected) }; Promise.resolve = function (value) { return new Promise((resolve, reject) => { if (value instanceof Promise) { value.then(resolve, reject) } else { resolve(value) } }) }; Promise.reject = function (reason) { return new Promise((resolve, reject) => { reject(reason) }) }; Promise.all = function (promises) { const values = new Array(promises.length) let resolvedCount = 0 return new Promise((resolve, reject) => { promises.forEach((p, index) => { p.then( value => { resolvedCount++ values[index] = value if (resolvedCount === promises.length) { resolve(values) } }, reason => { reject(reason) } ) }) }) }; Promise.race = function (promises) { return new Promise((resolve, reject) => { promises.forEach((p, index) => { p.then(resolve, reject) }) }) }; window.Promise = Promise; })(window);
|
class 代码
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
| const PENDING = 'pending' const FULFILLED = 'fulfilled' const REJECTED = 'rejected'
class Promise { constructor(excutor) { const _this = this _this.status = PENDING _this.data = undefined _this.callbacks = [] function resolve(value) { if (_this.status !== PENDING) return _this.status = FULFILLED _this.data = value if (_this.callbacks.length > 0) { setTimeout(() => { _this.callbacks.forEach(callbacksObj => { callbacksObj.onResolved(value) }) }, 0); } } function reject(reason) { if (_this.status !== PENDING) return _this.status = REJECTED _this.data = reason if (_this.callbacks.length > 0) { setTimeout(() => { _this.callbacks.forEach(callbacksObj => { callbacksObj.onRejected(reason) }) }, 0); } } try { excutor(resolve, reject) } catch (error) { reject(error) } }
then(onResolved, onRejected) { onResolved = typeof onResolved === 'function' ? onResolved : value => value onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason} const _this = this return new Promise((resolve, reject) => { function handle(callback) { try { const result = callback(_this.data); if (result instanceof Promise) { result.then(resolve, reject); } else { resolve(result); } } catch (error) { reject(error); } } if (_this.status === PENDING) { _this.callbacks.push({ onResolved() { handle(onResolved) }, onRejected() { handle(onRejected) } }) } else if (_this.status === FULFILLED) { setTimeout(() => { handle(onResolved) }, 0); } else { setTimeout(() => { handle(onRejected) }, 0); } }) }; catch(onRejected) { return this.then(undefined, onRejected) }; static resolve = function (value) { return new Promise((resolve, reject) => { if (value instanceof Promise) { value.then(resolve, reject) } else { resolve(value) } }) }; static reject = function (reason) { return new Promise((resolve, reject) => { reject(reason) }) }; static all = function (promises) { const values = new Array(promises.length) let resolvedCount = 0 return new Promise((resolve, reject) => { promises.forEach((p, index) => { p.then( value => { resolvedCount++ values[index] = value if (resolvedCount === promises.length) { resolve(values) } }, reason => { reject(reason) } ) }) }) }; static race = function (promises) { return new Promise((resolve, reject) => { promises.forEach((p, index) => { p.then(resolve, reject) }) }) }; }
exports.Promise = Promise
|
结束
啊~ 终于写完了,其实里面内容都是抄的,我能怎么办,我也在学呀!
又又失业了,已经快要一个月没有上班了,感觉整个废人一样,只能靠学点东西过日子,但是没得钱,好难过。
呐,未来的我,你在干什么呢?
参考
Promise 教程
Promise/A+ 规范