前端必会系列之Promise

187 阅读14分钟

Promise

在ES6之前,我们解决异步函数的通用方式是传入一个回调函数,在ES6之后我们可以用Promise去解决异步函数,ES7还能使用async

Promise的状态变更是一个微任务

Promise特性

  1. Promise对象存在状态,状态只能在内部被修改,状态:pendding(进行中)fulfilled(成功)rejected(失败)
  2. Promise状态变更之后就不可改变,且,只能从 pendding变为fulfilled或者rejected,不能从fulfilled变成rejected
  3. Promise会吃掉错误,在new Promise实例中出现错误,会打印错误,但是不会影响JS主进程的执行

Promise基本使用

new Promise((resolve,reject)=>{})的构造函数中有两个函数;

  1. resolve():将promise状态更改为fulfilled,它的参数将会传给then的第一个参数函数
  2. reject():将promise状态更改为rejected,它的参数将会传给then的第二个参数函数
  3. 当使用resolve()reject(),之后,函数仍旧会继续执行,需要我们使用return终止掉防止意外

例子

const fn = ()=> {
    
    return new Promise((resolve,reject)=>{
        console.log("fn执行。。。")  // Promise 新建之后就会立即执行
        setTimeout(()=>{
            console.log("fn的回调执行。。。");
            return resolve('1243')   // 使用 return 终止掉 函数的执行
            // console.log("继续执行")   不加return 这行就会继续执行
        },1000)
    })
};
console.log("主进程开始")
fn().then((res)=>{
    console.log("promise状态已决",res)
})
console.log("主进程结束")
// 输出
// 主进程开始
// fn执行。。。
// 主进程结束
// fn的回调执行。。。
// promise状态已决 1243

Promise构造函数中的resolve()可以返回另一个Promise实例,此时,当前的Promise将由返回的那个Promise实例决定状态。

const p1 = ()=> {
    return new Promise((resolve,reject)=>{
        console.log("p1执行。。。")  // 先建就立即执行
        setTimeout(()=>{
            console.log("p1的回调执行。。。");
            resolve('1243')
        },1000)
    })
};
const p2 = ()=> {
    return new Promise((resolve,reject)=>{
        console.log("p2执行。。。")  // 先建就立即执行
        setTimeout(()=>{
            console.log("p2的回调执行。。。");
            resolve(p1())  // 这里的p2返回的是一个promise实例p1
        },1000)
    })
};
p2().then((res)=>{
    console.log("promise状态已决",res)
})
/**
 * 输出
    p2执行。。。
    p2的回调执行。。。
    p1执行。。。
    p1的回调执行。。。
    promise状态已决 1243
 */

看上面的代码,我们可知,p2返回的是P1的promise实例,p2的状态由p1决定,当p1调用resolve(),p1的状态是fulfilled,那么p2的状态就是fulfilled,p1的状态是rejected,那么p2的状态就是rejectedp2的状态由p1的状态决定

Promise.resolve()
Promise.resolve(abc)

相当于

new Promise(resolve => resolve(abc))
  1. 这个函数的作用是:简化我们新建Promise对象的写法
  2. 这个函数是一个幂等方法(多少次调用都是一样的结果)
  3. 这个函数会把传入的参数,包装成一个Promise对象,修改Promise状态为fulfilled,并返回

当传入的是一个thenable对象时,它会先执行thenable对象的then方法

thenable 对象:这个对象中有 then 方法

const foo = {
    then(resolve,reject){
        console.log("then方法执行。。。")
        resolve(123);
    }
}
Promise.resolve(foo).then(r=>{
    console.log("Promise.resolve()的then方法开始执行。。",r)
})
/**
 * 
then方法执行。。。
Promise.resolve()的then方法开始执行。。 123
 */

如上所示,foo是一个thenable对象,且使用resolve()返回了一个值42,随后在Promise.resolve().then()中执行它

Promise.reject()

Promise.reject(abc)

相当于

new Promise((resolve,reject)=>reject(abc))

作用是立即创造一个状态为rejected的Promise对象,并返回

Promise.prototype.then()

我们在proimise实例上可以使用then(onFulfilled,onRejected)方法捕获promise的状态

then(...)是一个微任务

  1. onFulfilled:可选; 此函数将捕获Promise状态为fulfilled,也就是在new Promise((resolve,reject)=>{})执行时,使用构造函数的第一个参数resolve更改的状态,它的参数就是resolve的参数。
  2. onRejected:可选;此函数将捕获Promise状态为rejected,也就是在new Promise((resolve,reject)=>{})执行时,使用构造函数的第二个参数reject更改的状态,它的参数就是reject的参数
  3. then返回一个新的Promise实例(如果不指定的话,那么就是返回undefined,相当于then(res=>Promise.resolve(undefined))),因此可用于链式调用
  4. 如果then()不传第二个参数的话,我们还可以通过.catch去捕获状态为rejectedpromise,如果是链式调用的话,那么任一个promise实例出现rejected都会进入catch中,并中止链式调用执行
  5. 如果onFulfilled或者onRejected不是一个函数,那么将会被忽略,且新的Promise的状态受之前的promise状态的影响

例子

const fn =  new Promise((resolve,rej)=>{
    return resolve(123)
})
console.log("主进程 S")
fn.then(res=>{
    console.log("状态为:fulfilled",res)
});
console.log("主进程 E")
/**
主进程 S
主进程 E
状态为:fulfilled 123
 */

从上我们可以看到,then方法中的onFulfilled得到了resolve的返回值,因为此时的Promise状态是fulfilled

const fn =  new Promise((resolve,reject)=>{
    return reject(123)
})
console.log("主进程 S")
fn.then(res=>{
    console.log("参数是:",res)
},(err)=>{
    console.log("状态为:rejected",err)
});
console.log("主进程 E")
/**
主进程 S
主进程 E
状态为:rejected: 123
 */

从上我们可以看到,then方法中的onRejected得到了reject的返回值,因为此时的Promise状态是fulfilled,上面的例子,我们还可以使用.catch改造。
看下面的例子

const fn =  new Promise((resolve,reject)=>{
    return reject(123)
})
console.log("主进程 S")
fn.then(res=>{
    console.log("参数是:",res)
}).catch((err)=>{
    console.log("状态为:rejected",err)
})
console.log("主进程 E")
/**
主进程 S
主进程 E
参数是: 123
 */

我们使用.catch捕获了状态为rejected的Promise实例。
如果onFulfilled或者onRejected不是一个函数,那么将会被忽略,且新的Promise的状态受之前的promise状态的影响,让我们看下面的例子:

Promise.resolve(123).then("字符串A").then(res=>console.log("fulfilled:",res));
// 输出  fulfilled: 123

Promise.reject(123).then("字符串A","字符串B").then(res=>console.log("fulfilled:",res),err=>console.log("rejected:",err));
// 输出:rejected: 123

我们看到第一行代码,因为then传入的是一个字符串,所以被忽略了,这个then新建的Promise实例,受上一个Promise状态的影响。

链式调用

then方法返回的是一个Promise实例,所以能进行链式调用,让我们来看下面的例子:

const p1 =  ()=> new Promise((resolve,reject)=>{
    console.log('立即执行p1。。。');
    setTimeout(()=>{
        console.log('p1执行。。。');
        resolve(123);
    },1000)
})
const p2 =  (v)=> new Promise((resolve,reject)=>{  // 假设 p2 是需要 p1 的参数才能去执行异步操作
    console.log("立即执行p2。。。")
    setTimeout(()=>{
        console.log('p2执行。。。');
        resolve(`${v}456`);    
    },1000)
})
// 链式调用
p1().then(resP1=>{
    console.log("p1的状态为 fulfilled:",resP1)
    return p2(resP1);   // p2 继续执行。。
}).then(resP2=>{
    console.log("p2的状态为 fulfilled:",resP2)
    return 'pppp'  // 这里返回一个 常量  这个常量生成的Promise对象状态默认就是 fulfilled
}).then(res=>{
    console.log("这个promise的状态为 fulfilled",res)
})
/**
    立即执行p1。。。
    p1执行。。。
    p1的状态为 fulfilled: 123
    立即执行p2。。。
    p2执行。。。
    p2的状态为 fulfilled: 123456
    这个promise的状态为 fulfilled pppp
 */

我们通过then可返回一个Promise对象的特性,实现了链式调用,当异步操作p1执行完毕,在执行异步操作p2;p2.then中我们return了一个常量,这个常量生成的Promise的状态默认就是fulfilled
当链式调用的任何一个环节发生错误,都将阻止链式调用,进入到 catch()中

const p1 =  ()=> new Promise((resolve,reject)=>{
    console.log('立即执行p1。。。');
    setTimeout(()=>{
        console.log('p1执行。。。');
        resolve(123);
    },1000)
})
const p2 =  (v)=> new Promise((resolve,reject)=>{  // 假设 p2 是需要 p1 的参数才能去执行异步操作
    console.log("立即执行p2。。。")
    setTimeout(()=>{
        console.log('p2执行。。。');
        reject(`${v}456`);      // 假设 p2 异步操作失败了
    },1000)
})
// 链式调用
p1().then(resP1=>{
    console.log("p1的状态为 fulfilled:",resP1)
    return p2(resP1);   // p2 继续执行。。
}).then(resP2=>{
    console.log("p2的状态为 fulfilled:",resP2)
    return 'pppp'  // 这里返回一个 常量
}).then(res=>{
    console.log("这个promise的状态为 fulfilled",res)
}).catch(err=>{
    console.log("rejected:",err);
})
/**
    立即执行p1。。。
    p1执行。。。
    p1的状态为 fulfilled: 123
    立即执行p2。。。
    p2执行。。。
    rejected: 123456
 */

从上面例子可以看到,p2的状态是rejected,所以,链式调用失败,进入到了 catch 中。

Promise.prototype.catch()

提供Promise实例的错误捕获机制。

  1. Promise的状态为rejected 或者then中出现错误,都将走到catch中
  2. Promise.prototyp.catch 将返回一个新的Promise实例,默认是返回Promise.resolve(undefined),状态为fulfilled
  3. 其实它的本质就是then的语法糖,内部实现大概就是.then(null,onRejected(err=>Promise.resolve(undefined)))
  4. Promise.prototyp.catch 同 Promise.prototyp.then 一样是微任务 基本用法:
const p = ()=>new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log("异步操作完成,假设失败了");
        return reject(123);  // 假设失败了
    })
})
p().then(res=>{}).catch(err=>{
    console.log("rejected",err);
})

当链式调用时,任何一个Promise实例状态为rejected或者then中发生错误都将走到catch

const p = ()=>new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log("异步操作完成");
        return resolve(123);  
    })
})
p().then(res=>{
    console.log("此时的promise状态为:fulfilled")
    console.log("这里输出一个不存在的值",aaa);  // then 内部这里抛出一个错误
}).catch(err=>{
    console.log("rejected:",err);
})
// rejected: ReferenceError: aaa is not defined

可以看到,我们的Promise实例p的状态已经是fulfilled了,但是,由于在then中出现了错误,所以我们进入了catch

const p1 =  Promise.reject(234).catch(err=>err);   //  catch 默认返回一个 Promise实例,相当于 Promise.resolve(err)
p1.then(res=>{console.log("p1的状态为fulfilled :",res)})  
// p1的状态为fulfilled : 234
const p2 = Promise.reject(123);
const p3 = p2.catch(err=>Promise.reject(456));
console.log(p3)  // rejected 

我们看到这里p1的状态是 fulfilled ,因为 catch 默认返回了一个fulfilled的状态
p2是一个rejected状态的Promise,在p2的catch中返回了一个Promise状态为rejected并赋值给P3,我们可以看到p3的状态为rejected

setTimeout(()=>console.log("宏任务setTimeout执行"),0)
console.log("主进程 S")
p1.catch(err=>{console.log("catch执行")});
console.log("主进程 E")
/*
  主进程 S
  主进程 E
  catch执行
  宏任务setTimeout执行
*/

从上可以看出catch的操作是异步的(很合理,毕竟是then的语法糖)

Promise.all()

假如我们的异步操作没有彼此之间的依赖关系,那么我们就不需要链式调用了,可以使用all()进行并发请求。

  1. Promise.all([])中传入一个Promise实例数组(不一定要数组,只要具有Iterator接口的数据也行)
  2. Promise.all([])只有,所有的Promise实例都成为fulfilled才能进入then的onFulfilled
  3. Promise.all([]),中的Promise实例,当自己定义了catch时,就不会被Promise.all()判断为rejected
const p1 = ()=>new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log("p1完成");
        return resolve(123);  
    },500)
})
const p2 = ()=>new Promise((resolve,reject)=>{  // 假设p2 的异步操作为 1S
    setTimeout(()=>{
        console.log("p2完成");
        return resolve(456);  
    },1000)   
})
const p3 = ()=>new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log("p3完成");
        return resolve(789);  
    },0)
})
Promise.all([p1(),p2(),p3()]).then((res=>{   // 当使用Promise.all时,onFulfilled 的参数为 结果数组,顺序按照调用顺序排序
   const [resP1,resP2,resP3] = res;
   console.log("看看p1:",resP1)
   console.log("看看p2:",resP2)
   console.log("看看p3:",resP3)
}))

当使用Promise.all时,onFulfilled 的参数为 结果数组,顺序按照调用顺序排序 只有当p1,p2,p3的状态都为fulfilled才会进入then方法,then(onFulfilled,onRejected)中的onFulfilled参数为一个结果数组,顺序是all中的调用顺序(不受异步操作时长的控制)。
看下面的例子,我们修改p2的状态为:rejected

const p1 = ()=>new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log("p1完成");
        return resolve(123);  
    })
})
const p2 = ()=>new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log("p2完成");
        return reject(456);  // 假设失败了
    },1000)
})
const p3 = ()=>new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log("p3完成");
        return resolve(789);  
    })
})
Promise.all([p1(),p2(),p3()]).then((res=>{   // 当使用Promise.all时,onFulfilled 的参数为 结果数组,顺序按照调用顺序排序
   const [resP1,resP2,resP3] = res;
   console.log("看看p1:",resP1)
   console.log("看看p2:",resP2)
   console.log("看看p3:",resP3)
})).catch(err=>{
    console.log("发生错误:",err)
})
// p1完成
// p3完成
// p2完成
// 发生错误: 456

当然了,我们使用then(onFulfilled,onRejected)中的onRejected也是一样的效果:

const p1 = ()=>new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log("p1完成");
        return resolve(123);  
    })
})
const p2 = ()=>new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log("p2完成");
        return reject(456);  // 假设失败了
    },1000)
})
const p3 = ()=>new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log("p3完成");
        return resolve(789);  
    })
})
Promise.all([p1(),p2(),p3()]).then((res=>{   // 当使用Promise.all时,onFulfilled 的参数为 结果数组,顺序按照调用顺序排序
   const [resP1,resP2,resP3] = res;
   console.log("看看p1:",resP1)
   console.log("看看p2:",resP2)
   console.log("看看p3:",resP3)
}),err=>{
    console.log("看看错误:",err);
})
/**
  p1完成
  p3完成
  p2完成
  看看错误: 456
 */

当我们在Promise实例中,返回了自身的.catch函数,Promise.all()将忽略掉它(也就是不会进入Promise.all的方法中)

上面这段话也很好理解,Promise.catch 实例返回的是一个新的Promise对象,且它的状态是fulfilled,自然Promise.all会成功处理,进入onFulfilled

const p1 = ()=> new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log("p1完成");
        return resolve(123);  // 假设失败了
    })
})
const p2 = ()=> new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log("p2完成");
        return reject(456);  // 假设失败了
    },1000)
})
.catch(err=>{
    console.log("p2内部的catch执行了。。。",err)
})
const p3 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log("p3完成");
        return resolve(789);  // 假设失败了
    })
})
Promise.all([p1,p2,p3]).then((res=>{   // 当使用Promise.all时,onFulfilled 的参数为 结果数组,顺序按照调用顺序排序
    console.log("看看结果:",res);
}),err=>{
    console.log("Promise.all的catch执行了:",err);
})
/**
    p1完成
    p3完成
    p2完成
    p2内部的catch执行了。。。 456
    看看结果: [ 123, undefined, 789 ]
 */

可以看到,我们给p2定义了catch函数,当p2的状态为rejected时,Promise.all将继续执行

Promise.race()

Promise.all()需要全部的Promise实例的状态都为fulfilled才执行then(onFulfilled,onRejected)不同。Promise.race()是只要其中的一个Promise实例状态发生改变就去执行then(onFulfilled,onRejected)

  1. 和Promise.all(),一样,传入一个Promise实例数组,或者具有Iterator接口的数据也行
  2. 当这个Promise实例数组中的某一个实例状态发生改变,那就执行then
const p1 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log("p1完成");
        return resolve(123);  // 假设失败了
    },500)
})
const p2 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log("p2完成");
        return resolve(456);  // 假设失败了
    },1000)
})
.catch(err=>{
    console.log("p2内部的catch执行了。。。",err)
})
const p3 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log("p3完成");
        return resolve(789);  // 假设失败了
    })
})
Promise.race([p1,p2,p3]).then((res=>{  
    console.log("看看结果:",res);
}),err=>{
    console.log("Promise.all的catch执行了:",err);
})
/**
    p1完成
    看看结果: 123
    p3完成
    p2完成
 */

从输出我们可以看出来,p1、p2、p3的Promise实例数组,p3的异步操作最早完成,于是Promise.racethen执行了,且值是p3的值 重头戏来了,让我们根据PromiseA+规范实现一个简易版的Promise

Promise实现

参考:juejin.cn/post/699459…
PromiseA+规范:promisesaplus.com/
源码

/**
 * promise的实现
 */
/**
 * promise的实现
 */
 class MyPromise{
  constructor(handler){
      this.PromiseResult = null // 终值
      this.STATUS = ["PENGDING","FULFILLED","REJECTED"]
      this.PromiseState  = this.STATUS[0];  // 状态
      // 添加执行队列
      this.onFulfilledCallbacks = [];  // 成功回调
      this.onRejectedCallbacks  = [];   // 失败回调

      this.initBind();
      try {
          handler(this.resolve,this.reject)
      } catch (error) {
          this.reject();
      }
  }
  resolve(val){
      if(this.PromiseState!=this.STATUS[0])
          return
      this.PromiseResult = val;
      this.PromiseState = this.STATUS[1];
      // 依次执行队列
      while (this.onFulfilledCallbacks.length) {
          this.onFulfilledCallbacks.shift()(this.PromiseResult)
      }
  }
  reject(val){
      if(this.PromiseState!=this.STATUS[0])
          return
      this.PromiseResult = val;
      this.PromiseState = this.STATUS[2]
      // 依次执行队列
      while (this.onRejectedCallbacks.length) {
          this.onRejectedCallbacks.shift()(this.PromiseResult)
      }
  }
  // 两个回调
  /**
   * 这里就需要调用到then的特性了
   * 1、链式调用: // 链式调用 输出 200
          const p3 = new Promise((resolve, reject) => {
              resolve(100)
          }).then(res => 2 * res, err => console.log(err)).then(res => console.log(res), err => console.log(err))
      2、下一个then的状态依赖于第一个then的onFulfilled、onRejected返回值,
         若第一个then的回调函数返回的是一个Promise,那么当这个Promise为成功时下一个then执行onFulfilled;失败时,下一个then执行onRejected
         若第一个then的回调函数返回的不是一个Promise,那么无论这个then是成功的还是失败的,下一个then都默认执行onFulfilled
   * @param {*} onFulfilled 
   * @param {*} onRejected 
   * @returns 
   */
  then(onFulfilled,onRejected){
    const isFunction = (f)=> Object.prototype.toString.call(f)==="[object Function]"
      // 保证进来的是函数类型
      // 即使不是,也可以使用适配器进行转换 
      onFulfilled = isFunction(onFulfilled) ? onFulfilled :val=>val;
      onRejected = isFunction(onRejected) ? onRejected :reason=>{throw reason};
      var thenPromise = new MyPromise((resolve,reject)=>{
          const resolvePromise = cb =>{
              // 这里加的setTimeOut是为了兼容 同步的promise(就是实现了一个微任务)
              setTimeout(()=>{
                  try {
                      const x = cb(this.PromiseResult)   // 获取return 的值
                      if(x===thenPromise){   // 如果这个return 返回时自身的话
                          throw new Error("不能返回自身")
                      }
                      if(x instanceof MyPromise){   // 如果这个return 返回的是 promise的话
                          x.then(resolve, reject)
                      }else{   // 不是的话,那就是 字符或者其他类型 此时是默认是成功状态的
                          resolve(x)
                      }
                  } catch (error) {
                      reject(err)
                  }
              })
          }
          if(this.PromiseState===this.STATUS[1]){
              resolvePromise(onFulfilled)
              // onFulfilled(this.PromiseResult);
          }
          if(this.PromiseState===this.STATUS[0]){
              this.onFulfilledCallbacks.push(onFulfilled)
              this.onRejectedCallbacks.push(onRejected)
          }
          if(this.PromiseState===this.STATUS[2]){
              resolvePromise(onRejected)
              // onRejected(this.PromiseResult);
          }
      })
      return thenPromise
  }
  /**
   * 这个函数的作用是,当这个promises数组都是fuilfilled(成功)状态时,才执行then的成功回调函数,返回值全是这个数组的item的resolve结果
   * 当这个promises数组中,出现非Promise的实例时,自动算fuilfilled
   * 只要这个数组中出现了一个rejected(失败),执行then的失败回调函数,参数是这个失败的reject的参数
   * @param {数组}} promises 
   * @returns 
   */
  static all(promises) {
      const result = []
      let count = 0
      return new MyPromise((resolve, reject) => {
          const addData = (index, value) => {
              result[index] = value
              count++
              if (count === promises.length) resolve(result)
          }
          promises.forEach((promise, index) => {
              if (promise instanceof MyPromise) {
                  promise.then(res => {
                      addData(index, res)
                  }, err => reject(err))
              } else {
                  addData(index, promise)
              }
          })
      })
  }
  /**
   * 接收promise数组,非promise数组的自动算成功
   * 哪个promise返回结果最快,就返回那个结果,无论成功或者失败
   * @param {*} promises 
   */
  static race (promises){
      return new MyPromise((resolve, reject) => {
          promises.forEach(promise => {
              if (promise instanceof MyPromise) {
                  promise.then(res => {
                      resolve(res)
                  }, err => {
                      reject(err)
                  })
              } else {
                  resolve(promise)
              }
          })
      })
  }
  
  catch (onRejected){
    if([this.STATUS[1],this.STATUS[0]].includes(this.PromiseResult)){  // pedding fulfilled 不能进入
      return 
    }
    // 之前提到过 catch 是 then(null,onRejected)  的语法糖
    return this.then(null,onRejected)
  }

  initBind() {
      // 修改 this.resolve 以及 this.reject 的this指向 MyPromise内部 避免发生隐式丢失
      // 这里为什么不用call 或者 apply 呢 因为 bind 绑定的函数,不会立即执行
      this.resolve = this.resolve.bind(this)
      this.reject = this.reject.bind(this)
  }
}
const p1 = new MyPromise((resolve,reject)=>{
    console.log("执行MyPromise的构造函数");
    setTimeout(resolve(22),0)
})
p1.then((res)=>{
    console.log("执行then方法:",res);
})
console.log("主流程执行。。。")

附:compose函数实现

从《JS高级程序设计第四版》得知,可利用Promise链式调用的特性,实现一个compose函数

compose函数:组合函数,传入函数数组,对函数进行依次调用,此刻调用的参数是上一个调用函数的返回值

Promise的实现

function compose(...fns) {
    return (x) => fns.reduce((promise, fn) => promise.then(fn), Promise.resolve(x))
}
    function addTwo(x) {return x + 2;}
    function addThree(x) {return x + 3;}
    function addFive(x) {return x + 5;}
    let addTen = compose(addTwo, addThree, addFive);
    addTen(8).then(console.log); // 18

普通版的compose实现

const compose =  (x)=>{
    return (...args)=>{
        let result = null;
        args.reduce((acc,cur,index)=>{
            acc = cur(acc);
            result = acc;  // 做个赋值
            return acc
        },x)
        return result;
    }
}

如有错误,望指正