天天看點

手寫promise——Ts,實作then;catch;all這3個常用方法 (步驟詳細)最後放上完整代碼(包括使用示例)

1.首先定義一個類MyPromise以及聲明一些關鍵的參數類型

MyPromise

這個類裡應該包含

屬性

  1. status

    狀态;
  2. callbacks

    用于存放then、catch傳入的方法
  3. result

    用于儲存結果

方法

  1. resolve

    成功回調函數
  2. reject

    錯誤回調函數
/**
*	定義泛型
*/
type MyPromiseType = InstanceType<typeof MyPromise>
type GetKeyType<T,K extends keyof T> = T[K]
type Resolve = GetKeyType<MyPromiseType, 'resolve'>
type Reject = GetKeyType<MyPromiseType, 'reject'>

class MyPromise {
  [key:string]: any
  status: 'resolved' | 'rejected' | 'pending' = 'pending'
  callbacks: any[] = []
  result:any = null
  constructor(fn:(_:Resolve,__:Reject) => void) {
  	/**
  	* 綁定函數this指向
  	*/
    this.initBind()
    /**
    * 初始化值
    */
    this.initValue()
    
    fn(this.resolve, this.reject)
  }
  
   initValue() {
    this.status = 'pending'
    this.callbacks = []
    this.result = null
  }
  
  initBind () {
    this.resolve = this.resolve.bind(this)
    this.reject = this.reject.bind(this)
  }

   resolve(data:any) {
    if (this.status !== 'pending') return
    this.status = 'resolved'
    this.result = data
    try {
  }

  reject(data:any) {
    if (this.status !== 'pending') return
    this.status = 'rejected'
    this.result = data
 }

}
           
promise狀态一旦發生改變,

是不可逆的

,我們需要在resolve、reject這個方法裡面添加

if (this.status !== ‘pending’) return

防止promise狀态發生二次改變

ps:如果對ts有興趣可以點選下面可連結

TS進階

2. 實作then

then實作的功能

  1. 接受

    兩個回調函數

    ,一個成功,一個失敗
  2. 儲存回調函數

    ,在調用

    resolve

    或者

    reject

    方法時再調用對應的函數(及

    callbacks

    裡的方法)
  3. 能夠

    鍊式調用

    ,比如new MyPromise.then(callback).catch(callback)
MyPromise.prototype.then = function(success:Function, onRejected:Function) {
  if (this.status === 'pending') {
    this.callbacks.push({success,error})
  } else if (this.status === 'resolved') {
    setTimeout(() => {
      success(this.result)
    })
  } else if (this.status === 'rejected') {
    onRejected(this.result)
  }
  return this
}
           

實作思路

  1. 根據狀态

    status

    判斷是将方法存起來還是立即調用;
  2. setTimeout

    代替微任務,保證能擷取到this.result;
  3. return this

    保證能鍊式調用;

3. 實作catch

catch實作的功能

  1. catch接受

    一個回調函數

  2. then中的回調執行錯誤時

    會執行catch的回調
// 實作catch
MyPromise.prototype.catch = function (onRejected:Function) { //.catch接受一個回調函數
  if (this.status === 'pending') {
      // 将回調函數放入callbacks數組中
      this.callbacks.push({ onRejected })
  } else if (this.status === 'rejected') {
    setTimeout(() => {
        onRejected(this.result)
    })
  }
}
           

這兩個方法實作了還暫時不能使用,需要在

MyPromise這個類

中的對應方法,加入如下代碼

resolve(data:any) {
    if (this.status !== 'pending') return
    this.status = 'resolved'
    this.result = data
    try {
      this.callbacks.forEach(val => {
        val.success && val.success(this.result)
      })
    } catch (error) {
      this.callbacks.forEach(val => {
        val.onRejected && val.onRejected(error)
      })
    }
  }

  reject(data:any) {
    if (this.status !== 'pending') return
    this.status = 'rejected'
    this.result = data
    this.callbacks.forEach(val => {
      val.onRejected && val.onRejected(this.result)
    })
  }
           

實作思路

  1. 使用new MyPromise這個方法中,使用者主動調用resolve時,

    檢測是否有在then

    中存入的方法;
  2. 由于callbacks這個數組不止裝了then的方法,執行時我們需要

    執行對用的方法

  3. then的回調函數中,如果

    出現錯誤代碼,就會執行catch的回調函數

    ;

4. 實作all

all 實作的功能

  1. 接收一個Promise

    數組

  2. 當這個數組所有promise都傳回resolve,

    Rromise.all傳回一個resolve

    , 傳回的是一個數組,這個數組中的值與promise數組一 一對應的;
  3. 或當其中一個promise傳回reject,那麼Rromise.all立即中斷後面的執行,

    立即傳回reject

MyPromise.all = function(list:any[]) {
  return new MyPromise((resolve, reject) => {
    const allList = list.filter(val => val instanceof MyPromise)
    const resultList:any[] = Array.from({length: allList.length})
    allList.forEach((val, idx) => {
      val
        .then((result:any) => {
          resultList.splice(idx, 1, result)
          resultList.filter(item => item).length === allList.length && resolve(resultList)
        })
        .catch((err:any) => {
          reject(err)
        })
    })
  })
}
           

最後放上完整代碼(包括使用示例)

type MyPromiseType = InstanceType<typeof MyPromise>
type GetKeyType<T,K extends keyof T> = T[K]
type Resolve = GetKeyType<MyPromiseType, 'resolve'>
type Reject = GetKeyType<MyPromiseType, 'reject'>

class MyPromise {
  [key:string]: any
  status: 'resolved' | 'rejected' | 'pending' = 'pending'
  callbacks: any[] = []
  result:any = null
  constructor(fn:(_:Resolve,__:Reject) => void) {
    this.initBind()
    this.initValue()
    fn(this.resolve, this.reject)
  }

  initValue() {
    this.status = 'pending'
    this.callbacks = []
    this.result = null
  }

  initBind () {
    this.resolve = this.resolve.bind(this)
    this.reject = this.reject.bind(this)
  }

  resolve(data:any) {
    if (this.status !== 'pending') return
    this.status = 'resolved'
    this.result = data
    try {
      this.callbacks.forEach(val => {
        val.success && val.success(this.result)
      })
    } catch (error) {
      this.callbacks.forEach(val => {
        val.onRejected && val.onRejected(error)
      })
    }
  }

  reject(data:any) {
    if (this.status !== 'pending') return
    this.status = 'rejected'
    this.result = data
    this.callbacks.forEach(val => {
      val.onRejected && val.onRejected(this.result)
    })
  }
}

// 實作then
MyPromise.prototype.then = function(success:Function, onRejected:Function) {
  if (this.status === 'pending') {
    this.callbacks.push({success,onRejected})
  } else if (this.status === 'resolved') {
    setTimeout(() => {
      success(this.result)
    })
  } else if (this.status === 'rejected') {
    onRejected(this.result)
  }
  return this
}


// 實作catch
MyPromise.prototype.catch = function (onRejected:Function) { //.catch接受一個回調函數
  if (this.status === 'pending') {
      // 将回調函數放入callbacks數組中
      this.callbacks.push({ onRejected })
  } else if (this.status === 'rejected') {
    setTimeout(() => {
        onRejected(this.result)
    })
  }
}

//實作all
// @ts-ignore
 MyPromise.all = function(list:any[]) {
  return new MyPromise((resolve, reject) => {
    const allList = list.filter(val => val instanceof MyPromise)
    const resultList:any[] = Array.from({length: allList.length})
    allList.forEach((val, idx) => {
      val
        .then((result:any) => {
          resultList.splice(idx, 1, result)
          resultList.filter(item => item).length === allList.length && resolve(resultList)
        })
        .catch((err:any) => {
          reject(err)
        })
    })
  })
}


// new MyPromise((resolve:any, reject:any) => {
//   const num = 1
//   setTimeout(() => {
//     if (num % 2) {
//       resolve({msg: "成功"})
//     } else {
//       reject({msg: "成功"})
//     }
//   }, 2000);
// }).then(() => {
//   throw Error('124')
// }).catch((err:any) => {
//   console.log(err);
// })


// const fn1 = () => {
//   return test1
// }

// (async() => {
//   const result = await fn1()
//   console.log(result);
// })()

const v1 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
      resolve({msg: "成功1"})
  }, 5000);
})
const v2 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
      // resolve({msg: "成功2"})
      reject({msg: "失敗2"})
  }, 4000);
})
const v3 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
      resolve({msg: "成功3"})
  }, 1000);
})

// @ts-ignore
MyPromise.all([v1,v2,v3]).then(res => {
  console.log(res);
}).catch((err:any) => {
  console.log(err);
})

           

繼續閱讀