首先我们先看一下promise是如何使用的:
<script> let p1=new Promise((resolve,reject)=>{ resolve() }) p1.then(()=>{ console.log(123) }) </script>
通过promise构建出来的对象有三种状态,Pending(进行中),Fulfilled(已成功),Rejected(已失败)
状态只能由 Pending 变为 Fulfilled 或由 Pending 变为 Rejected ,且状态改变之后不会在发生变化,会一直保持这个状态。
通过then函数注册成功或者失败函数,promise内部调用resolve或者reject调用对应的函数。
; (function () { const status = { 0: "pending", 1: "fulfilled", 2: "rejected" } class customePromise { constructor(func) { if (typeof func != "function") { throw TypeError("Promise resolver " + func + " is not a function") } func(this._resolve.bind(this), this._reject.bind(this)); } _resolve(val) { } _reject(val) { } } window.customePromise = customePromise; })();
首先我们定义了一个customePromise的类,通过window暴露出去,通过前面promise调用我们可以看到参数是一个函数,该函数接受两个参数,resolve,reject,通过调用两个函数来执行对应的成功或者失败函数。第一步,我们在内部首先要判断传入是否为一个函数,才进行下一步操作。
; (function () { const status = { 0: "pending", 1: "fulfilled", 2: "rejected" } class customePromise { constructor(func) { this._status = status[0]; this.resolveArr = []; this.rejectArr = []; this.value=""; if(typeof func!="function"){ throw TypeError("Promise resolver "+ func+" is not a function") } try { func(this._resolve.bind(this), this._reject.bind(this)); } catch (err) { this._reject(err) } } _resolve(val) { } _reject(val) { } } window.customePromise = customePromise; })();
promise最大的好处就是他的捕获机制,一旦你在外部函数吃错都可以在内部可以捕获到,这样你外面的脚本还能继续往下执行,并通过失败函数可以打印出来。
一开始我们在构造函数开始执行的时候保存了这个对象状态为pending,然后在这个promise对象上挂载了两个属性用来保存成功和失败函数。下面我们来实现then函数
then(resolveFunc, rejectFunc) { switch (this._status) { case "pending": this.resolveArr.push(resolveFunc); this.rejectArr.push(rejectFunc); break; case "fulfilled": resolvefunc(this.value) break; case "rejected": rejectfunc(this.value) break; } }
通过判断promise对象来决定函数是否执行,如果处于pending状态,就把成功失败函数存入对应的数组,如果状态已经改变,就执行对应的函数。下面实现成功失败函数的代码。
_resolve(val) { setTimeout(() => { this.value=val; this._status = status[1]; this.resolveArr.forEach(item => { item(val); }) }, 0) } _reject(val) { setTimeout(() => { this.value=val; this._status = status[2]; this.rejectArr.forEach(item => { item(val); }) }, 0) }
注意下我们的promise属于微任务,他会让我们主线程的任务先执行,所以我们这里采用异步模拟。但这里还是存在一个问题,就是我们可以在外部多次调用,这明显是不允许,所以我们来优化下代码。
_resolve(val) { setTimeout(() => { if (this._status != status[0]) { return; } this.value=val; this._status = status[1]; this.resolveArr.forEach(item => { item(val); }) }, 0) } _reject(val) { setTimeout(() => { if (this._status != status[0]) { return; } this.value=val; this._status = status[2]; this.rejectArr.forEach(item => { item(val); }) }, 0) }
用过jquery的同学都知道jquery是可以链式调用,同样我们的promise也是可以的,那就表示我们的then函数返回值同样也是个promise对象,下面我们来改写then函数。
then(resolveFunc, rejectFunc) { let resolvefunc, rejectfunc; let _this = this; return new customePromise(function (resolve, reject) { resolvefunc = function (val) { let value = resolveFunc(val); resolve(value) } rejectfunc = function (val) { let value = rejectFunc(val); reject(value) } switch (_this._status) { case "pending": _this.resolveArr.push(resolvefunc); _this.rejectArr.push(rejectfunc); break; case "fulfilled": resolvefunc() break; case "rejected": rejectfunc break; } }) }
注意这里并不好理解,因为之前我们存入的事成功和失败函数,这里我们包装了一个函数并把then函数返回值promise对象的resolve函数也存入进去,这就表示如果我们第一个promise状态改变会接着触发第二个promise对象的执行,但需要注意如果我们函数的返回值同样是一个promise对象的话,我们必要要等待其状态改变才能触发他的下一个的then函数,这是一个难点,很不好理解,我们使用过把then函数返回的promise对象的resolve时间挂在函数内部的then上面,一旦内部函数状态改变会触发then函数的对应的事件,当然我们也是要加上容错处理,下面附上代码:
then(resolveFunc, rejectFunc) { let resolvefunc, rejectfunc; let _this = this; return new customePromise(function (resolve, reject) { resolvefunc = function (val) { try { if (typeof resolveFunc != "function") { resolve(val) } else { let value = resolveFunc(val); if (value instanceof customePromise) { value.then(resolve) } else { resolve(value) } } } catch (err) { console.log(err) reject(err) } } rejectfunc = function (val) { try { if (typeof rejectFunc != "function") { resolve(val) } else { let value = rejectFunc(val); if (value instanceof customePromise) { value.then(reject) } else { reject(value) } } } catch (err) { reject(err) } } switch (_this._status) { case "pending": _this.resolveArr.push(resolvefunc); _this.rejectArr.push(rejectfunc); break; case "fulfilled": resolvefunc() break; case "rejected": rejectfunc break; } }) }
最后说一下resolve()参数如果是一个promise对象的话,必须要等待参数的状态改变才能触发他的then函数,原理跟上面的很相似。
_resolve(val) { setTimeout(() => { if (val instanceof customePromise) { val.then(()=>{ this._status = status[1]; this.resolveArr.forEach(item => { item(val); }) }) return; } if (this._status != status[0]) { return; } this._status = status[1]; this.resolveArr.forEach(item => { item(val); }) }, 0) } _reject(val) { setTimeout(() => { if (val instanceof customePromise) { val.then(()=>{ this._status = status[2]; this.rejectArr.forEach(item => { item(val); }) }) return; } if (this._status != status[0]) { return; } this._status = status[2]; this.rejectArr.forEach(item => { item(val); }) }, 0) }
最后附上所有的代码:
; (function () { const status = { 0: "pending", 1: "fulfilled", 2: "rejected" } class customePromise { constructor(func) { this._status = status[0]; this.resolveArr = []; this.rejectArr = []; if(typeof func!="function"){ throw TypeError("Promise resolver "+ func+" is not a function") } try { func(this._resolve.bind(this), this._reject.bind(this)); } catch (err) { this._reject(err) } } _resolve(val) { setTimeout(() => { if (val instanceof customePromise) { val.then(()=>{ this._status = status[1]; this.resolveArr.forEach(item => { item(val); }) }) return; } if (this._status != status[0]) { return; } this._status = status[1]; this.resolveArr.forEach(item => { item(val); }) }, 0) } _reject(val) { setTimeout(() => { if (val instanceof customePromise) { val.then(()=>{ this._status = status[2]; this.rejectArr.forEach(item => { item(val); }) }) return; } if (this._status != status[0]) { return; } this._status = status[2]; this.rejectArr.forEach(item => { item(val); }) }, 0) } then(resolveFunc, rejectFunc) { let resolvefunc, rejectfunc; let _this = this; return new customePromise(function (resolve, reject) { resolvefunc = function (val) { try { if (typeof resolveFunc != "function") { resolve(val) } else { let value = resolveFunc(val); if (value instanceof customePromise) { value.then(resolve) } else { resolve(value) } } } catch (err) { console.log(err) reject(err) } } rejectfunc = function (val) { try { if (typeof rejectFunc != "function") { resolve(val) } else { let value = rejectFunc(val); if (value instanceof customePromise) { value.then(reject) } else { reject(value) } } } catch (err) { reject(err) } } switch (_this._status) { case "pending": _this.resolveArr.push(resolvefunc); _this.rejectArr.push(rejectfunc); break; case "fulfilled": resolvefunc() break; case "rejected": rejectfunc break; } }) } } window.customePromise = customePromise; })();