简单实现Promise

简单实现Promise

2019年08月18日 17:24:12 yhy1315 阅读数 3 标签: Promise实现JavaScript 更多

个人分类: JavaScript

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/yhy1315/article/details/99707014

简单实现Promise

Promise在我们平时写js中经常用到,(如果你没有经常用到,也许你该好好学习一下ES6了!)但只有知道其内部实现原理才能更好的使用它,所以我们今天来一起深入学习Promise!

了解Promise

学习一个东西,最好是从它的历史学起,为什么它会出现?为什么它更好?为什么它这样设计?保留这些问题,我们将一一解释。

不够优雅的callback

基于单线程js的开发都快把人搞秃了,想象一种情形:用户点击提交你页面表单,为了让用户有更好的体验,我们不跳转式提交表单,那么我们就要在JavaScript线程中提交。但是我们很穷,服务器的带宽只有1Mbps,这会导致什么?JavaScript在提交表单后苦苦等待服务器响应,但是由于我们网速太慢,无法及时获得相应,用户也不耐烦在页面瞎点(永远不要信任用户!),用户会发现所有页面相应都无法获得(主线程在处理相应!),那他就会放弃这家网站!

没错,单线程就会出现这些事情,后来聪明的工程师想出了一个办法,通过事件机制来回调异步操作!那么,上面情况就轻而易举的解决了!我们只需要监听ajax的onMessage方法,并在方法中调用我们的callback函数即可!

回调虽然很好,但是有一个比较严重的问题!看下面例子。

ajax.onMessage(function(msg){

let ajax2 = Ajax.get("www.domain/controller.com")

ajax2.onMessage(function(msg){

let ajax3 = Ajax.get(".....")

ajax3.onMessage(function(msg){

//....

})

})})

哦!回调越来越多,我们好像无法掌握这串代码了!这就是回调不足之处!回调地狱。

拯救世界的Promise

那么有没有一种方法,能让我们想同步一样解决异步问题呢?聪明的工程师又想到一种方法,我们应该将这些回调封装成一个函数,供我们随时使用,而且为了解决嵌套的可读性差,我们应该使用链式调用(相信我,链式真的比嵌套好)!

来看一下Promise的使用:

//Ajax全为抽象ajax操作,不要将它带入任何实际操作!//我们封装了一个ajax在Promise中的操作let ajaxToPromise = url=>{

return new Promise((resolve,reject)=>{

//为了方便在本地测试,我们不用ajax请求,我们改用setTimeOut方法,相信聪明的你一定能理解

setTimeout(()=>{

resolve()

console.log("延迟 " + url + " ms的操作被完成!")

},url);

})}//封装完成 我们看看使用时能为我们提供什么便利let url1,url2,url3;ajaxToPromise(1000).then(()=>{return ajaxToPromise(1000)}).then(()=>{return ajaxToPromise(1000)})

如果你运行,你会发现每隔1000ms就会有输出。

很清楚,我们通过我们封装的ajaxToPromise方法,一行解决了异步请求多次问题!接下来,我们将会知道Promise究竟在做什么?

new Promise( function(resolve, reject) {...} /* executor */ );

简单理解下:Promise构造函数仅仅接受函数作为参数,并在构造方法中立即调用该函数。以后Promise会作为该函数的代理,在该函数改变Promise状态值时,可以使用then()方法执行后续操作。

Promise的内部实现

如果你只对Promise的运行与使用感兴趣你可以跳过这部分,但是我会推荐任何人读它。

Promise内部使用回调函数调用then()方法注册的回调,在什么时候用?我们显式调用resolve()就会更改Promise状态,相应调用在then()中注册的回调。

如果你看不太懂,也没有关系,毕竟语言是很抽象的,我们直接上代码,不用担心,我们不会直接写出Promise全部代码,一点一点来才利于学习。

构造函数实现

希望大家都喜欢ES6的语法!(笑

class MyPromise{

/**

* 构造函数 立即执行callback函数

* @param {Function} callback

*/

constructor(callback) {

if(this.isFunction(callback)){

callback()

}else{

throw new Error("MyPromise 必须接受一个函数参数!")

}

}

/**

* 判断func是否为Function对象

* @param {Function} func

*/

isFunction(func){

return typeof func == "function"

}}

构造方法已经完成,但是我们注意力应该放在then()方法和resolve()方法。下面我们先考虑then()方法的实现。

then()方法实现

then()方法应该去注册执行回调函数,而本身不执行,选择让用户在callback方法中执行是更好的选择。那么我们注册的方法应该存放一下,选择队列存放吧!我们维护两个队列onFulfilledQueue与onRejectedQueue,我们创建一个方法initData(),其中初始化我们所有的成员变量。

class MyPromise{

constructor(callback) {

if(this.isFunction(callback)){

this.initData()//初始化变量

callback()

}else{

throw new Error("MyPromise 必须接受一个函数参数!")

}

}

//...上面代码这里省略

/**

* 初始化MyPromise所谓成员变量

*/

initData(){

this.callback = null;//用户传入构造的callback

this.onFulfilledQueue = []//注册的成功回调

this.onRejectedQueue = []//注册的失败回调

this.value = null;//要返回的成功值

this.error = null;//要返回的失败值

}}

then()方法,接受最多两个参数onFulfilled,onRejected分别应当在状态是成功与失败时调用,那么我们先引入状态!

// 定义Promise的三种状态常量// 把下面代码加入你的initData()中this.PENDING = 0//进行中this.FULFILLED = 1//已完成this.REJECTED = -1//以失败this.status = this.PENDING//现在状态 默认为进行中

ok,到这一步,我们封装了成员变量初始化,为我们的Mypromise引入了状态机制,then()的雏形可以写出来了。

/**

* then方法最多接受两个函数类型参数

* 如果此时MyPromise状态为完成将会执行onFulfilled

* 若为错误执行onRejected

* @param {Function} onFulfilled

* @param {Function} onRejected

*/

then(onFulfilled, onRejected) {

if(this.status == this.PENDING){//未完成时 加入队列

if(this.isFunction(onFulfilled))

this.onFulfilledQueue.push(onFulfilled)

if(this.isFunction(onRejected))

this.onRejectedQueue.push(onRejected)

}else if(this.status == this.FULFILLED&& this.isFunction(onFulfilled)){

onFulfilled(this.value)//如果这时候状态改变为已完成 我们直接调用onFulfilled

}else if(this.status==this.REJECTED&&this.isFunction(onRejected)){

onRejected(this.error)//这里同上面

}

}

基本雏形出来了,但是我们还要考虑,因为then()要支持链式调用,所以我们应该在最后返回MyPromise的一个实例。

但是又出现了新的问题,我们返回的MyPromise实例什么时候改变状态?我们应该依赖于上个MyPromise的then()方法的返回值,所以我们要大改代码

继续完善代码

then(onFulfilled, onRejected) {

return new MyPromise((onFulfilledNext, onRejectedNext) => {

let fulfilled = value => {

try {

if (!this.isFunction(onFulfilled)) {

onFulfilledNext(value)

} else {

let res = onFulfilled(value);

if (res instanceof MyPromise) {

res.then(onFulfilledNext, onRejectedNext);

} else {

onFulfilledNext(res)

}

}

} catch (error) {

onRejectedNext(error)

}

}

let rejected = error => {

try {

if (!this.isFunction(onRejected)) {

onRejectedNext(error)

} else {

let res = onRejected(error);

if (res instanceof MyPromise) {

// 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调

res.then(onFulfilledNext, onRejectedNext)

} else {

//否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数

onFulfilledNext(res)

}

}

} catch (err) {

// 如果函数执行出错,新的Promise对象的状态为失败

onRejectedNext(err)

}

}

switch (this.status) {

// 当状态为pending时,将then方法回调函数假如执行队列等待执行

case this.PENDING:

this.onFulfilledQueue.push(fulfilled)

this.onRejectedQueue.push(rejected)

break

// 当状态已经改变时,立即执行对应的回调函数

case this.FULFILLED:

fulfilled(this.value)

break

case this.REJECTED:

rejected(this.error)

break

}

});

}

现在代码很完美,我们then中返回的MyPromise依赖于上个then的调用及返回值了。

实现_resolve与_reject方法

这一节就比较简单,我们要干的事情,就是判断状态,并全部调用队列中的函数。

_resolve(value) {

if (this.status != this.PENDING) return;

this.status = this.FULFILLED;

this.value = value;

const run = () => {

while (this.onFulfilledQueue.length != 0) {

let cb = this.onFulfilledQueue.shift()

cb(this.value)

}

}

setTimeout(run, 0//要在MyPromise中支持同步操作 也就是resolve()之后操作先于then()中操作

//不太懂的同学可以看看我的有一篇详细讲解js中的event loop

}

_reject(error) {

if (this.status != this.PENDING) return

this.status = this.REJECTED

this.error = error

const run = () => {

while (this.onRejectedQueue.length != 0) {

let cb = this.onRejectedQueue.shift()

cb(this.error)

}

}

setTimeout(run, 0)

}

catch实现

catch(onRejected){

console.log(onRejected)

return this.then(null,onRejected)

}

catch()方法本身只是then()方法的一种调用。

结尾

脑瘫码农 纯靠自学 如有错误 望请指正 共同学习 不胜感激

参考

Promise-MDN

一步步实现Promise代码封装

Promise实现原理(附源码)

原文地址:https://www.cnblogs.com/grj001/p/12224360.html