来源
前提知识
高阶函数
高阶函数
满足以下条件之一:
- 函数的参数是一个 函数
- 函数的返回值是一个函数
闭包
定义: 有权访问另一个函数作用域中变量的函数(来源红宝书
)函数柯里化
柯里化(Currying),把接受多个参数的函数转换成接受一个单一参数的函数举例
```javascript let add = function(x) {
}return function(y) {
return x + y
}
add(3)(4) // 7 复制代码
实际开发如果需要用到 **柯里化**,推荐使用[ lodash.curry](https://lodash.com/docs/4.17.11#curry)
<a name="mVxH3"></a>
## 应用(类型判断)
`typeof` 无法判断 **对象** 类型<br />`constructor` 判断 是谁构造出来的<br />`instanceof` 判断 谁是谁的实例(即引用类型)<br />`Object.prototype.toString.call()` 完美判断
```javascript
function isType(type) {
return function (content) {
return Object.prototype.toString.call(content) === `[object ${type}]`
}
}
let isString = isType('String')
console.log(isString('132456'))
// 函数柯里化
console.log(isType('Number')(132456))
复制代码
AOP 面向切片编程
也成为 装饰者模式定义:
指在不修改原有代码的情况下增加新功能
function sleep(who) {
who?console.log(who + '睡觉'):console.log('睡觉')
}
Function.prototype.before = function (callback) {
return (...args)=> {// args 传入的参数数组
callback()
args?this(...args):this() // 传入参数
}
}
let Wash = sleep.before(function () {
console.log('洗脸')
})
Wash() // 洗脸 睡觉
Wash('我') // 洗脸 我睡觉
复制代码
观察者模式
订阅发布
有挣议
,有些人说这个不算是观察者模式。个人觉得算是属于
let e = {
_obg:{}, // 事件
_callback:[], // 处理函数列表
on(callback) { // 订阅
this._callback.push(callback)
},
emit(key,value){ // 发布
this._obg[key] = value
this._callback.forEach(fn=>{
fn(this._obg) // 参数传入
})
}
}
e.on(function (obj) {
console.log('发布一个')
console.log(obj)
})
setTimeout(function () {
e.emit('name','琛')
},1000)
复制代码
举一个简单的例子,即微博,你关注了A,A发动态就会通知你。你和A没有直接联系,通过 微博自己的调度来完成
观察者模式
与发布订阅区别
发布订阅模式
是 两者之间没有直接关系,通过实践调度中心来完成。而观察者模式
是相互依赖的,一个改变。另一个也发生改变
例子
// 设计模式 观察者模式
// 与发布订阅两者区别
// 发布订阅 是基于一个中间管理 on 和 emit 没有直接关系
// 观察者模式 是 Observer and Observed 有直接关系
// 一个依赖改变,另一个也改变 Vue
class Observer { // 观察者
constructor(name) { // 传递参数
this.name = name
}
update(baby){
console.log(this.name+'知道'+baby.name+baby.state)
}
}
class Observed{ // 被观察者
constructor(name) {
this.name = name
this.state = '开心'
this.Observer = []
}
addObserver(o){
this.Observer.push(o)
}
setState(state){
this.state = state
this.Observer.forEach(o=>{
o.update(this)
})
}
}
let baby = new Observed('宝宝')
let dad = new Observer('爸爸')
let mom = new Observer('妈妈')
// 添加观察者
baby.addObserver(dad)
baby.addObserver(mom)
// 设置状态
baby.setState('不开心')
baby.setState('开心')
复制代码
进入正题
基本的promise使用
promise
用来解决异步
Promise 是一个天生的类 需要传入一个函数 默认会立即执行
有三种状态 ,成功(resolve) ,失败(reject), 等待
基本用法
let a = new Promise((resolve, reject) => {
// 这两个方法可以更改promise 状态
// resolve()如果是这样 下面的输出为 undefined
resolve('成功')
// reject('失败')
})
a.then((data)=>{
console.log(data) // 成功
},(err)=>{
console.log(err) // 失败
})
复制代码
resolve,reject 这两个方法可以改变状态。如果是 resolve
,则走then
的第一个函数,reject
走then
的第二个函数。并且都将参数传入。
注意:走成功了就不可以走失败,反之亦然
从0开始,手写Promise
明白了基本用法,我们开始模仿一下Promise
。这里使用ES6
语法来模仿。
这是Promise的规范
同步实现
首先,需要就收一个executor
,是一个函数。立即执行,有三那种状态,里面还有resolve
,reject
两个参数,这两个参数是函数,需要接收两个参数。同时promise
还有then
方法
思路分析
// promise 同步
// 三种状态的定义
const Pending = 'PENDING'
const Success = 'SUCCESS'
const Error = 'Error'
// promise 的实现
class WritePromise {
constructor(fn) { // 初始化
this.state = Pending // 状态初始化
this.value = undefined // 成功信息
this.err = undefined // 错误信息
let resolve = (value)=>{
if (this.state === Pending){
this.value = value
this.state = Success
}
}
let reject = (err)=>{
if (this.state === Pending){
this.value = err
this.state = Error
}
}
// 可能会出错
try {
fn(resolve,reject) // 立即执行
}catch (e) {
console.log(e) // 如果内部出错 直接交给reject 方法向下传递
reject(e)
}
}
then(onfulfilled,onrejected){
switch (this.state) {
case Success:
onfulfilled(this.value)
break
case Error:
onrejected(this.err)
break
}
}
}
// export default WritePromise 浏览器端
module.exports = WritePromise
复制代码
在new的过程中,执行constructor
,传入的 resolve
orreject
,进行赋值和状态改变。然后then
方法更具 state
的状态进行不同的操作。onfulfilled
and onrejected
是传入的操作函数
为什么要加try catch
在使用过程中,不仅仅只有reject
可以接收错误,也可以手动抛出错误。这样就reject
捕获不到错误。 所以要加上 try catch 。 保证可以正常运行
测试
let WritePromise = require('./WritePromise')
let promise = new WritePromise((resolve, reject) => {
// 1.
resolve('成功')
// 2.
// reject('失败')
})
promise.then((data) => { // onfulfilled 成功
console.log(data)
}, (err) => { // onrejected 失败
console.log(err)
})
// 输出 1. 成功 2. 失败
复制代码
异步实现
大家会发现。如果在resolve
orreject
,执行异步代码(例如定时器)。会发现没有结果。这是因为我们刚才写的都是同步代码。现在要改一下,改成异步的
这时候就用到我们前面的知识了,发布订阅模式
思路
首先,我们应该知道在`constructor`中传入的`fn`,如果加上定时器的话,它的状态`state`不会发生任何改变。也就是一直处于`等待状态`, 所以并不会执行`then`里面的函数。所以我们应该考虑一下当他处于`等待`的时候。是不是应该吧传入的函数存储起来,等到上面执行`resolve`or`reject`的时候,再把这个函数执行。
实现
// promise 异步
const Pending = 'PENDING'
const Success = 'SUCCESS'
const Error = 'Error'
class WritePromiseAsync {
constructor(fn) {
this.state = Pending
this.value = undefined
this.err = undefined
// 回调函数的存储
this.SuccessCal = []
this.ErrorCal = []
let resolve = (value)=>{
if (this.state === Pending){
this.value = value
this.state = Success
// 对回调函数进行变量 然后执行
this.SuccessCal.forEach((fn)=>fn())
}
}
let reject = (err)=>{
if (this.state === Pending){
this.value = err
this.state = Error
this.ErrorCal.forEach((fn)=>fn())
}
}
// 可能会出错
try {
fn(resolve,reject) // 立即执行
}catch (e) {
console.log(e) // 如果内部出错 直接交给reject 方法向下传递
reject(e)
}
}
then(onfulfilled,onrejected){
switch (this.state) {
case Success:
onfulfilled(this.value)
break
case Error:
onrejected(this.err)
break
case Pending:
this.SuccessCal.push(()=>{
// 为什么要这样写 因为这样可以做一些逻辑 AOP
// 这里面可以做一些逻辑
onfulfilled(this.value)
})
this.ErrorCal.push(()=>{
onrejected(this.err)
})
break
}
}
}
module.exports = WritePromiseAsync
复制代码
在顺一遍。 创建对象之后,调用then
方法, 代码开始执行,执行到then
的时候,发现没有对应的状态改变,就先把它存储起来。等到定时器结束之后,在把所有的函数都执行一次
链式调用
- 每次调用返回的都是一个新的Promise实例(这就是为什么可以一直
then
) - 链式调用的参数通过返回值传递
如果返回的不是普通值,是promise,则会使用这个promise的结果// 第二点的 代码解释
let b = new Promise((resolve, reject) => {
resolve('data')
}).then((data)=>{
data = data + '132456'
// then可以返回一个值,如果是普通值。就会走到下一个then 的成功中
return data
}).then((data)=>{
console.log(data) // 输出 data132456
})
复制代码
let b = new Promise((resolve, reject) => {
resolve('data')
}).then((data)=>{
data = data + '132456'
return data
}).then(()=>{
return new Promise((resolve, reject) => {
resolve('我是promise 的返回')
// 如果返回的是一个promise,那么会采用这个promise的结果
})
}).then((data)=>{
console.log(data) // 输出 我是promise 的返回
})
复制代码
catch
用来捕获 最近的且没有捕获的错误let b = new Promise((resolve, reject) => {
reject('data')
}).then().catch(err=>{ // 捕获错误 捕获最近的没有捕获的错误
console.log(err+'catch') // datacatch
// 注意 返回的也是undefined
})
复制代码
注意点
上述走的是成功
,失败也一样。但会有一个小坑。
特别注意,这里会经常有遗漏。let b = new Promise((resolve, reject) => {
resolve('data')
}).then(()=>{},err=>{
console.log(err)
// 在失败函数中如果返回的是一个普通值,也会走下一次then的成功中
// return undefined 相当于返回了一个这个
}).then((data)=>{
console.log(data+'success') // 这个会走 成功的值 输出 underfinedsuccess
},(err)=>{
console.log(err+'err')
})
复制代码
链式调用的手写实现
接着上次的WritePromiseAsync
实现多次then传递 思路
原版做法中,当连续调用then
方法的时候,会把上一次的结果传递给下一个then
。
上面说过每次调用then
方法会返回一个promise
实例。所以,我们需要在调用then
方法的时候返回一个promise
的实例,并且接收到then
方法的结果。在传递给这个promise
注意,调用的时候要把// 多余的我就不写了,主要写差异化 的 then方法
then(onfulfilled, onrejected) {
let promise2 = new ChainPromise((resolve, reject) => {
let x
switch (this.state) {
case Success:
x = onfulfilled(this.value)
resolve(x)
break
case Error:
x = onrejected(this.value)
reject(x)
break
case Pending:
this.SuccessCal.push(() => {
try {
let x = onfulfilled(this.value)
resolve(x)
} catch (e) {
reject(e)
}
})
this.ErrorCal.push(() => {
try {
let x = onrejected(this.err)
reject(x)
} catch (e) {
reject(e)
}
})
break
}
})
return promise2
}
复制代码
then
的两个函数都要写上,否则会报错(还没有处理)
这样过后 就可以实现 多次then方法传递结果了实现 返回promise 思路
说一下上面得哪个x
,我们是直接把它返回给对应得处理方法,如果x
是一个promise
呢? 按照原版得来说。我们应该把这个promise
的结果作为返回值来继续传递。所以我们应该对这个x
进行处理
创建一个方法solveX
,来处理x
。
为什么要把function solveX(promise2, x, resolve, reject) {
if (promise2 === x){
return reject(new TypeError('引用本身'))
}
if ((typeof x === 'object' && x != null)|| typeof x === 'function'){
// 处理promise
try {
let then = x.then
if (typeof then === 'function'){ // 只能认定他是promise了
then.call(x,(data)=>{
console.log(data)
resolve(data)
},(err)=>{
reject(err)
})
} else {
resolve(x)
}
} catch (e) {
reject(e) // 取值失败 走err
}
}else {
// 是一个普通值
resolve(x)
}
}
复制代码
promise2
传进来呢? 因为如果x
就是promise2
呢?则会是一个死循环。
对x
进行判断,如果是普通值,直接返回就可以了。如果不是,我们取then
方法(注意是方法,不是结果). 如果有这个方法,我们就认定他是一个promise
(可能有人会说如果then
是一个空方法呢?,那也只能认定了,我们最多只能做到这种程度的判断了。)
注意then
的this 指向问题关于promise2传入问题
如果直接传入try {
x = onfulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
// 需要用x 来比较promise2的值
// resolve()
} catch (e) { // 一旦出错,走下一个promise 的错误处理方法
reject(e)
}
复制代码
promise2
的话,因为是同步的过程,在创建的时候promise2
还没有生成,所以会报错。这时候我们可以加一个定时器,把它变成异步。这就解决了这个问题
注意,即使写的是then(onfulfilled, onrejected) {
let promise2 = new ChainPromise((resolve, reject) => {
let x
switch (this.state) {
case Success:
setTimeout(() => { // 如果不加定时器,promise2获取不到
try {
x = onfulfilled(this.value)
resolvePromise(promise2, x, resolve, reject) // 需要用x 来比较promise2的值
// resolve()
} catch (e) { // 一旦出错,走下一个promise 的错误处理方法
reject(e)
}
}, 0)
// 实现之后要判断 X 如果x是一个普通值,就正常返回。如果是一个promise 则把promise的执行结果作为参数传递给 相应的处理函数
break
case Error:
setTimeout(() => {
try {
x = onrejected(this.err)
// reject(x)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
break
case Pending:
this.SuccessCal.push(() => {
try {
let x = onfulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
this.ErrorCal.push(() => {
try {
let x = onrejected(this.err)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
break
}
})
return promise2
}
复制代码
0
,也不会立即执行。解决then里面继续返回promise
上面我们写了一个方法来处理promise
,只需要进行一个递归就可以解决/**
*
* @param promise2
* @param x
* @param resolve
* @param reject
* @returns {*}
*/
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('引用本身'))
}
if ((typeof x === 'object' && x != null) || typeof x === 'function') {
// 处理promise
try {
let then = x.then
if (typeof then === 'function') {
then.call(x, (data) => {
// resolve(data) 将data重新放入这个函数。直到是一个普通值再进行返回
resolvePromise(promise2, data, resolve, reject)
}, (err) => {
// reject(err)
resolvePromise(promise2, err, resolve, reject)
})
} else {
resolve(x)
}
} catch (e) {
reject(e) // 取值失败 走err
}
} else {
// 是一个普通值
resolve(x)
}
}
复制代码
解决then必须传值
将两个函数进行判断。如果不是函数,默认赋一个函数then(onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function'?onfulfilled:v=>v
onrejected = typeof onrejected === 'function'?onrejected:err=>{throw err}
........
}
复制代码
防止多次调用
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>--'))
}
let called // 添加一个变量进行控制
if ((typeof x === 'object' && x != null) || typeof x === 'function') {
try {
let then = x.then
if (typeof then === 'function') {
then.call(x, y => {
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject)
}, r => {
if (called) return // 如果发现被调用过 直接return
called = true
reject(r)
})
} else {
resolve(x)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
resolve(x)
}
}
复制代码
总结代码
// promise 链式调用的实现
const PENDING = 'PENDING'
const RESOLVED = 'RESOLVED '
const REJECTED = 'REJECTED'
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>--'))
}
let called
if ((typeof x === 'object' && x != null) || typeof x === 'function') {
try {
let then = x.then
if (typeof then === 'function') {
then.call(x, y => {
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject)
}, r => {
if (called) return
called = true
reject(r)
})
} else {
resolve(x)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
resolve(x)
}
}
class Promise {
constructor(executor) {
this.status = PENDING
this.value = undefined
this.reason = undefined
this.SuccessCal = []
this.ErrorCal = []
let resolve = value => {
if (this.status === PENDING) {
this.value = value
this.status = RESOLVED
this.SuccessCal.forEach(fn => fn())
}
}
let reject = reason => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.ErrorCal.forEach(fn => fn())
}
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
then(onRESOLVED, onrejected) {
onRESOLVED = typeof onRESOLVED === 'function' ? onRESOLVED : v => v
onrejected = typeof onrejected === 'function' ? onrejected : err => {
throw err
}
let promise2 = new Promise((resolve, reject) => {
if (this.status === RESOLVED) {
setTimeout(() => { // 如果不加定时器,promise2获取不到
try {
let x = onRESOLVED(this.value)
resolvePromise(promise2, x, resolve, reject) // 需要用x 来比较promise2的值
} catch (e) { // 一旦出错,走下一个promise 的错误处理方法
reject(e)
}
}, 0)
// 实现之后要判断 X 如果x是一个普通值,就正常返回。如果是一个promise 则把promise的执行结果作为参数传递给 相应的处理函数
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onrejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
}
if (this.status === PENDING) {
this.SuccessCal.push(() => { // 为什么要这样写 因为这样可以做一些逻辑
// 这里面可以做一些逻辑
setTimeout(() => {
try {
let x = onRESOLVED(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
this.ErrorCal.push(() => {
setTimeout(() => {
try {
let x = onrejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
}
})
return promise2
}
}
// 测试 需要测试再添加
Promise.defer = Promise.deferred = function () {
let dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = Promise
复制代码
关于符合性测试
这个是测试工具的github
安装之后, 执行npx promises-aplus-tests promise.js
为什么是npx
? 我没有全局安装
全部通过