来源

前提知识

高阶函数

高阶函数

满足以下条件之一:

  1. 函数的参数是一个 函数
  2. 函数的返回值是一个函数

    闭包

    定义: 有权访问另一个函数作用域中变量的函数(来源红宝书)

    函数柯里化

    柯里化(Currying),把接受多个参数的函数转换成接受一个单一参数的函数

    举例

    ```javascript let add = function(x) {
    1. return function(y) {
    2. return x + y
    3. }
    }

add(3)(4) // 7 复制代码

  1. 实际开发如果需要用到 **柯里化**,推荐使用[ lodash.curry](https://lodash.com/docs/4.17.11#curry)
  2. <a name="mVxH3"></a>
  3. ## 应用(类型判断)
  4. `typeof` 无法判断 **对象** 类型<br />`constructor` 判断 是谁构造出来的<br />`instanceof` 判断 谁是谁的实例(即引用类型)<br />`Object.prototype.toString.call()` 完美判断
  5. ```javascript
  6. function isType(type) {
  7. return function (content) {
  8. return Object.prototype.toString.call(content) === `[object ${type}]`
  9. }
  10. }
  11. let isString = isType('String')
  12. console.log(isString('132456'))
  13. // 函数柯里化
  14. console.log(isType('Number')(132456))
  15. 复制代码

AOP 面向切片编程

也成为 装饰者模式
定义:指在不修改原有代码的情况下增加新功能

  1. function sleep(who) {
  2. who?console.log(who + '睡觉'):console.log('睡觉')
  3. }
  4. Function.prototype.before = function (callback) {
  5. return (...args)=> {// args 传入的参数数组
  6. callback()
  7. args?this(...args):this() // 传入参数
  8. }
  9. }
  10. let Wash = sleep.before(function () {
  11. console.log('洗脸')
  12. })
  13. Wash() // 洗脸 睡觉
  14. Wash('我') // 洗脸 我睡觉
  15. 复制代码

观察者模式

订阅发布

有挣议,有些人说这个不算是观察者模式。个人觉得算是属于

  1. let e = {
  2. _obg:{}, // 事件
  3. _callback:[], // 处理函数列表
  4. on(callback) { // 订阅
  5. this._callback.push(callback)
  6. },
  7. emit(key,value){ // 发布
  8. this._obg[key] = value
  9. this._callback.forEach(fn=>{
  10. fn(this._obg) // 参数传入
  11. })
  12. }
  13. }
  14. e.on(function (obj) {
  15. console.log('发布一个')
  16. console.log(obj)
  17. })
  18. setTimeout(function () {
  19. e.emit('name','琛')
  20. },1000)
  21. 复制代码

举一个简单的例子,即微博,你关注了A,A发动态就会通知你。你和A没有直接联系,通过 微博自己的调度来完成

观察者模式

与发布订阅区别

发布订阅模式是 两者之间没有直接关系,通过实践调度中心来完成。而观察者模式是相互依赖的,一个改变。另一个也发生改变

例子

  1. // 设计模式 观察者模式
  2. // 与发布订阅两者区别
  3. // 发布订阅 是基于一个中间管理 on 和 emit 没有直接关系
  4. // 观察者模式 是 Observer and Observed 有直接关系
  5. // 一个依赖改变,另一个也改变 Vue
  6. class Observer { // 观察者
  7. constructor(name) { // 传递参数
  8. this.name = name
  9. }
  10. update(baby){
  11. console.log(this.name+'知道'+baby.name+baby.state)
  12. }
  13. }
  14. class Observed{ // 被观察者
  15. constructor(name) {
  16. this.name = name
  17. this.state = '开心'
  18. this.Observer = []
  19. }
  20. addObserver(o){
  21. this.Observer.push(o)
  22. }
  23. setState(state){
  24. this.state = state
  25. this.Observer.forEach(o=>{
  26. o.update(this)
  27. })
  28. }
  29. }
  30. let baby = new Observed('宝宝')
  31. let dad = new Observer('爸爸')
  32. let mom = new Observer('妈妈')
  33. // 添加观察者
  34. baby.addObserver(dad)
  35. baby.addObserver(mom)
  36. // 设置状态
  37. baby.setState('不开心')
  38. baby.setState('开心')
  39. 复制代码

进入正题

基本的promise使用

promise

用来解决异步
Promise 是一个天生的类 需要传入一个函数 默认会立即执行
有三种状态 ,成功(resolve) ,失败(reject), 等待

基本用法

  1. let a = new Promise((resolve, reject) => {
  2. // 这两个方法可以更改promise 状态
  3. // resolve()如果是这样 下面的输出为 undefined
  4. resolve('成功')
  5. // reject('失败')
  6. })
  7. a.then((data)=>{
  8. console.log(data) // 成功
  9. },(err)=>{
  10. console.log(err) // 失败
  11. })
  12. 复制代码

resolve,reject 这两个方法可以改变状态。如果是 resolve,则走then的第一个函数,rejectthen的第二个函数。并且都将参数传入。
注意:走成功了就不可以走失败,反之亦然

从0开始,手写Promise

明白了基本用法,我们开始模仿一下Promise。这里使用ES6语法来模仿。

这是Promise的规范

同步实现

首先,需要就收一个executor,是一个函数。立即执行,有三那种状态,里面还有resolve,reject两个参数,这两个参数是函数,需要接收两个参数。同时promise还有then方法

思路分析

  1. // promise 同步
  2. // 三种状态的定义
  3. const Pending = 'PENDING'
  4. const Success = 'SUCCESS'
  5. const Error = 'Error'
  6. // promise 的实现
  7. class WritePromise {
  8. constructor(fn) { // 初始化
  9. this.state = Pending // 状态初始化
  10. this.value = undefined // 成功信息
  11. this.err = undefined // 错误信息
  12. let resolve = (value)=>{
  13. if (this.state === Pending){
  14. this.value = value
  15. this.state = Success
  16. }
  17. }
  18. let reject = (err)=>{
  19. if (this.state === Pending){
  20. this.value = err
  21. this.state = Error
  22. }
  23. }
  24. // 可能会出错
  25. try {
  26. fn(resolve,reject) // 立即执行
  27. }catch (e) {
  28. console.log(e) // 如果内部出错 直接交给reject 方法向下传递
  29. reject(e)
  30. }
  31. }
  32. then(onfulfilled,onrejected){
  33. switch (this.state) {
  34. case Success:
  35. onfulfilled(this.value)
  36. break
  37. case Error:
  38. onrejected(this.err)
  39. break
  40. }
  41. }
  42. }
  43. // export default WritePromise 浏览器端
  44. module.exports = WritePromise
  45. 复制代码

在new的过程中,执行constructor,传入的 resolveorreject,进行赋值和状态改变。然后then方法更具 state的状态进行不同的操作。onfulfilled and onrejected是传入的操作函数
为什么要加try catch
在使用过程中,不仅仅只有reject可以接收错误,也可以手动抛出错误。这样就reject捕获不到错误。 所以要加上 try catch 。 保证可以正常运行

测试

  1. let WritePromise = require('./WritePromise')
  2. let promise = new WritePromise((resolve, reject) => {
  3. // 1.
  4. resolve('成功')
  5. // 2.
  6. // reject('失败')
  7. })
  8. promise.then((data) => { // onfulfilled 成功
  9. console.log(data)
  10. }, (err) => { // onrejected 失败
  11. console.log(err)
  12. })
  13. // 输出 1. 成功 2. 失败
  14. 复制代码

异步实现

大家会发现。如果在resolveorreject,执行异步代码(例如定时器)。会发现没有结果。这是因为我们刚才写的都是同步代码。现在要改一下,改成异步的
这时候就用到我们前面的知识了,发布订阅模式

思路

  1. 首先,我们应该知道在`constructor`中传入的`fn`,如果加上定时器的话,它的状态`state`不会发生任何改变。也就是一直处于`等待状态`, 所以并不会执行`then`里面的函数。所以我们应该考虑一下当他处于`等待`的时候。是不是应该吧传入的函数存储起来,等到上面执行`resolve`or`reject`的时候,再把这个函数执行。

实现

  1. // promise 异步
  2. const Pending = 'PENDING'
  3. const Success = 'SUCCESS'
  4. const Error = 'Error'
  5. class WritePromiseAsync {
  6. constructor(fn) {
  7. this.state = Pending
  8. this.value = undefined
  9. this.err = undefined
  10. // 回调函数的存储
  11. this.SuccessCal = []
  12. this.ErrorCal = []
  13. let resolve = (value)=>{
  14. if (this.state === Pending){
  15. this.value = value
  16. this.state = Success
  17. // 对回调函数进行变量 然后执行
  18. this.SuccessCal.forEach((fn)=>fn())
  19. }
  20. }
  21. let reject = (err)=>{
  22. if (this.state === Pending){
  23. this.value = err
  24. this.state = Error
  25. this.ErrorCal.forEach((fn)=>fn())
  26. }
  27. }
  28. // 可能会出错
  29. try {
  30. fn(resolve,reject) // 立即执行
  31. }catch (e) {
  32. console.log(e) // 如果内部出错 直接交给reject 方法向下传递
  33. reject(e)
  34. }
  35. }
  36. then(onfulfilled,onrejected){
  37. switch (this.state) {
  38. case Success:
  39. onfulfilled(this.value)
  40. break
  41. case Error:
  42. onrejected(this.err)
  43. break
  44. case Pending:
  45. this.SuccessCal.push(()=>{
  46. // 为什么要这样写 因为这样可以做一些逻辑 AOP
  47. // 这里面可以做一些逻辑
  48. onfulfilled(this.value)
  49. })
  50. this.ErrorCal.push(()=>{
  51. onrejected(this.err)
  52. })
  53. break
  54. }
  55. }
  56. }
  57. module.exports = WritePromiseAsync
  58. 复制代码

在顺一遍。 创建对象之后,调用then方法, 代码开始执行,执行到then的时候,发现没有对应的状态改变,就先把它存储起来。等到定时器结束之后,在把所有的函数都执行一次

链式调用

  • 每次调用返回的都是一个新的Promise实例(这就是为什么可以一直then)
  • 链式调用的参数通过返回值传递
    1. // 第二点的 代码解释
    2. let b = new Promise((resolve, reject) => {
    3. resolve('data')
    4. }).then((data)=>{
    5. data = data + '132456'
    6. // then可以返回一个值,如果是普通值。就会走到下一个then 的成功中
    7. return data
    8. }).then((data)=>{
    9. console.log(data) // 输出 data132456
    10. })
    11. 复制代码
    如果返回的不是普通值,是promise,则会使用这个promise的结果
    1. let b = new Promise((resolve, reject) => {
    2. resolve('data')
    3. }).then((data)=>{
    4. data = data + '132456'
    5. return data
    6. }).then(()=>{
    7. return new Promise((resolve, reject) => {
    8. resolve('我是promise 的返回')
    9. // 如果返回的是一个promise,那么会采用这个promise的结果
    10. })
    11. }).then((data)=>{
    12. console.log(data) // 输出 我是promise 的返回
    13. })
    14. 复制代码

    catch

    用来捕获 最近的且没有捕获的错误
    1. let b = new Promise((resolve, reject) => {
    2. reject('data')
    3. }).then().catch(err=>{ // 捕获错误 捕获最近的没有捕获的错误
    4. console.log(err+'catch') // datacatch
    5. // 注意 返回的也是undefined
    6. })
    7. 复制代码

    注意点

    上述走的是成功,失败也一样。但会有一个小坑。
    1. let b = new Promise((resolve, reject) => {
    2. resolve('data')
    3. }).then(()=>{},err=>{
    4. console.log(err)
    5. // 在失败函数中如果返回的是一个普通值,也会走下一次then的成功中
    6. // return undefined 相当于返回了一个这个
    7. }).then((data)=>{
    8. console.log(data+'success') // 这个会走 成功的值 输出 underfinedsuccess
    9. },(err)=>{
    10. console.log(err+'err')
    11. })
    12. 复制代码
    特别注意,这里会经常有遗漏。

    链式调用的手写实现

    接着上次的WritePromiseAsync

    实现多次then传递 思路

    原版做法中,当连续调用then方法的时候,会把上一次的结果传递给下一个then
    上面说过每次调用then方法会返回一个promise实例。所以,我们需要在调用then方法的时候返回一个promise的实例,并且接收到then方法的结果。在传递给这个promise
    1. // 多余的我就不写了,主要写差异化 的 then方法
    2. then(onfulfilled, onrejected) {
    3. let promise2 = new ChainPromise((resolve, reject) => {
    4. let x
    5. switch (this.state) {
    6. case Success:
    7. x = onfulfilled(this.value)
    8. resolve(x)
    9. break
    10. case Error:
    11. x = onrejected(this.value)
    12. reject(x)
    13. break
    14. case Pending:
    15. this.SuccessCal.push(() => {
    16. try {
    17. let x = onfulfilled(this.value)
    18. resolve(x)
    19. } catch (e) {
    20. reject(e)
    21. }
    22. })
    23. this.ErrorCal.push(() => {
    24. try {
    25. let x = onrejected(this.err)
    26. reject(x)
    27. } catch (e) {
    28. reject(e)
    29. }
    30. })
    31. break
    32. }
    33. })
    34. return promise2
    35. }
    36. 复制代码
    注意,调用的时候要把 then的两个函数都要写上,否则会报错(还没有处理)
    这样过后 就可以实现 多次then方法传递结果了

    实现 返回promise 思路

    说一下上面得哪个x,我们是直接把它返回给对应得处理方法,如果x是一个promise呢? 按照原版得来说。我们应该把这个promise的结果作为返回值来继续传递。所以我们应该对这个x进行处理
    创建一个方法solveX,来处理x
    1. function solveX(promise2, x, resolve, reject) {
    2. if (promise2 === x){
    3. return reject(new TypeError('引用本身'))
    4. }
    5. if ((typeof x === 'object' && x != null)|| typeof x === 'function'){
    6. // 处理promise
    7. try {
    8. let then = x.then
    9. if (typeof then === 'function'){ // 只能认定他是promise了
    10. then.call(x,(data)=>{
    11. console.log(data)
    12. resolve(data)
    13. },(err)=>{
    14. reject(err)
    15. })
    16. } else {
    17. resolve(x)
    18. }
    19. } catch (e) {
    20. reject(e) // 取值失败 走err
    21. }
    22. }else {
    23. // 是一个普通值
    24. resolve(x)
    25. }
    26. }
    27. 复制代码
    为什么要把promise2传进来呢? 因为如果 x就是promise2呢?则会是一个死循环。
    x进行判断,如果是普通值,直接返回就可以了。如果不是,我们取then方法(注意是方法,不是结果). 如果有这个方法,我们就认定他是一个promise(可能有人会说如果then是一个空方法呢?,那也只能认定了,我们最多只能做到这种程度的判断了。)
    注意then的this 指向问题

    关于promise2传入问题

    1. try {
    2. x = onfulfilled(this.value)
    3. resolvePromise(promise2, x, resolve, reject)
    4. // 需要用x 来比较promise2的值
    5. // resolve()
    6. } catch (e) { // 一旦出错,走下一个promise 的错误处理方法
    7. reject(e)
    8. }
    9. 复制代码
    如果直接传入promise2的话,因为是同步的过程,在创建的时候promise2还没有生成,所以会报错。这时候我们可以加一个定时器,把它变成异步。这就解决了这个问题
    1. then(onfulfilled, onrejected) {
    2. let promise2 = new ChainPromise((resolve, reject) => {
    3. let x
    4. switch (this.state) {
    5. case Success:
    6. setTimeout(() => { // 如果不加定时器,promise2获取不到
    7. try {
    8. x = onfulfilled(this.value)
    9. resolvePromise(promise2, x, resolve, reject) // 需要用x 来比较promise2的值
    10. // resolve()
    11. } catch (e) { // 一旦出错,走下一个promise 的错误处理方法
    12. reject(e)
    13. }
    14. }, 0)
    15. // 实现之后要判断 X 如果x是一个普通值,就正常返回。如果是一个promise 则把promise的执行结果作为参数传递给 相应的处理函数
    16. break
    17. case Error:
    18. setTimeout(() => {
    19. try {
    20. x = onrejected(this.err)
    21. // reject(x)
    22. resolvePromise(promise2, x, resolve, reject)
    23. } catch (e) {
    24. reject(e)
    25. }
    26. }, 0)
    27. break
    28. case Pending:
    29. this.SuccessCal.push(() => {
    30. try {
    31. let x = onfulfilled(this.value)
    32. resolvePromise(promise2, x, resolve, reject)
    33. } catch (e) {
    34. reject(e)
    35. }
    36. })
    37. this.ErrorCal.push(() => {
    38. try {
    39. let x = onrejected(this.err)
    40. resolvePromise(promise2, x, resolve, reject)
    41. } catch (e) {
    42. reject(e)
    43. }
    44. })
    45. break
    46. }
    47. })
    48. return promise2
    49. }
    50. 复制代码
    注意,即使写的是0,也不会立即执行。

    解决then里面继续返回promise

    上面我们写了一个方法来处理promise,只需要进行一个递归就可以解决
    1. /**
    2. *
    3. * @param promise2
    4. * @param x
    5. * @param resolve
    6. * @param reject
    7. * @returns {*}
    8. */
    9. function resolvePromise(promise2, x, resolve, reject) {
    10. if (promise2 === x) {
    11. return reject(new TypeError('引用本身'))
    12. }
    13. if ((typeof x === 'object' && x != null) || typeof x === 'function') {
    14. // 处理promise
    15. try {
    16. let then = x.then
    17. if (typeof then === 'function') {
    18. then.call(x, (data) => {
    19. // resolve(data) 将data重新放入这个函数。直到是一个普通值再进行返回
    20. resolvePromise(promise2, data, resolve, reject)
    21. }, (err) => {
    22. // reject(err)
    23. resolvePromise(promise2, err, resolve, reject)
    24. })
    25. } else {
    26. resolve(x)
    27. }
    28. } catch (e) {
    29. reject(e) // 取值失败 走err
    30. }
    31. } else {
    32. // 是一个普通值
    33. resolve(x)
    34. }
    35. }
    36. 复制代码

    解决then必须传值

    1. then(onfulfilled, onrejected) {
    2. onfulfilled = typeof onfulfilled === 'function'?onfulfilled:v=>v
    3. onrejected = typeof onrejected === 'function'?onrejected:err=>{throw err}
    4. ........
    5. }
    6. 复制代码
    将两个函数进行判断。如果不是函数,默认赋一个函数

    防止多次调用

    1. function resolvePromise(promise2, x, resolve, reject) {
    2. if (promise2 === x) {
    3. return reject(new TypeError('Chaining cycle detected for promise #<Promise>--'))
    4. }
    5. let called // 添加一个变量进行控制
    6. if ((typeof x === 'object' && x != null) || typeof x === 'function') {
    7. try {
    8. let then = x.then
    9. if (typeof then === 'function') {
    10. then.call(x, y => {
    11. if (called) return
    12. called = true
    13. resolvePromise(promise2, y, resolve, reject)
    14. }, r => {
    15. if (called) return // 如果发现被调用过 直接return
    16. called = true
    17. reject(r)
    18. })
    19. } else {
    20. resolve(x)
    21. }
    22. } catch (e) {
    23. if (called) return
    24. called = true
    25. reject(e)
    26. }
    27. } else {
    28. resolve(x)
    29. }
    30. }
    31. 复制代码

    总结代码

    1. // promise 链式调用的实现
    2. const PENDING = 'PENDING'
    3. const RESOLVED = 'RESOLVED '
    4. const REJECTED = 'REJECTED'
    5. function resolvePromise(promise2, x, resolve, reject) {
    6. if (promise2 === x) {
    7. return reject(new TypeError('Chaining cycle detected for promise #<Promise>--'))
    8. }
    9. let called
    10. if ((typeof x === 'object' && x != null) || typeof x === 'function') {
    11. try {
    12. let then = x.then
    13. if (typeof then === 'function') {
    14. then.call(x, y => {
    15. if (called) return
    16. called = true
    17. resolvePromise(promise2, y, resolve, reject)
    18. }, r => {
    19. if (called) return
    20. called = true
    21. reject(r)
    22. })
    23. } else {
    24. resolve(x)
    25. }
    26. } catch (e) {
    27. if (called) return
    28. called = true
    29. reject(e)
    30. }
    31. } else {
    32. resolve(x)
    33. }
    34. }
    35. class Promise {
    36. constructor(executor) {
    37. this.status = PENDING
    38. this.value = undefined
    39. this.reason = undefined
    40. this.SuccessCal = []
    41. this.ErrorCal = []
    42. let resolve = value => {
    43. if (this.status === PENDING) {
    44. this.value = value
    45. this.status = RESOLVED
    46. this.SuccessCal.forEach(fn => fn())
    47. }
    48. }
    49. let reject = reason => {
    50. if (this.status === PENDING) {
    51. this.reason = reason
    52. this.status = REJECTED
    53. this.ErrorCal.forEach(fn => fn())
    54. }
    55. }
    56. try {
    57. executor(resolve, reject)
    58. } catch (e) {
    59. reject(e)
    60. }
    61. }
    62. then(onRESOLVED, onrejected) {
    63. onRESOLVED = typeof onRESOLVED === 'function' ? onRESOLVED : v => v
    64. onrejected = typeof onrejected === 'function' ? onrejected : err => {
    65. throw err
    66. }
    67. let promise2 = new Promise((resolve, reject) => {
    68. if (this.status === RESOLVED) {
    69. setTimeout(() => { // 如果不加定时器,promise2获取不到
    70. try {
    71. let x = onRESOLVED(this.value)
    72. resolvePromise(promise2, x, resolve, reject) // 需要用x 来比较promise2的值
    73. } catch (e) { // 一旦出错,走下一个promise 的错误处理方法
    74. reject(e)
    75. }
    76. }, 0)
    77. // 实现之后要判断 X 如果x是一个普通值,就正常返回。如果是一个promise 则把promise的执行结果作为参数传递给 相应的处理函数
    78. }
    79. if (this.status === REJECTED) {
    80. setTimeout(() => {
    81. try {
    82. let x = onrejected(this.reason)
    83. resolvePromise(promise2, x, resolve, reject)
    84. } catch (e) {
    85. reject(e)
    86. }
    87. }, 0)
    88. }
    89. if (this.status === PENDING) {
    90. this.SuccessCal.push(() => { // 为什么要这样写 因为这样可以做一些逻辑
    91. // 这里面可以做一些逻辑
    92. setTimeout(() => {
    93. try {
    94. let x = onRESOLVED(this.value)
    95. resolvePromise(promise2, x, resolve, reject)
    96. } catch (e) {
    97. reject(e)
    98. }
    99. }, 0)
    100. })
    101. this.ErrorCal.push(() => {
    102. setTimeout(() => {
    103. try {
    104. let x = onrejected(this.reason)
    105. resolvePromise(promise2, x, resolve, reject)
    106. } catch (e) {
    107. reject(e)
    108. }
    109. }, 0)
    110. })
    111. }
    112. })
    113. return promise2
    114. }
    115. }
    116. // 测试 需要测试再添加
    117. Promise.defer = Promise.deferred = function () {
    118. let dfd = {}
    119. dfd.promise = new Promise((resolve, reject) => {
    120. dfd.resolve = resolve
    121. dfd.reject = reject
    122. })
    123. return dfd
    124. }
    125. module.exports = Promise
    126. 复制代码

    关于符合性测试

    这个是测试工具的github
    安装之后, 执行
    npx promises-aplus-tests promise.js
    为什么是npx? 我没有全局安装
    全部通过
    3.Promise - 图1