promise详解 : 实现promise(附实现代码)
promise then 的特点 :
- then 函数的返回值是一个 promise, 可以继续调用 then 函数
- 回调函数 resolve 和 reject 的参数 value /reason, 可以传递给 then函数的回调函数, 最终 resolve(res) 的res 传递给了 then(onFulfilled(v)=>{},onrejected(e)=>{}) 中的 v, 而 reject(err) 中的 err 传递给了其中的 e
then 函数的回调函数的返回值可以分成 :
- 普通值(字符串、对象、函数、undefined等), 只要不是 promise 或者 错误, 都可以看做是普通值
- Promise 的实例
- 错误
根据 then 函数的回调函数的返回值, 确定下一次链式调用的方式
- then 函数的回调函数的返回值 : 即 onFulfilled 函数的返回值 , 或者 onRejected 的函数的返回值
1. then 函数的回调函数的返回值是普通值
- then 函数的返回值可以是 onFulFilled 函数的返回值, 也可以是 onRejected 函数的返回值, 若是不用 return 指定返回值, 则默认返回undefined。 如果该返回值是普通的值, 则下一次链式调用(即调用then)进入 onFulfilled 回调函数, 并且将该返回值作为 onFulfilled 回调函数的参数
const p = new Promise((resolve, reject)=> {
setTimeout(()=> {
resolve('ok1')
},1000)
})
p.then(res=> {
return 'ok2'
}).then(res=> {
console.log(res)
}).then(res=> {
console.log(res)
})
// 输出:
// ok2
// undefined
- 对于 onReject 返回普通类型的值, 也按照上述规则:
const p = new Promise((resolve, reject)=> {
setTimeout(()=> {
reject('not ok1')
},1000)
})
p.then(res=> {
return 'ok2'
},(e)=> {
return 'not ok2'
}).then(res=> {
console.log(res)
},(e)=> {
console.log(e)
}).then(res=> {
console.log(res)
})
// 输出 :
// not ok2
// undefined
- 在 onRejected 回调函数中不用return指定返回值, 在下一次链式调用的时候, 也会 进入 resolve 函数, 只有发成错误才可以进入下个链式调用的 Onrejected 回调函数:
const p = new Promise((resolve, reject)=> {
setTimeout(()=> {
reject('not ok1')
},1000)
})
p.then(res=> {
return 'ok2'
},(e)=> {
console.log(e)
}).then(res=> {
console.log(res)
},(e)=> {
console.log(e)
}).then(res=> {
throw new Error('Error')
}, e=> {
console.log(e)
}).then(res=> {
console.log(res)
},e=> {
console.log(e)
})
// not ok1
// undefined
// Error: Error
2. then 函数的回调函数的返回值是 Promise 的实例, 即一个新的 promise
- 下一次链式调用会采用这个被返回的 promise 的状态, 如果这个 promise 的状态是 fulfilled 的则, 下次链式调用进入 onFulfilled 函数, 如果这个promise 的状态是 rejected, 则下次链式调用进入 onRejected 函数
const p = new Promise((resolve, reject)=> {
setTimeout(()=> {
resolve('ok1')
},1000)
})
p.then(res=> {
return new Promise((resolve, reject)=> {
console.log(res)
resolve('ok2')
})
},(e)=> {
console.log(e, 'error1')
}).then((res)=> {
console.log(res)
return new Promise((reolve,reject)=> {
reject('notok1')
})
},e=> {
console.log(e, 'error2')
}).then(res=> {
console.log(res)
}, e=> {
console.log(e, 'error3')
})
// ok1
// ok2
// notok1 error3
3. then 函数的回调函数中发生错误
如果错误没有被就近处理, 则会沿着链式调用的链条一直向下传递, 直到找到一个错误处理的 onRejected 回调函数, 再根据 onRejected 的返回值确定后边的链式调用的执行情况。 跑错之后, onRejected 回调函数之前的所有代码, 都无法被执行, 参考下例:
const p = new Promise((resolve, reject)=> {
setTimeout(()=> {
reject('notok1')
},1000)
})
p.then(res=> {
console.log(res)
}).then(res=> [
console.log(res)
]).then(res=> {
console.log(res)
},e=> {
console.log(e)
return 'caught error'
}).then(res=>{
console.log('success :' , res)
},e=>console.log(e))
// 输出
// notok1
// success : caught error
实现代码如下
// promise 的三种状态, 等待、完成(成功)、拒绝(失败)
// 也可以根据promise的状态划分成 已决和未决两种状态, 已决 : FULFILLED、REJECTED, 未决: PENDING
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
const isPromise = p => typeof p.then === "function"
// 解析 x 与 promise2 , 最终确定 promise2 的状态
const resolvePromise = (promise2, x, resolve, reject) => {
if (promise2 === x) return reject(new TypeError('循环调用了同一个未决的 promise'))
// 定义一个哨兵变量, 防止 promise 的状态在已决后发生改变
let isCalled
if (typeof x === 'object' && x !== null || typeof x === 'function') {
try {
// then = x.then 将初次调用 x.then 时,取到的值缓存
// 而多次调用 x.then 可能发生属性读取错误( 虽然一般不会)
// 所以此处直接调用 then.call, 而不是再次去读取 x.then
const then = x.then
// 可以判断 x 为一个promise, 即 resolve(new Promise()) 这种情况
if ('function' === typeof then) {
// x 是一个 promise, promise2 的状态最终取决有 x 这个 promise 的状态
// 所以要调用属于 x 的 then 函数, 才会进入 onFulfilled 或者 onRjected, 得到下一次链式调用中的回调函数的返回值
then.call(x, y => {
if (isCalled) return
isCalled = true
resolvePromise(promise2, y, resolve, reject)
}, e => {
if (isCalled) return
isCalled = true
reject(e)
})
} else {
// x 不是一个promise , 可能是类似这种有 then 属性的普通对象 x = {then : '100'}
// 直接 resolve, 将状态改成 FULFILLED
resolve(x)
}
} catch (error) {
if (isCalled) return
isCalled = true
reject(error)
}
} else {
// 排除掉 x 为一个 promise 的可能性, 直接resolve(x)
resolve(x)
}
}
// Promise 类
class Promise {
constructor(executor) {
// promise 初始状态为 PENDING
this.status = PENDING
this.value = null
this.reason = null
this.onFulfilledCallbacks = []
this.onRejectedCallbacks = []
const resolve = (value) => {
// 递归解析 resolve 的参数, 直到参数不为 promise 的实例
if(value instanceof Promise) {
return value.then(resolve,reject)
}
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
this.onFulfilledCallbacks.forEach(fn => fn())
}
}
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED
this.reason = reason
this.onRejectedCallbacks.forEach(fn => fn())
}
}
// 立即执行 executor, 捕获错误后直接变成拒绝态
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
static resolve (value) {
return new Promise(resolve => resolve(value))
}
static reject(reason) {
return new Promise((resolve,reject)=> reject(reason))
}
static all(promises) {
return new Promise((resolve, reject)=> {
let arr = [], index = 0
const processData = (i, data)=> {
arr[i] = data
if(++index >= promises.length) {
resolve(arr)
}
}
for(let i =0 ; i< promises.length; i++) {
if(isPromise(promises[i])) {
promises[i].then(data=> {
processData(i,data)
},reason=> {
reject(reason)
})
}else {
processData(i, promises[i])
}
}
})
}
static race(promises) {
return new Promise((resolve, reject)=> {
for(let i = 0; i < promises.length; i++) {
if(isPromise(promises[i])) {
promises[i].then(resolve,reject)
}else {
resolve(promises[i])
}
}
})
}
// then 函数返回一个新的 Promise 实例, 实现链式调用
// 这个新的 promise 会采用当前 promise 的状态
then(onFulfilled, onRejected) {
// 如果 onFulfilled 不是一个函数, 则将 resolve 的参数传递给下个then 函数的 onFulfilled 作为参数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
// 如果 onRjected 不是一个函数, 则将错误抛出, 传递到下一个 then 函数的 onRjected 作为参数
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }
const _this = this
const promise2 = new Promise((resolve, reject) => {
// 如果当前实例状态是 FULFILLED, 则调用成功的回调函数, 即 then(onFulfilled, onRejected) 中的 onFulfilled
// 然后将 onFulfilled 的返回值, 继续用 resolvePromise 解析,直到解析到一个不为 promise 的普通值, 或者解析出错
const getFulfilled = () => {
const x = onFulfilled(_this.value)
resolvePromise(promise2, x, resolve, reject)
}
const getRejected = () => {
const x = onRejected(_this.reason)
resolvePromise(promise2, x, resolve, reject)
}
// 此处设置 promise2 的状态, 由于此段代码包含在 promise2 实例化的过程中,因此, 无法获取 promise2 的实例
// 故而将 resolvePromise 函数 (解析 promise2 和 当前实例的 then 函数的回调函数的返回值 )放在一个延时定时器中
// 使得执行 resolvePromise 的任务放在任务队列的最后, 从而确保 先实例化 promise2 成功
// 然后根据当前实例的状态, 得到当前实例回调函数的返回值 x , 并将 x 传入 resolvePromise 进行统一解析处理
const getAsyncTaskResult = f => {
setTimeout(() => {
try {
f()
} catch (error) {
reject(error)
}
}, 0);
}
// 根据当前实例的状态制定不同的策略 :
// 1. 当前实例为 FULFILLED 状态时,则直接调用 onFulfilled , 并将结果交由 resolvePromise 处理
// 2. 当前实例为 REJECTED 状态时,则直接调用 onRejected , 并将结果交由 resolvePromise 处理
// 3.1 当前实例为 PENDING 状态时, 则将 onRjected 和 onFulfilled 的调用缓存在一个任务队列中
// 3.2 然后等待当前实例成功(resolve)或者失败(reject)
// 3.3 由于在构造函数中订阅了 resolve 和 reject, 所以当前实例一旦发布状态, 对应的任务队列将会按照序被调用
const stratagies = {
PENDING() {
_this.onFulfilledCallbacks.push(() => getAsyncTaskResult(getFulfilled))
_this.onRejectedCallbacks.push(() => getAsyncTaskResult(getRejected))
},
FULFILLED() {
getAsyncTaskResult(getFulfilled)
},
REJECTED() {
getAsyncTaskResult(getRejected)
},
}
Reflect.has(stratagies, this.status) && stratagies[this.status]()
})
return promise2
}
// catch 是一个只有失败回调的then函数
catch(errorCallback) {
this.then(null, errorCallback)
}
// finally
// 不管上一个 promise 成功还是失败, finally 的回调函数都会执行
// 不改变 then 函数两个回调函数的参数, 仍然用的是上一次链式调用的最终结果
finally(callback) {
return this.then(value=> {
return Promise.resolve( allback()).then(()=> value)
}, reason=> {
return Promise.resolve(callback()).then(()=> {throw reason})
})
}
}
// 测试代码
// 安装 : npm i promises-aplus-tests -g
// 运行 : promises-aplus-tests filename
Promise.defer = Promise.deferred = function () {
const dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = Promise
promise详解 : 实现promise(附实现代码)的更多相关文章
- 【转】IOS AutoLayout详解(三)用代码实现(附Demo下载)
转载自:blog.csdn.net/hello_hwc IOS SDK详解 前言: 在开发的过程中,有时候创建View没办法通过Storyboard来进行,又需要AutoLayout,这时候用代码创建 ...
- (转)Uri详解之——Uri结构与代码提取
前言:依然没有前言…… 相关博客:1.<Uri详解之——Uri结构与代码提取>2.<Uri详解之二——通过自定义Uri外部启动APP与Notification启动> 上几篇给大 ...
- Uri详解之——Uri结构与代码提取
目录(?)[+] 前言:依然没有前言…… 相关博客:1.<Uri详解之——Uri结构与代码提取>2.<Uri详解之二——通过自定义Uri外部启动APP与Notification启动& ...
- angular $q promise详解
前言 通过本文,你大概能清楚angular promise是个啥,$q又是个啥,以及怎么用它.这里咱们先灌输下promise的思想. 下面写的全是废话,一些看着高逼格其实没什么大作用的概念,想知道$q ...
- ES6 Promise 详解
一.概念 Promise,从语法上来讲,它是一个对象,是一个构造函数,可以获取 异步操作 的信息. 简单来讲,就是用同步的方式写异步代码,用来解决回调问题. 二.特点 Promise 对象有两个特点: ...
- js中的promise详解
一 概述 Promise是异步编程的一种解决方案,可以替代传统的解决方案--回调函数和事件.ES6统一了用法,并原生提供了Promise对象.作为对象,Promise有一下两个特点: (1)对象的 ...
- Promise详解
前言 && 基础概念 Promise 是解决 JS 异步的一种方案,相比传统的回调函数,Promise 能解决多个回调严重嵌套的问题. Promise 对象代表一个异步操作,有三种状态 ...
- 关于Promise详解
异步回调 回调地狱 在需要多个操作的时候,会导致多个回调函数嵌套,导致代码不够直观,就是常说的回调地狱 并行结果 如果几个异步操作之间并没有前后顺序之分,但需要等多个异步操作都完成后才能执行后续的任务 ...
- ES6中的Promise详解
Promise 在 JavaScript 中很早就有各种的开源实现,ES6 将其纳入了官方标准,提供了原生 api 支持,使用更加便捷. 定义 Promise 是一个对象,它用来标识 JavaScri ...
随机推荐
- Zabbix5.0Yum安装
1.1.操作系统配置: 1.操作系统属性(虚拟机下环境): (1)操作系统版本: [root@localhost ~]# cat /etc/redhat-release CentOS Linux re ...
- vue3,后台管理列表页面各组件之间的状态关系
技术栈 vite2 vue 3.0.5 vue-router 4.0.6 vue-data-state 0.1.1 element-plus 1.0.2-beta.39 前情回顾 表单控件 查询控件 ...
- CRM系统选型时的参考哪些方面
企业不论在制定营销策略或是在进行CRM系统选型时,首先都是要了解自身的需求.每一家企业的情况和需求都有很大差异,CRM系统的功能也都各有偏重.有些CRM偏重销售管理.有些注重于营销自动化.有些则侧重于 ...
- Java中的自增自减
情况①: for (int i = 0; i < 100; i++) { j = 1 + j++; } System.out.println(j); 结果是 0 !! 这是由于在进行后自增/自减 ...
- WebContent的子目录里面的jsp文件无法将数据传递给Servlet
在WebContent下创建子目录FormCheck,register.jsp将跳转到RegisterServlet这个Servlet中去 分两种情况:在web.xml里面配置 和 使用注解 1.在w ...
- Fiddler大全之断点操作
打断点(bpu) 1.为什么要打断点呢?比如一个购买的金额输入框,输入框前端做了限制100-1000,那么我们测试的时候,需要测试小于100的情况下.很显然前端只能输入大于100的.这是我们可以先抓到 ...
- webview和H5交互
由于H5的灵活多变,动态可配的特点,也为了避免冗长 的审核周期,H5页面在app上的重要性正日益突显. iOS应用于H5交互的控件主要是UIWebView及WKWebView WKWebView是14 ...
- 章节1-Prometheus基础(1)
目录 一.Prometheus安装部署 1. 简介 监控的目的 Prometheus的优势 2. Prometheus工作流程: 2.1 服务端 2.2 客户端 2.3 metrics主要数据类型 3 ...
- external-resizer 源码分析/pvc 扩容分析
kubernetes ceph-csi分析目录导航 基于tag v0.5.0 https://github.com/kubernetes-csi/external-resizer/releases/t ...
- python的setup.py文件及其常用命令
编写setup.py文件,获取帮助:python setup.py --help-commands [python] Standard commands: build ...