理解一个东西最好的办法之一就是动手本身写,So,他(Promise)来了。废话很少说,来看如何实现。面试
“五岳归来不看山,黄山归来不看岳。”但愿看完这篇,你就不用再去看其余 Promise 的实现原理了。数组
先来看Promise
用法:promise
new Promise((resolve, reject) => { resolve('hello'); // or reject('hello') }) .then(res => {}) .catch(err => {}) -----------分割线 // 分解一下,也就是下面这样 let executor = (resolve, reject) => { resolve('hello'); // or reject('hello') } new Promise(executor) .then(res => {}) .catch(err => {}) 复制代码
咱们来分析一下他有哪些功能/特性:浏览器
接下来,咱们要一一撕下他的假装,揭开他的真面目。bash
基于上面分析结果,咱们先来实现前面三个功能:markdown
class Promise { constructor(executor) { // 定义 resolve let resolve = res => {} // 定义 reject let reject = err => {} // 自动执行 executor(resolve, reject); } } // 测试一下: new Promise((resolve, reject) => { console.log('执行到啦~') }) 复制代码
能够将上面代码复制到控制台执行,查看效果:异步
Ok,fine,接下来,咱们来实现她的三种状态。函数
promise 状态有以下特色:
1.promise 对象初始化状态为 pendingoop
2.当调用resolve(成功),会由pending => fulfilled测试
3.当调用reject(失败),会由pending => rejected
Promsie 状态 只能由 pending => fulfilled/rejected, 一旦修改就不能再变
class Promise { constructor(executor) { this.status = "pending"; // 默认状态 this.value; // resolve 成功时的值 this.error; // reject 失败时的值 let resolve = res => { if(this.status === "pending") { this.value = res; this.status = "resolved"; } } let reject = err => { if(this.status === "pending") { this.error = err; this.status = "rejected"; } } executor(resolve, reject); } } 复制代码
测试一下,若是不去resolve,也不去reject
// 测试一下:
new Promise((resolve, reject) => {
})
复制代码
那么Promise应该是初始状态。咱们将上面代码执行测试一下,获得结果以下:
{status: "pending"}
。
当咱们执行 resolve
// 测试一下: new Promise((resolve, reject) => { resolve('成功啦~'); }) 复制代码
将获得结果以下:
当执行 reject
// 测试一下: new Promise((resolve, reject) => { resolve('失败啦~') }) 复制代码
Promise 这个对象有 then 方法,仍是先来分析,then 有什么?
promise.then(onFulfilled, onRejected); // 这里假设 promise 继承于 Promise 类
复制代码
咱们继续在前面 Promise 类中书写 then 方法:
class Promise { constructor(executor) { this.status = "pending"; // 默认promise状态 this.value; // resolve成功时的值 this.error; // reject失败时的值 let resolve = res => { if(this.status === "pending") { this.value = res; this.status = "resolved"; } } let reject = err => { if(this.status === "pending") { this.error = err; this.status = "rejected"; } } executor(resolve, reject) } // 声明 then then(onFullfilled, onRejected) { if(this.status === "resolved") { onFullfilled(this.value) } if(this.status === "rejected") { onRejected(this.error) } } } 复制代码
测试一下:
new Promise((resolve, reject) => { resolve("成功啦~"); // 或 reject("失败啦~") }) .then(res => { console.log(res); }, err => { console.log(err); }) 复制代码
获得结果:
至此,基本实现简单的同步代码,可是当 resolve 在 setTimeout
内执行,then 时 state 仍是 pending 等待状态。咱们就须要在 then 调用的时候,将成功和失败存到各自的数组,一旦 reject 或者 resolve,就调用它们。
相似于分布订阅,先将 then 内的两个函数存储,因为 promise 能够有多个 then,因此存在同一个数组内。当成功或失败的时候用 forEach 调用他们。
class Promise { constructor(executor) { this.status = "pending"; // 默认promise状态 this.value; // resolve成功时的值 this.error; // reject失败时的值 + this.resolveQueue = []; // 成功存放的数组 + this.rejectQueue = []; // 失败存放法数组 let resolve = value => { if(this.status === "pending") { this.value = value; this.status = "resolved"; // 一旦resolve执行,调用成功数组的函数 + this.resolveQueue.forEach(fn => fn()); } } let reject = value => { if(this.status === "pending") { this.error = value; this.status = "rejected"; } // 一旦reject执行,调用失败数组的函数 + this.rejectQueue.forEach(fn => fn()); } executor(resolve, reject) } // 执行到then的时候 then(onFullfilled, onRejected) { if(this.status === "resolved") { this.resolveQueue.push(() => { onFullfilled(this.value); }) } if(this.status === "rejected") { this.rejectQueue.push(() => { onRejected(this.error); }) } // 当状态state为pending时 + if(this.status === "pending") { // onFulfilled传入到成功数组 + this.resolveQueue.push(() => { + onFullfilled(this.value); + }) // onRejected传入到失败数组 + this.rejectQueue.push(() => { + onRejected(this.error); + }) + } } } 复制代码
咱们经常用到new Promise().then().then()
这样的写法,这就是链式调用,原来是用于解决地狱回调的。那么如何去实现呢? 为了达到这个效果,咱们能够再第一个 then 函数内再返回一个 Promise
,让这个新的 Promise
返回的值传递到下一个 then 中。
一句话总结:
经过在 then 中 return 一个新的
Promise
,从而实现 then 的链式调用!
代码以下:
class Promise { constructor(executor) { this.status = "pending"; // 默认promise状态 this.value; // resolve 成功时的值 this.error; // reject 失败时的值 this.resolveQueue = []; // 成功时回调队列 this.rejectQueue = []; // 失败时回调队列 let resolve = value => { if(this.status === "pending") { this.value = value; this.status = "resolved"; this.resolveQueue.forEach(fn => fn()) } } let reject = value => { if(this.status === "pending") { this.error = value; this.status = "rejected"; this.rejectQueue.forEach(fn => fn()) } } executor(resolve, reject) } then(onFullfilled, onRejected) { let promise2; if(this.status === "resolved") { promise2 = new Promise((resolve, reject) => { let x = onFullfilled(this.value); resolvePromise(promise2, x, resolve, reject); }) } if(this.status === "rejected") { promise2 = new Promise((resolve, reject) => { let x = onRejected(this.value); resolvePromise(promise2, x, resolve, reject); }) } if(this.status === "pending") { promise2 = new Promise((resolve, reject) => { this.resolveQueue.push(() => { let x = onFullfilled(this.value); resolvePromise(promise2, x, resolve, reject); }) this.rejectQueue.push(() => { let x = onRejected(this.error); resolvePromise(promise2, x, resolve, reject); }) }) } return promise2; } } -------------------分割线 // 将上面代码整理一下 class Promise { constructor(executor) { this.status = "pending"; // 默认promise状态 this.value; // resolve成功时的值 this.error; // reject失败时的值 this.resolveQueue = []; // 成功时回调队列 this.rejectQueue = []; // 失败时回调队列 let resolve = value => { if(this.status === "pending") { this.value = value; this.status = "resolved"; this.resolveQueue.forEach(fn => fn()) } } let reject = value => { if(this.status === "pending") { this.error = value; this.status = "rejected"; this.rejectQueue.forEach(fn => fn()) } } executor(resolve, reject) } then(onFullfilled, onRejected) { let promise2; promise2 = new Promise((resolve, reject) => { if(this.status === "resolved") { let x = onFullfilled(this.value); // resolvePromise函数,处理本身return的promise和默认的promise2的关系 resolvePromise(promise2, x, resolve, reject); } if(this.status === "rejected") { let x = onRejected(this.value); resolvePromise(promise2, x, resolve, reject); } if(this.status === "pending") { this.resolveQueue.push(() => { let x = onFullfilled(this.value); resolvePromise(promise2, x, resolve, reject); }) this.rejectQueue.push(() => { let x = onRejected(this.error); resolvePromise(promise2, x, resolve, reject); }) } }); // 返回 promise,达成链式效果 return promise2; } } 复制代码
最后,咱们来完成上面的 resolvePromise 函数,咱们暂且将第一个 then 返回的值成为 x,在这个函数中,咱们须要去判断 x 是否是 promise(这里是重点!):
resolvePromise 代码以下:
/** * 处理promise递归的函数 * * promise2 {Promise} 默认返回的promise * x {*} 咱们本身 return 的对象 * resolve * reject */ function resolvePromise(promise2, x, resolve, reject){ // 循环引用报错 if(x === promise2){ // reject 报错抛出 return reject(new TypeError('Chaining cycle detected for promise')); } // 锁,防止屡次调用 let called; // x 不是 null 且 x 是对象或者函数 if (x != null && (typeof x === 'object' || typeof x === 'function')) { try { // A+ 规定,声明then = x的then方法 let then = x.then; // 若是then是函数,就默认是promise了 if (typeof then === 'function') { // then 执行 第一个参数是 this 后面是成功的回调 和 失败的回调 then.call(x, y => { // 成功和失败只能调用一个 if (called) return; called = true; // 核心点2:resolve 的结果依旧是 promise 那就继续递归执行 // 核心点2:resolve 的结果依旧是 promise 那就继续递归执行 // 核心点2:resolve 的结果依旧是 promise 那就继续递归执行 resolvePromise(promise2, y, resolve, reject); }, err => { // 成功和失败只能调用一个 if (called) return; called = true; reject(err);// 失败了就失败了 }) } else { resolve(x); // 直接成功便可 } } catch (e) { // 走到 catch 也属于失败 if (called) return; called = true; // 取then出错了那就不要在继续执行了 reject(e); } } else { resolve(x); } } 复制代码
完整测试代码以下,能够复制进浏览器控制台执行下:
function resolvePromise(promise2, x, resolve, reject){ // 循环引用报错 if(x === promise2){ // reject 报错抛出 return reject(new TypeError('Chaining cycle detected for promise')); } // 锁,防止屡次调用 let called; // x不是null 且x是对象或者函数 if (x != null && (typeof x === 'object' || typeof x === 'function')) { try { // A+ 规定,声明then = x的then方法 let then = x.then; // 若是then是函数,就默认是promise了 if (typeof then === 'function') { // 就让then执行 第一个参数是this 后面是成功的回调 和 失败的回调 then.call(x, y => { // 成功和失败只能调用一个 if (called) return; called = true; // resolve的结果依旧是promise 那就继续递归执行 resolvePromise(promise2, y, resolve, reject); }, err => { // 成功和失败只能调用一个 if (called) return; called = true; reject(err);// 失败了就失败了 }) } else { resolve(x); // 直接成功便可 } } catch (e) { // 也属于失败 if (called) return; called = true; // 取then出错了那就不要在继续执行了 reject(e); } } else { resolve(x); } } class Promise { constructor(executor) { this.status = "pending"; // 默认promise状态 this.value; // resolve成功时的值 this.error; // reject失败时的值 this.resolveQueue = []; // 成功时回调队列 this.rejectQueue = []; // 失败时回调队列 let resolve = value => { if(this.status === "pending") { this.value = value; this.status = "resolved"; this.resolveQueue.forEach(fn => fn()) } } let reject = value => { if(this.status === "pending") { this.error = value; this.status = "rejected"; this.rejectQueue.forEach(fn => fn()) } } executor(resolve, reject) } then(onFullfilled, onRejected) { let promise2; promise2 = new Promise((resolve, reject) => { if(this.status === "resolved") { let x = onFullfilled(this.value); // resolvePromise函数,处理本身return的promise和默认的promise2的关系 resolvePromise(promise2, x, resolve, reject); } if(this.status === "rejected") { let x = onRejected(this.value); resolvePromise(promise2, x, resolve, reject); } if(this.status === "pending") { this.resolveQueue.push(() => { let x = onFullfilled(this.value); resolvePromise(promise2, x, resolve, reject); }) this.rejectQueue.push(() => { let x = onRejected(this.error); resolvePromise(promise2, x, resolve, reject); }) } }); // 返回 promise,达成链式效果 return promise2; } } // 测试如下代码 new Promise((resolve, reject) => { resolve(); }).then((res)=>{ console.log('进入第一个then!') return new Promise((resolve,reject)=>{ resolve('hello world'); }) }).then((res)=>{ console.log('进入第二个then!', res); }) 复制代码
核心思路:
用setTimeout解决异步问题
代码以下:
class Promise { constructor(executor) { this.status = "pending"; // 默认promise状态 this.value; // resolve成功时的值 this.error; // reject失败时的值 this.resolveQueue = []; // 成功时回调队列 this.rejectQueue = []; // 失败时回调队列 let resolve = value => { if(this.status === "pending") { this.value = value; this.status = "resolved"; this.resolveQueue.forEach(fn => fn()) } } let reject = value => { if(this.status === "pending") { this.error = value; this.status = "rejected"; this.rejectQueue.forEach(fn => fn()) } } executor(resolve, reject) } then(onFullfilled, onRejected) { let promise2; promise2 = new Promise((resolve, reject) => { if(this.status === "resolved") { // 异步 + setTimeout(() => { let x = onFullfilled(this.value); // resolvePromise函数,处理本身return的promise和默认的promise2的关系 resolvePromise(promise2, x, resolve, reject); + }, 0) } if(this.status === "rejected") { // 异步 + setTimeout(() => { let x = onRejected(this.value); resolvePromise(promise2, x, resolve, reject); + }, 0) } if(this.status === "pending") { this.resolveQueue.push(() => { // 异步 + setTimeout(() => { let x = onFullfilled(this.value); resolvePromise(promise2, x, resolve, reject); + }, 0) }) this.rejectQueue.push(() => { // 异步 + setTimeout(() => { let x = onRejected(this.error); resolvePromise(promise2, x, resolve, reject); + }, 0) }) } }); // 返回 promise,达成链式效果 return promise2; } } 复制代码
new Promise((resolve, reject)=>{ resolve('YoYo'); }).then().then().then().then().then().then().then((res)=>{ console.log(res); }) 复制代码
当执行上面多个 then,咱们指望最后那个 then 打印出 'YoYo'。
实现很简单:onFulfilled 若是不是函数,就忽略 onFulfilled,直接返回 value!
相应的,咱们也要处理下没有 onRejected 的状况:onRejected 若是不是函数,就忽略 onRejected,直接扔出错误!
代码以下,在以前的 Promise 类的 then 加入:
then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err;} <!--... 省略--> } 复制代码
核心思路:
catch 是失败的回调,至关于执行 this.then(null,fn)
class Promise { constructor(executor) { <!--... 省略--> } then(onFullfilled, onRejected) { <!--... 省略--> } + catch(onRejected) { + return this.then(null, onRejected) + } } 复制代码
此外,咱们还须要对其余几个函数外使用 try/catch 去作异常捕获,这里不展开,理解便可(本文最后源码中会展现)。
这是一道经典面试题!
Promise.all()
接收一个数组做为参数,该方法返回一个 Promise
实例,此实例在 iterable 参数内全部的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);若是参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败的缘由是第一个失败 promise 的结果。
用法以下:
var p1 = Promise.resolve(3); var p2 = 1337; var p3 = new Promise((resolve, reject) => { setTimeout(resolve, 100, 'foo'); }); Promise.all([p1, p2, p3]).then(values => { console.log(values); // [3, 1337, "foo"] }); 复制代码
接下来看看如何实现: 下面手写的,没有测试,回头补充!下班先~
Promise.all = function(promises) { let count = 0; let res = []; return new Promise((resolve, reject) => { for(let i = 0; i<promises.length; i++) { promises[i].then(res => { res.push(res); count++; if(count === promises.length) resolve(res); }) } }) .catch(err => { reject(err); }) } 复制代码
Promise.race() 它一样接收一个promise对象组成的数组做为参数,并返回一个新的promise对象。一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。
Promise.race = function(promises) { return new Promise((resolve, reject) => { for(let i = 0; i<promises.length; i++) { promises[i].then(resolve, reject); } }) } 复制代码
Promise.resolve = function(value) { return new Promise((resolve, reject) => { resolve(value); }) } 复制代码
Promise.reject = function(value) { return new Promise((resolve, reject) => { reject(value); }) } 复制代码
当课后做业吧,同窗们本身写,能够放回复中哈~
Reference: Promises/A+