手写 Promise 实现

Promise的基本使用

Promise定义及用法详情文档:Promise MAD文档

function testPromise(param) {
return new Promise((resolve, reject) => {
setTimeout(() => {
params
? resolve('resolve:' + param)
: reject('reject:' + param)
}, 1000)
})
}

我们能够通过 .then 方法来获取执行成功或失败的结果,如:

const param = true

testPromise(param).then(res => {
// 当 param = true 时执行
console.log(res) // -> resolve: true
}, err => {
// 当 param = false 时执行
console.log(res) // -> reject: false
})
  • Promise通常用于需要异步处理,比如HTTP请求等场景
  • Promise会加入 JS 的微任务队列,故也可用于特定场景的优化处理

    关于JS微任务队列文章参考:Vue3中微任务队列

实现Promise

  • 目标1:实现简单版本的 MPromise 类

    Promise是一个类,构造函数接受一个函数,这个函数的两个参数 resolve, reject 也是函数

    Promise实际上是由三个状态来驱动的: PENDING(等待)、FULFILLED(完成)、REJECTED(拒绝)
    class MPromise{
    // 分别设置Promise的三个执行状态
    static PENDING = 'PENDING' // 等待
    static FULFILLED = 'FULFILLED' // 已完成 .then
    static REJECTED = 'REJECTED' // 已拒绝 .catch constructor(executor) {
    // 初始化状态为 PENDING
    this.status = MPromise.PENDING
    // 分别存储执行 成功 和 执行 失败的值
    this.resolveResult = undefined
    this.rejectReason = undefined
    // 存储回调函数
    // 由于同一个 Promise 的.then函数可以调用多次,这里需要使用数组来存储
    this.callback = []
    // 将 执行函数 中的 resolve与 reject 方法执行 this 绑定
    executor(this._resolve.bind(this), this._reject.bind(this))
    } then(resolveFn, rejectFn) {
    this.callback.push({
    resolveFn,
    rejectFn
    })
    } _resolve(result) {
    // 更改状态
    this.status = MPromise.FULFILLED
    // 设置 .then 参数值
    this.resolveResult = result
    // 执行回调函数
    this.callback.forEach(cb => this._handler(cb))
    } _reject(errorBody) {
    // 更改状态
    this.status = MPromise.REJECTED
    // 设置 .catch 参数值
    this.rejectReason = errorBody
    // 执行回调函数
    this.callback.forEach(cb => this._handler(cb))
    } // 根据当前的状态 执行对应的 callback 函数
    _handler(callback) {
    const { resolveFn, rejectFn } = callback if(this.status === MPromise.REJECTED && rejectFn) {
    rejectFn(this.rejectReason)
    } else if(this.status === MPromise.FULFILLED && resolveFn) {
    resolveFn(this.resolveResult)
    }
    }
    }

    我们可以先对简单版本的 MPromise 进行测试

    function testMPromise(test) {
    return new MPromise((resolve, reject) => {
    setTimeout(() => {
    test ? resolve('resolve') : reject('resolve')
    }, 500)
    })
    } const p = testMPromise(true).then(r => {
    console.log('then: ', r) // -> 在 500ms 后输出: then:resolve
    }) // 由于我们没有实现链式调用,p输出的是 undefined,所有 p.then 会抛出异常
    console.log(p)
  • 目标2:实现链式调用

    实现链式调用我们需要在调用 then 方法时返回一个新的 MPromise 对象

    然后我们需要对 _handle 函数进行改造,因为我们需要将上一次 then 函数的返回值传递下去

    我们还需要处理 then 中返回的是一个 MPromise 对象的情况

    这里不能直接将 this 返回,我们必须保证每一个 MPromise 都是独立的,不然会造成内部变量的混乱

    // 添加一个工具函数,判断是否为 MPromise 类型对象
    const isMPromise = (obj) => obj instanceof MPromise class MPromise {
    // ......
    then(resolveFn, rejectFn) {
    const newMPromiseCb = (nextResolveFn, nextRejectFn) => {
    // 我们不能在使用 this.callback.push() 的方式添加回调函数
    // 这样会导致不在同一个上下文(this)中
    // 调用处理函数 _handler,在 _handler 函数中去添加 callback
    // 这样就能保证往正确的上下文this中添加回调
    this._handler({
    resolveFn,
    rejectFn,
    nextResolveFn,
    nextRejectFn,
    })
    } // 处理链式调用的问题:
    // 创建一个 新的 MPromise 对象,并将其返回,使其能够进行链式调用
    return new MPromise(newMPromiseCb)
    } // 还需要考虑.then中返回的是一个 MPromise 对象该如何处理?
    _resolve(result) {
    if(isMPromise(result)) {
    // 若上一次处理的返回值为一个 MPromise 对象
    // 需要执行这个 MPromise
    // 将 FULFILLED 和 REJECTED 状态分别交给 this._resolve 和 _reject去执行
    result.then(
    this._resolve.bind(this),
    this._reject.bind(this)
    )
    } else {
    // 更改状态
    this.status = MPromise.FULFILLED
    // 设置 .then 参数值
    this.resolveResult = result
    // 执行回调函数
    this.callback.forEach(cb => this._handler(cb))
    }
    } _reject(errorBody) {
    // 与 _resolve 中的处理逻辑相同
    if(isMPromise(errorBody)) {
    errorBody.then(
    this._resolve.bind(this),
    this._reject.bind(this)
    )
    } else {
    // 更改状态
    this.status = MPromise.REJECTED
    // 设置 .catch 参数值
    this.rejectReason = errorBody
    // 执行回调函数
    this.callback.forEach(cb => this._handler(cb))
    }
    } _handle(callback) {
    const {
    resolveFn,
    rejectFn,
    nextResolveFn
    nextRejectFn
    } = callback // 当 MPromise 状态为 PENDING 时将其回调函数收集到 this.callback 中
    if(this.status === MPromise.PENDING) {
    this.callback.push(callback)
    return
    } if(this.status === MPromise.REJECTED && rejectFn) {
    const reason = rejectFn
    ? rejectFn(this.rejectReason)
    :this.rejectReason
    nextRejectFn(reason)
    } else if(this.status === MPromise.FULFILLED && resolveFn) {
    // 先判断是否传入了 resolveFn 回调函数
    // 存在 则需要执行该函数,并将其返回值作为 nextResolveFn 的参数传入进去
    const reason = rejectFn
    ? resolveFn(this.resolveResult)
    : this.resolveResult
    nextResolveFn(reason)
    }
    }
    }
  • 目标3:实现常用的静态方法

    .catch
    class MPromise{
    // ...
    // 添加 catch 函数
    catch(rejectFn) {
    // 我们只需要调用一下 then 方法,并将第一个参数传入 undefined 即可
    return this.then(undefined, rejectFn)
    }
    // ...
    }

    .finally

    class MPromise{
    // ...
    // 添加一个 finally 函数
    finally(fn) {
    // 这里我们只需要将传入的回调函数 fn,都当做 then 函数的参数传进去即可
    // 因为 then 函数中会根据状态至少执行其中一个函数
    this.then(fn, fn)
    }
    // ...
    }

    Promise.reject 与 Promise.resolve

    class MPromise {
    // ...
    static reject(errorBody) {
    // 其实只要注意判断传入的 参数 是否为一个 MPromise,或者是 存在 catch 属性的一个对象
    if(errorBody instanceof MPromise || (typeof errorBody === 'object' && 'catch' in errorBody)) {
    // 直接把这个对象返回就行了
    return errorBody
    } // 包装一下,返回一个状态为 REJECTED MPromise 对象即可
    return new MPromise((resolve, reject) => {
    reject(errorBody)
    })
    } // resolve 与 reject 方法一致
    static resolve(resolveValue) {
    if(errorBody instanceof MPromise || (typeof errorBody === 'object' && 'then' in resolveValue)) {
    return resolveValue
    }
    return new MPromise(resolve => resolve(resolveValue))
    }
    // ...
    }

    Promise.all

    class MPromise {
    // ...
    // iterables: 为数组类型
    static all(iterables) {
    const res = []
    return new MPromise((resolve, reject) => {
    iterables.forEach((c, index) => {
    // MPromise.resolve 转换为 MPPromise 对象
    MPromise.resolve(c).then(
    (res) => {
    // 收集结果
    res.push(res) if(index >= iterables.length - 1) {
    resolve(res)
    }
    },
    // 若执行到 reject 则会直接停止调用并且返回当次执行失败的原因
    reject
    )
    })
    })
    }
    // ...
    }

完整代码

1.手写Promise - 实现一个基础的Promise

2.手把手教你实现 Promise

3.简易版本没有对异常等逻辑进行处理

const isMPromise = (obj) => obj instanceof MPromise

class MPromise{
static PENDING = 'PENDING'
static FULFILLED = 'FULFILLED'
static REJECTED = 'REJECTED' constructor(executor) {
// 初始化状态为 PENDING
this.status = MPromise.PENDING
// 分别存储执行 成功 和 执行 失败的值
this.resolveResult = undefined
this.rejectReason = undefined
// 存储回调函数
this.callback = []
// 将 执行函数 中的 resolve与 reject 方法执行 this 绑定
executor(this._resolve.bind(this), this._reject.bind(this))
} then(resolveFn, rejectFn) {
// 实现链式调用,返回一个新的 MPromise 对象
const _promise = new MPromise((nextResolve, nextReject) => {
this._handler({
resolveFn,
rejectFn,
nextReject,
nextResolve
})
})
return _promise
} catch(rejectFn) {
return this.then(undefined, rejectFn)
} // 添加一个 finally 函数
finally(fn) {
// 这里我们只需要将传入的回调函数 fn,都当做 then 函数的参数传进去即可
// 因为 then 函数中会根据状态至少执行其中一个函数
this.then(fn, fn)
} // iterables: 为数组类型
static all(iterables) {
const res = []
return new MPromise((resolve, reject) => {
iterables.forEach((c, index) => {
// MPromise.resolve 转换为 MPPromise 对象
MPromise.resolve(c).then(
(res) => {
// 收集结果
res.push(res) if(index >= iterables.length - 1) {
resolve(res)
}
},
// 若执行到 reject 则会直接停止调用并且返回当次执行失败的原因
reject
)
})
})
} static reject(errorBody) {
if(errorBody instanceof MPromise || (typeof errorBody === 'object' && 'catch' in errorBody)) {
return errorBody
}
return new MPromise((resolve, reject) => {
reject(errorBody)
})
} static resolve(resolveValue) {
if(errorBody instanceof MPromise || (typeof errorBody === 'object' && 'then' in resolveValue)) {
return resolveValue
}
return new MPromise(resolve => resolve(resolveValue))
} _resolve(result) {
if(isMPromise(result)) {
result.then(
this._resolve.bind(this),
this._reject.bind(this)
)
} else {
this.status = MPromise.FULFILLED
this.resolveResult = result
// console.log('callback', this.callback)
this.callback.forEach(cb => this._handler(cb))
}
} _reject(errorBody) {
if(isMPromise(errorBody)) {
result.then(
this._resolve.bind(this),
this._reject.bind(this)
)
} else {
this.status = MPromise.REJECTED
this.rejectReason = errorBody this.callback.forEach(cb => this._handler(cb))
}
} _handler(callback) {
if(this.status === MPromise.PENDING) {
this.callback.push(callback)
return
} const {
resolveFn,
rejectFn,
nextReject,
nextResolve
} = callback if(this.status === MPromise.REJECTED && rejectFn) {
const _reason = rejectFn ? rejectFn(this.rejectReason) : this.rejectReason
nextReject(_reason)
}else if(this.status === MPromise.FULFILLED && resolveFn) { const _reason = resolveFn ? resolveFn(this.resolveResult) : this.resolveResult
// 将上一个 then 中的返回值作为下次 then 的参数传入
nextResolve(_reason)
}
}
}

--你我本是平凡人,平凡有多烦--

JavaScript之Promise实现原理(手写简易版本 MPromise)的更多相关文章

  1. JDK动态代理深入理解分析并手写简易JDK动态代理(下)

    原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2019-01-05/27.html 作者:夜月归途 出处:http://www.guitu ...

  2. JDK动态代理深入理解分析并手写简易JDK动态代理(上)

    原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2019-01-03/27.html 作者:夜月归途 出处:http://www.guitu ...

  3. 【教程】手写简易web服务器

    package com.littlepage.testjdbc; import java.io.BufferedReader; import java.io.FileReader; import ja ...

  4. 手写简易SpringMVC

    手写简易SpringMVC 手写系列框架代码基于普通Maven构建,因此在手写SpringMVC的过程中,需要手动的集成Tomcat容器 必备知识: Servlet相关理解和使用,Maven,Java ...

  5. 手写简易的Mybatis

    手写简易的Mybatis 此篇文章用来记录今天花个五个小时写出来的简易版mybatis,主要实现了基于注解方式的增删查改,目前支持List,Object类型的查找,参数都是基于Map集合的,可以先看一 ...

  6. Javascript之我也来手写一下Promise

    Promise太重要了,可以说是改变了JavaScript开发体验重要内容之一.而Promise也可以说是现代Javascript中极为重要的核心概念,所以理解Promise/A+规范,理解Promi ...

  7. Java多线程之Executor框架和手写简易的线程池

    目录 Java多线程之一线程及其基本使用 Java多线程之二(Synchronized) Java多线程之三volatile与等待通知机制示例 线程池 什么是线程池 线程池一种线程使用模式,线程池会维 ...

  8. 手写简易WEB服务器

    今天我们来写一个类似于Tomcat的简易服务器.可供大家深入理解一下tomcat的工作原理,本文仅供新手参考,请各位大神指正!首先我们要准备的知识是: Socket编程 HTML HTTP协议 服务器 ...

  9. 手写简易版RPC框架基于Socket

    什么是RPC框架? RPC就是远程调用过程,实现各个服务间的通信,像调用本地服务一样. RPC有什么优点? - 提高服务的拓展性,解耦.- 开发人员可以针对模块开发,互不影响.- 提升系统的可维护性及 ...

随机推荐

  1. 【LeetCode】554. Brick Wall 解题报告(Python)

    [LeetCode]554. Brick Wall 解题报告(Python) 标签(空格分隔): LeetCode 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fux ...

  2. Lucky7(hdu5768)

    Lucky7 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Subm ...

  3. 【C++】关于new分配空间

    1如果不使用new,则在函数结束时内存被回收,指针变成野指针 #include <iostream> using namespace std; struct Node { int val; ...

  4. Adversarial Defense by Restricting the Hidden Space of Deep Neural Networks

    目录 概 主要内容 Mustafa A., Khan S., Hayat M., Goecke R., Shen J., Shao L., Adversarial Defense by Restric ...

  5. windows环境下elasticsearch安装教程(超详细)

    一.安装jdk ElasticSearch是基于lucence开发的,也就是运行需要java jdk支持.所以要先安装JAVA环境. 由于ElasticSearch 5.x 往后依赖于JDK 1.8的 ...

  6. gRPC创建Java RPC服务

    1.说明 本文介绍使用gRPC创建Java版本的RPC服务, 包括通过.proto文件生成Java代码的方法, 以及服务端和客户端代码使用示例. 2.创建生成代码工程 创建Maven工程,grpc-c ...

  7. openmesh - src - trimesh delete and add elements

    openmesh - src - trimesh delete and add elements openmesh 版本 8.1 About 本文主要介绍openmesh的如下接口 add_verte ...

  8. Docker下安装Nacos

    1:使用docker获取nacos服务镜像 docker pull nacos/nacos-server(不加版本号表示获取最新版本) 2:查看是否成功下载nacos镜像 docker images ...

  9. Linux_yum安装时报404错误

    使用yum安装报错如下: [root@localhost ~]# yum install gcc 已加载插件:fastestmirror Loading mirror speeds from cach ...

  10. centos7 date时间命令

    date "+%F %T" %F     full date; same as %Y-%m-%d  --相当于年月日格式 %T     time; same as %H:%M:%S ...