JavaScript之实现一个ES6Promise
说到 es6,promise 是绕不过的问题;如果说 es6 的 class 是基于 javascript 原型继承的封装,那么 promise 则是对 callback 回调机制的改进。这篇文章,不谈 promise 的实际应用;聊一下 promise 的实现原理,从最简单的解决方案入手,一步一步的自己实现一个 simplepromise。
正文
从最简单的 promise 初始化和使用入手:
const pro = new promise ((res, rej) => {}) pro.then(data => {}, err => {})
promise 的构造函数如上,需要传递一个函数作为参数,这个函数有两个变量: resolve, reject。而 promise 有不同的执行状态,分三种情况:resolve, reject, pending。根据以上的信息,写出最基本的 simplepromise 的类结构:
class simplepromise{ constructor(handler){ this._status = "pending" handler(this._resolve.bind(this), this._reject.bind(this))//参数函数的作用域指向class } _resolve(){} _reject(){} }
接下来思考一下_resolve 与_reject两个函数的作用。我们知道,promise 根据 then 方法来执行回调,而 then 是根据状态判断要执行的回调函数。不难推导出,_resolve 与_reject正是根据handler的执行来进行状态变更的,而状态只能由pending向reslove或rejected转换,所以有:
class simplepromise{ constructor(handler){ ... } _resolve(val){//异步返回的数据 if(this._status === "pending"){//保证状态的不可逆性 this._status = "resolved" this._value = val } } _reject(val){ if(this._status === "pending"){ this._status = "rejected" this._value = val } } }
then的调用逻辑
下面分析 then 函数的逻辑,从调用入手:
pro.then(data => {}, err => {})
then 接收两个参数,第一个是执行成功调用的函数,第二个是执行失败调用的函数。
class simplepromise{ constructor(handler){ ... } _resolve(val){ ... } _reject(val){ ... } then(success, fail){ switch (this._status){ case "pending": break; case "resolved": success(this._value) break; case "rejected": fail(this._value) break; } } }
以上实现了最简单的一个 promise
测试代码:
const pro = new simplepromise(function(res, rej) { let random = math.random() * 10 if(random > 5){ res("success") } else{ rej("fail") } }) pro.then(function(data) { console.log(data) }, function(err) { console.log(err) })
当然,这不能算是一个 promise,目前仅仅实现了根据状态调用不同的回调函数。还没有实现异步。
那如何实现异步呢?关键在于 then 函数,当判断_status为pending时,如何延后调用 success与fail函数,等待状态改变后再调用?
支持异步
这里采用数组来存储 fail 与 success 函数:
class simplepromise{ constructor(handler){ this.status = "pending" this._onsuccess = []//存储fail 与 success 函数 this._onfail = [] handler(this._resolve.bind(this), this._reject.bind(this)) } _resolve(val){ if(this.status === "pending"){ ... let temp while(this._onsuccess.length > 0){//依次执行onsuccess中的回调函数 temp = this._onsuccess.shift() temp(val) } } } _reject(val){ if(this.status === "pending"){ ... let temp while(this._onfail.length > 0){ temp = this._onfail.shift() temp(val) } } } then (success, fail){ switch (this.status){ case "pending": this._onsuccess.push(success) this._onfail.push(fail) break; ... } } }
使用 onsuccess 和 onfail 来存储回调函数,当处理状态为 pending 时,将回调函数 push 到相应的数组里,当状态变更后,依次执行数组里的回调函数。
测试代码:
const pro = new simplepromise(function(res, rej) { settimeout(function(){ let random = math.random() * 10 if(random > 5){ res("success") } else{ rej("fail") } }, 2000) }) pro.then(function(data) { console.log(data) }, function(err) { console.log(err) })
两秒后,会执行相应的回调。
到目前为止,最最最简单的一个 promise 骨架已经基本完成了。但是还有很多功能待完成。现在可以稍微休息一下,喝个咖啡打个鸡血,回来我们会继续让这个 promise 骨架更加丰满起来。
. . . . . .
完善promise
欢迎回来,下面我们继续完善我们的 promise。
上面完成了一个最基础的 promise,然而还远远不够。首先,promise 需要实现链式调用,其次 promise 还需要实现 all race resolve reject 等静态函数。
首先,如何实现 then 的链式调用呢?需要 then 返回的也是一个 promise。
于是有
class simplepromise{ ... then(success, fail){ return new simplepromise((nextsuccess, nextfail) => { const onfullfil = function(val){ const res = success(val) nextsuccess(res) } const onreject = function(val){ const res = fail(val) nextsuccess(res) ; } switch (this._status){ case "pending": this._onsuccess.push(onfullfil) this._onfail.push(onreject) break; case "resolved": onfullfil(this._value) break; case "rejected": onreject(this._value) break; } }) } }
测试代码:
const sp = new simplepromise(function (res, rej){ settimeout(function(){ let random = math.random() * 10 random > 5 res(random) : rej(random) }, 1000) }) sp.then(data => { console.log("more than 5 " + data) return data }, err =>{ console.log("less than 5 " + err) return err }).then((data) => { console.log(data) })
then的参数限制
完成了链式调用,then 方法还有许多其他限制:
下面思考 以下问题:代码中四个使用 promise 的语句之间的不同点在哪儿?
假设 dosomething 也 dosomethingelse 都返回 promise
dosomething().then(function () { return dosomethingelse(); }).then(finalhandler); dosomething().then(function () { dosomethingelse(); }).then(finalhandler);; dosomething().then(dosomethingelse()).then(finalhandler);; dosomething().then(dosomethingelse).then(finalhandler);;
答案 一会儿再揭晓,我们先来梳理以下then 方法对传入不同类型参数的处理机制:
直接上代码:
class simplepromise{ ... then(success, fail){ return new simplepromise((nextsuccess, nextfail) => { const onfullfil = function(val){ if(typeof success !== "function"){ nextsuccess(val) } else{ const res = success(val)//success 的返回值 if(res instanceof simplepromise){//如果success 返回一个promise 对象 res.then(nextsuccess, nextfail) } else{ nextsuccess(res) } } } if(fail){ const onreject = function(val){ if(typeof fail !== "function"){ nextsuccess(val) } else{ const res = fail(val) if(res instanceof simplepromise){ res.then(nextsuccess, nextfail) } else{ nextsuccess(res) } } } } else{ onreject = function(){} } switch (this._status){ case "pending": this._onsuccess.push(onfullfil) this._onfail.push(onreject) break; case "resolved": onfullfil(this._value) break; case "rejected": onreject(this._value) break; } }) } }
对于传入 then 方法的参数,首先判断其是否为 function,判断为否,直接执行 下一个 then 的 success 函数;判断为是,接着判断函数的返回值 res 类型是否为 promise,如果为否,直接执行下一个 then 的 success 函数,如果为是,通过 then 调用接下来的函数。
所以,上面的问题就不难得到答案了。
dosomething().then(function () { return dosomethingelse();//返回值为promise }).then(finalhandler); return: dosomething --->dosomethingelse(undefined) ---> final(dosomethingelseresult)
dosomething().then(function () { dosomethingelse();//返回值为 undefined }).then(finalhandler); return: dosomething --->dosomethingelse(undefined) ---> final(undefined)
dosomething().then(dosomethingelse())//参数 typeof != function .then(finalhandler); return: dosomething dosomethingelse(undefined) ---> final(dosomethingresult)
dosomething().then(dosomethingelse)//与1的调用方式是不同的 .then(finalhandler); return: dosomething --->dosomethingelse(dosomethingresult) ---> final(dosomethingelseresult)
好,then 方法已经完善好了。
静态函数
接下来是 promise 的各种静态函数
class simplepromise(){ ... static all(){} static race(){} static resolve(){} static reject(){} }
all
static all(promiselist){ if(array.isarray(promiselist)){ const len = promiselist.length; const count = 0 const arr = [] return new simplepromise((res, rej) => { for(let i = 0; i { arr[i] = data count ++ if(count === len){//每一个promise都执行完毕后返回 res(arr) } }, err => { rej(err) }) } }) } }
race
static race(promiselist){ if(array.isarray(promiselist)){ const len = promiselist.length return new simplepromise((res, rej) =>{ promiselist.foreach(item =>{ this.resolve(item).then(data => { res(data) }, err =>{ rej(err) }) }) }) } }
resolve
static resolve(obj){ if(obj instanceof simplepromise){ return obj } else { return new simplepromise((res) =>{ res(obj) }) } }
reject
static reject(obj){ if(obj instanceof simplepromise){ return obj } else { return new simplepromise((res, rej) =>{ rej(obj) }) } }
总结
现在,一个完整的 promise 对象就完成了。现在来总结一下 callback 回调和 promise 的异同吧。
其实,不管是 callback 还是 promise,这二者都是将需要滞后执行方法而提前声明的方式,只不过 callback 的处理方式比较粗犷,将 cb 函数放到异步执行的结尾;而 promise 优于 cb 的是通过定义了不同的执行状态,更加细致的进行结果处理,提供了很好的 catch 机制,这是其一;其二,then 的链式调用解决了 cb 的回调地狱;但是 then 的链式调用也不是很好的解决方案,如果封装不好,then里面套用大量的代码的话也会引起代码的不美观和上的困难,这一方面的终极解决方法还是 es7 的 async/await。
推荐阅读
-
JavaScript之实现一个ES6Promise
-
javascript实现的距离现在多长时间后的一个格式化的日期
-
JavaScript 网页中实现一个计算当年还剩多少时间的倒数计时程序
-
2017-12-02 编程语言试验之Antlr4+JavaScript实现"圈4"
-
JavaScript调试之console.log调试的一个小技巧分享
-
JavaScript实现一个带AI的井字棋游戏源码
-
JavaScript设计模式之观察者模式(发布订阅模式)原理与实现方法示例
-
mpvue小程序开发之 实现一个弹幕评论
-
JavaScript框架封装之JavaScript中的文本字符串的转义和反转义的实现讲解
-
Javascript特效实现之倒计时按钮代码教程