Promise是前端面试中的高频问题,我做为面试官的时候,问Promise的几率超过90%,据我所知,大多数公司,都会问一些关于Promise的问题。若是你能根据PromiseA+的规范,写出符合规范的源码,那么我想,对于面试中的Promise相关的问题,都可以给出比较完美的答案。javascript
个人建议是,对照规范多写几回实现,也许第一遍的时候,是改了屡次,才能经过测试,那么须要反复的写,我已经将Promise的源码实现写了不下七遍。前端
/** * 1. new Promise时,须要传递一个 executor 执行器,执行器马上执行 * 2. executor 接受两个参数,分别是 resolve 和 reject * 3. promise 只能从 pending 到 rejected, 或者从 pending 到 fulfilled * 4. promise 的状态一旦确认,就不会再改变 * 5. promise 都有 then 方法,then 接收两个参数,分别是 promise 成功的回调 onFulfilled, * 和 promise 失败的回调 onRejected * 6. 若是调用 then 时,promise已经成功,则执行 onFulfilled,并将promise的值做为参数传递进去。 * 若是promise已经失败,那么执行 onRejected, 并将 promise 失败的缘由做为参数传递进去。 * 若是promise的状态是pending,须要将onFulfilled和onRejected函数存放起来,等待状态肯定后,再依次将对应的函数执行(发布订阅) * 7. then 的参数 onFulfilled 和 onRejected 能够缺省 * 8. promise 能够then屡次,promise 的then 方法返回一个 promise * 9. 若是 then 返回的是一个结果,那么就会把这个结果做为参数,传递给下一个then的成功的回调(onFulfilled) * 10. 若是 then 中抛出了异常,那么就会把这个异常做为参数,传递给下一个then的失败的回调(onRejected) * 11.若是 then 返回的是一个promise,那么须要等这个promise,那么会等这个promise执行完,promise若是成功, * 就走下一个then的成功,若是失败,就走下一个then的失败 */ const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; function Promise(executor) { let self = this; self.status = PENDING; self.onFulfilled = [];//成功的回调 self.onRejected = []; //失败的回调 //PromiseA+ 2.1 function resolve(value) { if (self.status === PENDING) { self.status = FULFILLED; self.value = value; self.onFulfilled.forEach(fn => fn());//PromiseA+ 2.2.6.1 } } function reject(reason) { if (self.status === PENDING) { self.status = REJECTED; self.reason = reason; self.onRejected.forEach(fn => fn());//PromiseA+ 2.2.6.2 } } try { executor(resolve, reject); } catch (e) { reject(e); } } Promise.prototype.then = function (onFulfilled, onRejected) { //PromiseA+ 2.2.1 / PromiseA+ 2.2.5 / PromiseA+ 2.2.7.3 / PromiseA+ 2.2.7.4 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }; let self = this; //PromiseA+ 2.2.7 let promise2 = new Promise((resolve, reject) => { if (self.status === FULFILLED) { //PromiseA+ 2.2.2 //PromiseA+ 2.2.4 --- setTimeout setTimeout(() => { try { //PromiseA+ 2.2.7.1 let x = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { //PromiseA+ 2.2.7.2 reject(e); } }); } else if (self.status === REJECTED) { //PromiseA+ 2.2.3 setTimeout(() => { try { let x = onRejected(self.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }); } else if (self.status === PENDING) { self.onFulfilled.push(() => { setTimeout(() => { try { let x = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }); }); self.onRejected.push(() => { setTimeout(() => { try { let x = onRejected(self.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }); }); } }); return promise2; } function resolvePromise(promise2, x, resolve, reject) { let self = this; //PromiseA+ 2.3.1 if (promise2 === x) { reject(new TypeError('Chaining cycle')); } if (x && typeof x === 'object' || typeof x === 'function') { let used; //PromiseA+2.3.3.3.3 只能调用一次 try { let then = x.then; if (typeof then === 'function') { //PromiseA+2.3.3 then.call(x, (y) => { //PromiseA+2.3.3.1 if (used) return; used = true; resolvePromise(promise2, y, resolve, reject); }, (r) => { //PromiseA+2.3.3.2 if (used) return; used = true; reject(r); }); }else{ //PromiseA+2.3.3.4 if (used) return; used = true; resolve(x); } } catch (e) { //PromiseA+ 2.3.3.2 if (used) return; used = true; reject(e); } } else { //PromiseA+ 2.3.3.4 resolve(x); } } module.exports = Promise;
有专门的测试脚本能够测试所编写的代码是否符合PromiseA+的规范。java
首先,在promise实现的代码中,增长如下代码:git
Promise.defer = Promise.deferred = function () { let dfd = {}; dfd.promise = new Promise((resolve, reject) => { dfd.resolve = resolve; dfd.reject = reject; }); return dfd; }
安装测试脚本:es6
npm install -g promises-aplus-tests
若是当前的promise源码的文件名为promise.jsgithub
那么在对应的目录执行如下命令:面试
promises-aplus-tests promise.js
promises-aplus-tests中共有872条测试用例。以上代码,能够完美经过全部用例。shell
对上面的代码实现作一点简要说明(其它一些内容注释中已经写得很清楚):npm
PS: 下面是我翻译的规范,供参考数组
术语
要求
Promise 必须处于如下三个状态之一: pending, fulfilled 或者是 rejected
2.1.1.1 能够变成 fulfilled 或者是 rejected
2.1.2.1 不会变成其它状态 2.1.2.2 必须有一个value值
2.1.3.1 不会变成其它状态 2.1.3.2 必须有一个promise被reject的reason
归纳便是:promise的状态只能从pending变成fulfilled,或者从pending变成rejected.promise成功,有成功的value.promise失败的话,有失败的缘由
promise必须提供一个then方法,来访问最终的结果
promise的then方法接收两个参数
promise.then(onFulfilled, onRejected)
2.2.1.1 onFulfilled 必须是函数类型 2.2.1.2 onRejected 必须是函数类型
2.2.2.1 必须在promise变成 fulfilled 时,调用 onFulfilled,参数是promise的value 2.2.2.2 在promise的状态不是 fulfilled 以前,不能调用 2.2.2.3 onFulfilled 只能被调用一次
2.2.3.1 必须在promise变成 rejected 时,调用 onRejected,参数是promise的reason 2.2.3.2 在promise的状态不是 rejected 以前,不能调用 2.2.3.3 onRejected 只能被调用一次
2.2.6.1 若是promise变成了 fulfilled态,全部的onFulfilled回调都须要按照then的顺序执行 2.2.6.2 若是promise变成了 rejected态,全部的onRejected回调都须要按照then的顺序执行
promise2 = promise1.then(onFulfilled, onRejected);
2.2.7.1 onFulfilled 或 onRejected 执行的结果为x,调用 resolvePromise 2.2.7.2 若是 onFulfilled 或者 onRejected 执行时抛出异常e,promise2须要被reject 2.2.7.3 若是 onFulfilled 不是一个函数,promise2 以promise1的值fulfilled 2.2.7.4 若是 onRejected 不是一个函数,promise2 以promise1的reason rejected
resolvePromise(promise2, x, resolve, reject)
2.3.2.1 若是x是pending态,那么promise必需要在pending,直到 x 变成 fulfilled or rejected. 2.3.2.2 若是 x 被 fulfilled, fulfill promise with the same value. 2.3.2.3 若是 x 被 rejected, reject promise with the same reason.
2.3.3.1 let then = x.then. 2.3.3.2 若是 x.then 这步出错,那么 reject promise with e as the reason.. 2.3.3.3 若是 then 是一个函数,then.call(x, resolvePromiseFn, rejectPromise) 2.3.3.3.1 resolvePromiseFn 的 入参是 y, 执行 resolvePromise(promise2, y, resolve, reject); 2.3.3.3.2 rejectPromise 的 入参是 r, reject promise with r. 2.3.3.3.3 若是 resolvePromise 和 rejectPromise 都调用了,那么第一个调用优先,后面的调用忽略。 2.3.3.3.4 若是调用then抛出异常e 2.3.3.3.4.1 若是 resolvePromise 或 rejectPromise 已经被调用,那么忽略 2.3.3.3.4.3 不然,reject promise with e as the reason 2.3.3.4 若是 then 不是一个function. fulfill promise with x.
虽然上述的promise源码已经符合PromiseA+的规范,可是原生的Promise还提供了一些其余方法,如:
下面具体说一下每一个方法的实现:
Promise.resolve
Promise.resolve(value) 返回一个以给定值解析后的Promise 对象.
Promise.resolve = function (param) { if (param instanceof Promise) { return param; } return new Promise((resolve, reject) => { if (param && param.then && typeof param.then === 'function') { setTimeout(() => { param.then(resolve, reject); }); } else { resolve(param); } }); }
thenable对象的执行加 setTimeout的缘由是根据原生Promise对象执行的结果推断的,以下的测试代码,原生的执行结果为: 20 400 30;为了一样的执行顺序,增长了setTimeout延时。
测试代码:
let p = Promise.resolve(20); p.then((data) => { console.log(data); }); let p2 = Promise.resolve({ then: function(resolve, reject) { resolve(30); } }); p2.then((data)=> { console.log(data) }); let p3 = Promise.resolve(new Promise((resolve, reject) => { resolve(400) })); p3.then((data) => { console.log(data) });
Promise.reject
Promise.reject方法和Promise.resolve不一样,Promise.reject()方法的参数,会原封不动地做为reject的理由,变成后续方法的参数。
Promise.reject = function (reason) { return new Promise((resolve, reject) => { reject(reason); }); }
Promise.prototype.catch
Promise.prototype.catch 用于指定出错时的回调,是特殊的then方法,catch以后,能够继续 .then
Promise.prototype.catch = function (onRejected) { return this.then(null, onRejected); }
Promise.prototype.finally
无论成功仍是失败,都会走到finally中,而且finally以后,还能够继续then。而且会将值原封不动的传递给后面的then.
Promise.prototype.finally = function (callback) { return this.then((value) => { return Promise.resolve(callback()).then(() => { return value; }); }, (err) => { return Promise.resolve(callback()).then(() => { throw err; }); }); }
Promise.all
Promise.all(promises) 返回一个promise对象
Promise.all = function (promises) { return new Promise((resolve, reject) => { let index = 0; let result = []; if (promises.length === 0) { resolve(result); } else { function processValue(i, data) { result[i] = data; if (++index === promises.length) { resolve(result); } } for (let i = 0; i < promises.length; i++) { //promises[i] 多是普通值 Promise.resolve(promises[i]).then((data) => { processValue(i, data); }, (err) => { reject(err); return; }); } } }); }
测试代码:
var promise1 = new Promise((resolve, reject) => { resolve(3); }) var promise2 = 42; var promise3 = new Promise(function(resolve, reject) { setTimeout(resolve, 100, 'foo'); }); Promise.all([promise1, promise2, promise3]).then(function(values) { console.log(values); //[3, 42, 'foo'] },(err)=>{ console.log(err) }); var p = Promise.all([]); // will be immediately resolved var p2 = Promise.all([1337, "hi"]); // non-promise values will be ignored, but the evaluation will be done asynchronously console.log(p); console.log(p2) setTimeout(function(){ console.log('the stack is now empty'); console.log(p2); });
Promise.race
Promise.race函数返回一个 Promise,它将与第一个传递的 promise 相同的完成方式被完成。它能够是完成( resolves),也能够是失败(rejects),这要取决于第一个完成的方式是两个中的哪一个。
若是传的参数数组是空,则返回的 promise 将永远等待。
若是迭代包含一个或多个非承诺值和/或已解决/拒绝的承诺,则 Promise.race 将解析为迭代中找到的第一个值。
Promise.race = function (promises) { return new Promise((resolve, reject) => { if (promises.length === 0) { return; } else { for (let i = 0; i < promises.length; i++) { Promise.resolve(promises[i]).then((data) => { resolve(data); return; }, (err) => { reject(err); return; }); } } }); }
测试代码:
Promise.race([ new Promise((resolve, reject) => { setTimeout(() => { resolve(100) }, 1000) }), undefined, new Promise((resolve, reject) => { setTimeout(() => { reject(100) }, 100) }) ]).then((data) => { console.log('success ', data); }, (err) => { console.log('err ',err); }); Promise.race([ new Promise((resolve, reject) => { setTimeout(() => { resolve(100) }, 1000) }), new Promise((resolve, reject) => { setTimeout(() => { resolve(200) }, 200) }), new Promise((resolve, reject) => { setTimeout(() => { reject(100) }, 100) }) ]).then((data) => { console.log(data); }, (err) => { console.log(err); });
谢谢您花费宝贵的时间阅读本文,若是本文给了您一点帮助或者是启发,那么不要吝啬你的赞和Star哈,您的确定是我前进的最大动力。https://github.com/YvetteLau/...
推荐关注本人公众号: