老生常谈:Promise对象(2)链式调用

925 阅读7分钟

引言

上一篇介绍了Promise的简单实现,理解了promise解决异步问题的原理,今天来完善下promise的方法 上一篇文章结束用ES6语法实现promise基本功能

class Promise1 {
  // 实例属性和方法
  constructor(executor) {
    let that = this;
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = []; // 异步成功回调
    this.onRejectedCallbacks = []; // 异步失败回调
    try{
        executor(resolve, reject)
    }catch(err){
        console.log('执行器捕获异常',err)
    }
    function resolve(val) {
      if(that.state === 'pending') {
        that.value = val;
        that.onResolvedCallbacks.forEach(cb => {
          cb(val);
        })
        that.state = 'resolved';
      }
    }
    function reject(err) {
      if(that.state === 'pending') {
        that.reason = err;
        that.onRejectedCallbacks.forEach(cb => {
          cb(err);
        })
        that.state = 'rejected';
      }
    }
  }
 // 原型方法
 then(onFulfilled, onRejected) {
    if(this.state === 'pending') {
      if(typeof(onFulfilled) === 'function'){
        this.onResolvedCallbacks.push(onFulfilled);
      }
      if(typeof(onRejected) === 'function'){
        this.onRejectedCallbacks.push(onRejected);
      }
    }
    if(this.state === 'resolved' && typeof(onFulfilled) === 'function') {
      onFulfilled(this.value);
    }
    if(this.state === 'rejected' && typeof(onRejected) === 'function') {
      onFulfilled(this.value);
    }
  }
}
let p = new Promise1((resolve, reject)=>{
  setTimeout(() => {
    resolve('前端大菜鸟');
  }, 10);
})
p.then(res=> {
  console.log(res); // 前端大菜鸟
},err=>{
  console.log(err);
})

then链式调用

Promise之所以能够进行链式调用,是因为then()方法内部返回了一个Promise实例(promise2),这个Promise实例(promise2)才可以继续调用then()方法。并且这个Promise实例(promise2) resolve函数的参数,是上一个then的resolve函数的结果。reject同理。

效果

先看下效果

new Promise((resolve, reject) => {
    resolve(123)
}).then((res) => {
    console.log(res)
    return 456
}).then((res) => {
    console.log(res)
    return 789
}).then((res) => {
    console.log(res)
})
// 打印输出 123   456   789

改造第一步

根据上面的功能来改造then方法,返回一个新的promise实例(promise2)

class Promise1{
    ...
    then((onFulfilled, onRejected) {
       
       // ********定义一个Promise2**************
       let Promise2 = new Promise1((resolve, reject) => {
          if(this.state === 'pending') {
            this.onResolvedCallbacks.push(() => {
              let res = onFulfilled(this.value); 
              resolve(res); // *promise2 resolve的参数是上一个promise then的resolve(onFulfilled)的结果
            });
            this.onRejectedCallbacks.push(() => {
              let res = onRejected(this.reason) // 
              reject(res); // *promise2 reject的参数是上一个promise then的reject(onRejected)的结果
            });
          }
          if(this.state === 'resolved') {
            let res = onFulfilled(this.value); // 
            resolve(res); // *promise2 resolve的参数是上一个promise then的resolve(onFulfilled)的结果
          }
          if(this.state === 'rejected') {
            let res = onRejected(this.reason); // 
            reject(res); // *promise2 reject的参数是上一个promise then的reject(onRejected)的结果
          }  
        })
        // 返回Promise2
        return Promise2;
    }
}

let p = new Promise1((resolve, reject)=>{
  setTimeout(() => {
    resolve('前端大菜鸟');
  }, 10);
})
p.then(res=> {
  console.log(res); // 前端大菜鸟
  return 123
}).then(res1=>{
  console.log(res1); // 123
})
// 前端大菜鸟 123 

可以看到,then方法返回了一个新的Promise实例(promise2),并且将then(onFullFilled,onRejected)的回调的结果作为新的Promise实例(promise2)的参数resolve或者reject出去了。至此实现了普通值得链式调用。对于then返回的结果(onFullFilled、onRejected) 还有以下这几种情况。

  1. 返回一个函数,新的Promise实例(promise2)的then的onFullFilled函数会直接以这个函数当作参数
// ...
let p = new Promise1((resolve, reject)=>{
  setTimeout(() => {
    resolve('前端大菜鸟');
  }, 10);
})
p.then(res=> {
  console.log(res);
  return ()=>{console.log('小刺鸟');} // 返回一个函数
}).then(res1=>{ // 可以看到此时then的参数就是上个promise返回的函数
  console.log(res1()); // 小刺鸟
})
  1. 返回一个promise,新的Promise实例(promise2)的状态取决于上个返回的promise的状态,新的Promise实例(promise2)的返回值 是上个返回的promise的resolve() / reject()值。
// ...
let p = new Promise1((resolve, reject)=>{
  setTimeout(() => {
    resolve('前端大菜鸟');
  }, 10);
})
p.then(res=> {
  console.log(res); // 前端大菜鸟
  // 这里返回一个promise
  return new Promise1((resolve, reject)=> {
    resolve('我是小菜鸟1号')
  })
}).then(res1=>{
  console.log(res1); // 我是小菜鸟1号
})

// reject
p.then(res=> {
  console.log(res); // 前端大菜鸟
  // 这里返回一个promise
  return new Promise1((resolve, reject)=> {
    reject('我是小菜鸟2号')
  })
}).then(res1=>{
  console.log(res1); // 接受不到
},err=> {
    console.log(err) // 我是小菜鸟2号
})
// 可以看出当then返回一个promise时 新的promise实例(promise2)的状态是取决于上个返回的promise的状态 , 返回值也是上个promise resolve/reject的值

改造第二步

根据上述情况,此时,我们需要一个函数(formatPromise)来判断上一个then的onFullFilled/onrejected的返回值到底是普通值还是函数还是Promise对象。并且,当值不同时采用不一样的处理方式。

// promise2:新的Promise实例(promise2);result:上个promise的resolve() / reject()值
function formatPromise (promise2, result, resolve, reject){
  // 首先,先判断下promise2和result是否一样, 因为result是一个返回值,他有可能是一个Promise,那如果他直接返回第一个参数中的promise会造成死循环。
  if (promise2 === result) {
    return new Error('Promise循环引用')
  }
  // 判断是否是对象
  if (typeof result === 'object' && result != null) {
    try {
      // 如果是对象,先看是否存在then函数
      let then = result.then
      // 如果result.then是一个函数,就当成是Promise对象
      if (typeof then === 'function') {
        // 利用.call将this指向result, 防止result.then()报错
        then.call(result, res => {
          resolve(res)
        }, err => {
          reject(err)
        })
      } else {
        // 如果不是function,那么说明只是普通对象,并不是Promise对象,当普通值处理
        resolve(result)
      }
    } catch (err) {
      reject(err)
    }
  } else {
    // 不是对象,那就是普通值或者函数,直接resolve()
    resolve(result)
  }
}

改造第三步

我们构造好formatPromise函数后再去改造一下then函数,已知Promise 中处理器函数是同步执行,而then方法是异步的且是个微任务,所以我们用queueMicrotask来实现微任务。

class Promise1{
// ... 
then(onFulfilled, onRejected) {
        
    // ********定义一个Promise2**************
    let Promise2 = new Promise1((resolve, reject) => {
      onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
      onRejected = typeof onRejected === "function" ? onRejected : (err) => { throw err };

      if(this.state === 'pending') {
        this.onResolvedCallbacks.push(() => {
            queueMicrotask(() => {
              let res = onFulfilled(this.value); 
              // 调用formatPromise函数
              formatPromise(Promise2, res, resolve, reject); // *promise2 resolve的参数是上一个promise then的resolve(onFulfilled)的结果
            })
        });
        this.onRejectedCallbacks.push(() => {
            queueMicrotask(() => {
              let res = onRejected(this.reason) 
              // 调用formatPromise函数
              formatPromise(Promise2, res, resolve, reject); // *promise2 reject的参数是上一个promise then的reject(onRejected)的结果
            })
        });
      }
      if(this.state === 'resolved') {
          queueMicrotask(() => {
            let res = onFulfilled(this.value); 
            // 调用formatPromise函数
            formatPromise(Promise2, res, resolve, reject); // *promise2 resolve的参数是上一个promise then的resolve(onFulfilled)的结果
          })
      }
      if(this.state === 'rejected') {
          queueMicrotask(() => {
            let res = onRejected(this.reason); 
            // 调用formatPromise函数
            formatPromise(Promise2, res, resolve, reject); // *promise2 reject的参数是上一个promise then的reject(onRejected)的结果
          })
      }  
    })
    // 返回Promise2
    return Promise2;
  }
}

至此我们完成了promise的链式调用功能,当然现在有很多不足的地方,比如捕获异常和promise同时调用只能调用一次等等,感兴趣的同僚可以自行去了解一下吧

catch()

其实.catch(),和.then()的reject回调是一样的。只是使用位置不一样罢了。并且.catch()也支持链式调用。也就是说.catch和.then一样,也返回了一个Promise对象,所以其实catch内部就是执行了一个只有reject回调的then函数。

Promise1.prototype.catch = function (onRejected) {
  return this.then(null, onRejected)
}

resolve()

将现有对象转为 Promise 对象

const isPromise = (value) => {
    if ((value != null && typeof value === 'object') || typeof value === 'function') {
        if (typeof value.then == 'function') {
            return true
        }
    } else {
        return false
    }
}
Promise1.resolve = (value) => {
    // 如果是promise对象就直接将这个对象返回
    if (isPromise(value)) {
        return value
    } else {
        // 如果是一个普通值就将这个值包装成一个promise对象之后返回
        return new Promise1((resolve, reject) => {
            resolve(value)
        })
    }
}

reject()

返回一个新的 Promise 实例,该实例的状态为rejected

Promise1.reject = (value) => {
    return new Promise1((resolve, reject) => {
        reject(value)
    })
}

all()

Promise1.all = (array) => {
    //接收数组
    if (!Array.isArray(array)) {
      return reject(new TypeError('must array'))
    }
    let result = [] // 处理结果的数组
    let index = 0
    return new Promise1((resolve, reject) => {
      for (let i = 0; i < array.length; i++) {
        let current = array[i]
        if (current instanceof Promise1) {
          current.then(
            (value) => {
              addData(i, value)
            },
            (reason) => reject(reason)
          )
        } else {
          //普通值
          addData(i, array[i])
        }
      }
      //返回一个promise对象
      function addData(key, value) {
        result[key] = value
        index++
        if (index === array.length) resolve(result)
      }
    })
  }
}

完整代码(仅then)


class Promise1 {
  constructor(executor) {
    let that = this;
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = []; // 异步成功回调
    this.onRejectedCallbacks = []; // 异步失败回调

    executor(resolve, reject)

    function resolve(val) {
      if(that.state === 'pending') {
        that.value = val;
        that.onResolvedCallbacks.forEach(cb => {
          cb();
        })
        that.state = 'resolved';
      }
    }
    function reject(err) {
      if(that.state === 'pending') {
        that.reason = err;
        that.onRejectedCallbacks.forEach(cb => {
          cb();
        })
        that.state = 'rejected';
      }
    }
  }

  then(onFulfilled, onRejected) {
        
    // ********定义一个Promise2**************
    let Promise2 = new Promise1((resolve, reject) => {
      onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
      onRejected = typeof onRejected === "function" ? onRejected : (err) => { throw err };

      if(this.state === 'pending') {
        this.onResolvedCallbacks.push(() => {
            queueMicrotask(() => {
              let res = onFulfilled(this.value); 
              formatPromise(Promise2, res, resolve, reject); // *promise2 resolve的参数是上一个promise then的resolve(onFulfilled)的结果
            })
        });
        this.onRejectedCallbacks.push(() => {
            queueMicrotask(() => {
              let res = onRejected(this.reason) // 
              formatPromise(Promise2, res, resolve, reject); // *promise2 reject的参数是上一个promise then的reject(onRejected)的结果
            })
        });
      }
      if(this.state === 'resolved') {
          queueMicrotask(() => {
            let res = onFulfilled(this.value); // 
            formatPromise(Promise2, res, resolve, reject); // *promise2 resolve的参数是上一个promise then的resolve(onFulfilled)的结果
          })
      }
      if(this.state === 'rejected') {
          queueMicrotask(() => {
            let res = onRejected(this.reason); // 
            formatPromise(Promise2, res, resolve, reject); // *promise2 reject的参数是上一个promise then的reject(onRejected)的结果
          })
      }  
    })
    // 返回Promise2
    return Promise2;
  }
}

function formatPromise (promise2, result, resolve, reject){
  // 首先,先判断下promise2和result是否一样, 因为result是一个返回值,他有可能是一个Promise,那如果他直接返回第一个参数中的promise是会造成死循环的。
  if (promise2 === result) {
    return new Error('Promise循环引用')
  }
  // 判断是否是对象
  if (typeof result === 'object' && result != null) {
    try {
      // 如果是对象,先看是否存在then函数
      let then = result.then
      // 如果result.then是一个函数,就当成是Promise对象
      if (typeof then === 'function') {
        // 利用.call将this指向result, 防止result.then()报错
        then.call(result, res => {
          resolve(res)
        }, err => {
          reject(err)
        })
      } else {
        // 如果不是function,那么说明只是普通对象,并不是Promise对象,当普通值处理
        resolve(result)
      }
    } catch (err) {
      reject(err)
    }
  } else {
    // 不是对象,那就是普通值或者函数,直接resolve()
    resolve(result)
  }
}

let p = new Promise1((resolve, reject)=>{
  setTimeout(() => {
    resolve('我是前端路上的一个小菜鸟');
  }, 10);
})
p.then(res=> {
  console.log(res);
  return new Promise1((resolve)=>{
    resolve('我在不断进步!!!')
  })
}).then(res1=>{
  console.log(res1);
})

// 输出 我是前端路上的一个小菜鸟 我在不断进步!!!

结语

promise的介绍就止于此,如果感觉有帮助的大佬们,请留个赞,如果说有写的不对的地方,也请大佬们留言指正。