異步解決方案一:promise

以前一直對於promise,就是一頓用,歷來沒有深究其內部原理,無心間看到一些關於promise輸出順序的題目也是一頓瞎猜。。。哈哈,在個人同事對promise進行分享以後(雖然我由於起不來沒有聽),可是學習promise的意識就此種下。。。啥也不說了,就是學。。。css

帶着疑問去學習:jquery

  1. promise是幹什麼的?
  2. promise有哪些方法?
  3. pomise如何實現鏈式調用?以前jquery也是能夠鏈式調用,他們以前的區別?
  4. promise的then方法是如何肯定在前面resolve或者reject執行完拿到結果再執行的?
  5. 手寫一個知足promiseA+的promise方法(寫了一下,發現沒法經過promiseA+的測試用例,已放棄,拿着別人寫的仔細看了一下)

promise是幹什麼的

promise是一種異步編程的解決方案,它能夠實現把異步操做按照同步操做的流程表達出來。它能夠接收一個異步請求,而後在其then方法能夠獲得前面異步請求的結果進行後續操做,使用鏈式調用的方式將異步請求的結果按照同步的方式表達出來。編程

理解誤區

1. promise只是異步編程的解決方案,自身並非異步請求,在new Promise()內部傳的方法會當即執行。數組

const promise = new Promise(function (resolve, reject) {
  console.log('12')
  setTimeout(function () {
    resolve('success')
  }, 1000)
})
promise.then(function (success) {
  console.log(success)
})
// 執行結果: 先輸出12,過了一秒後再輸入success
複製代碼

2. new Promise內的resolve是異步執行的,且全部then方法和catch方法也會將在當前腳本全部同步任務執行完纔會執行promise

const myPromise = new Promise(function (resolve, reject) {
  console.log('1')
  resolve('2')
  console.log('3')
})
myPromise.then(function (success) {
  console.log(success)
})
setTimeout (function () {
    // 這裏能夠看到前面myPromise已經resolved了,可是他仍是會在最後執行,由於then內部也是異步操做
    myPromise.then(function (success) {
        console.log('again', success)
    })
    console.log('4')
}, 1000)
console.log('5')
// 執行結果是: 1 3 5 2 4 again 2
複製代碼

promise有哪些方法

基本用法

const promise = new Promise(function(resolve, reject) {
  if (/* 異步操做成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});
promise.then(function(value) {}, function (error) {})
複製代碼

基本用法簡單歸納一下:bash

  1. 三種狀態: - pending(進行中) - fulfilled(已成功) -rejected(已失敗)
  2. new Promise傳進的方法內resolve返回的是成功的結果,reject返回的是失敗的結果
  3. new Promise實例化後的then方法接受兩個回調函數,分別是resolve和reject的回調函數

掛載在原型上的方法

Promise.prototype.then()&&Promise.prototype.catch()

  • then: 該方法內的函數在Promise狀態爲resolve的狀況下執行,且then方法內屬於異步操做,若是前面的new Promise沒有resolve,then內部傳進來的函數不執行
  • catch:該方法內的函數在Promise狀態爲reject的狀況下執行,且catch方法內屬於異步操做,catch方法和then不太同樣,它在前面new Promise()出錯或者reject時,catch內部的方法都會執行。

then()和catch()默認返回的都是一個Promise實例對象,即便return了別的信息,返回的仍然是Promise實例對象,仍是且這個promise的狀態都是resolve異步

Promise.prototype.finally()

無論Promise對象最後狀態如何,finally方法都會執行,(promise是pedding狀態不會執行finally方法,resolve和reject都會執行)ide

掛載在Promise對象上的方法

Promise.resolve() && Promise.reject()

將現有對象轉換爲Promise對象異步編程

Promise.all()

將多個Promise實例包裝成一個新的Promise實例。函數

var p1 = new Promise((resolve) => {
  setTimeout(() => {
    resolve('p1')
  }, 1000)
})
var p2 = new Promise((resolve) => {
  setTimeout(() => {
    resolve('p2')
  }, 2000)
})
var p3 = new Promise((resolve) => {
  setTimeout(() => {
    resolve('p3')
  }, 3000)
})
var p = Promise.all([p1, p2, p3]).then((value) => {
  console.log(value)
}).catch((err) => {
  console.log(err)
})
// 返回值:['p1', 'p2', 'p3']
複製代碼

這裏Promise的狀態是由p1,p2,p3共同決定的:

  1. p1,p2和p3的狀態都爲resolve時,p1,p2和p3的返回值都以一個數組的形式傳給後續p的then方法裏
  2. 其中有一個的狀態變爲reject時,第一個被reject的實例的返回值會傳遞給後續p的catch方法裏
  3. 若是前面做爲參數傳進來的promise實例中狀態爲rejected了,且其本身定義了catch方法,那麼後續不會觸發Promise.all()的then方法
  4. 若是前面做爲參數傳進來的promise實例有狀態爲rejected,且其有catch方法,那麼就不會出發後面的Promise.all()的catch方法,會觸發then()方法。

Promise.race()

將現有對象轉換爲Promise對象,只要p一、p二、p3之中有一個實例率先改變狀態,p的狀態就跟着改變。那個率先改變的 Promise 實例的返回值,就傳遞給p的回調函數。

pomise如何實現鏈式調用?以前jquery也是能夠鏈式調用,他們以前的區別?

promise如何實現鏈式調用

在每一次執行Promise的then或者catch方法後,返回的都是一個新的Promise實例化對象,使得又一次可使用then或者catch方法,而且能夠拿到上一次then方法return回來的值。

例如:

then內部的函數return了一個數值a。咱們發現then方法執行完,返回的是一個新的promise實例,其promisevalue爲a

根據以上鋪墊,咱們就知道,其鏈式調用就是經過then和catch方法內部return new Promise()

jquery如何實現鏈式調用

經過return this,在其原型方法執行結束都會teturn其自己

var Jquery = function () {

}
Jquery.prototype = {
  css: function (attr, data) {
    return this
  },
  hide: function () {
    console.log('hide')
    return this
  }
}
var jq = new Jquery()
jq.css().hide()
複製代碼

這裏jq調用了css方法,首先會在jq上找css方法,沒有找到,而後就去其構造函數Jquery的原型中找,找到css方法並執行,在原型css方法裏return this中的this指向的是jq

二者區別

promise執行了then或者catch方法後,每一次返回的是一個新的Promise實例,jquey一直返回的都是其自己

promise的then方法是如何肯定在前面resolve或者reject執行完拿到結果再執行的

這裏學習了一下別人寫的promise,經過這個學習到了一些,中間關於這部分的代碼

const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
// promise 對象上有resolve,reject,all,race等方法,原型上有then,catch,finally方法
class myPromise {
  constructor (fn) {
    // 首先須要判斷fn是不是一個函數
    if (typeof fn != 'function') return 'fn is not function'
    this.PromiseStatus = PENDING
    this.PromiseValue = null
    this.PromiseReason = null
    this.onResolvedCallbacks = []
    this.onRejectedCallbacks = []
    const resolve = value => { // 代碼塊一
      if(value instanceof Promise) {
        return value.then(resolve, reject);
      }
      // new Promise構造函數傳進來的函數fn會當即執行,可是在new Promise內resolve和reject是異步操做(如何作到異步操做 =>使用setTimeout確保new Promise後的then和catch方法會在fn函數執行完才觸發)
      setTimeout(() => {
        if (this.PromiseStatus == PENDING) {
          this.PromiseStatus = RESOLVED
          this.PromiseValue = value
          this.onResolvedCallbacks.map(cb => {
            this.PromiseValue = cb(this.PromiseValue)
          })
        }
      })
    }
    const reject = value => {
      setTimeout(() => {
        if (this.PromiseStatus == PENDING) {
          this.PromiseStatus = REJECTED
          this.PromiseReason = value
          this.onRejectedCallbacks.map(cb => {
            this.PromiseReason = cb(this.PromiseReason)
          })
        }
      })
    } 
    try { // 代碼塊二
      // 當即執行new Promise內的
      fn(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }
  then (onResolved, onRejected) { // 代碼塊三
    onResolved = typeof onResolved === 'function' ? onResolved : function (v) { return v}
    onResolved = typeof onRejected === 'function' ? onRejected : function (v) { return v}
    console.log('this.PromiseStatus', this.PromiseStatus)
    if (this.PromiseStatus === RESOLVED) { // 代碼塊三 條件一
      return new myPromise((resolve, reject) => {
        // 何時會出現resolve一進來就變成RESOLVED或者REJECTED? 同一個Promise實例被屢次調用其then時,這樣會走下面的邏輯,爲肯定then內部仍是會異步執行,使用setTimeout
        setTimeout(() => {
          try {
            // 執行then內部的方法
            let x = onResolved(this.PromiseValue)
            resolvePromise(newPromise, x, resolve, reject);
          } catch (err) {
            reject(err)
          }
        })
      })
    }
    if (this.PromiseStatus === REJECTED) { // 代碼塊三 條件二
      return new myPromise((resolve, reject) => {
        setTimeout(() => {
          try {
            let x = onRejected(this.PromiseReason)
            resolvePromise(newPromise, x, resolve, reject);
          } catch (err) {
            reject(err)
          }
        })
      })
    }
    // 當沒有觸發resolve或者reject時,將onResolved和onRejected存放在onResolvedCallbacks/onRejectedCallbacks集合中
    if (this.PromiseStatus === PENDING) { // 代碼塊三 條件三
      return new myPromise ((resolve, reject) => {
        this.onResolvedCallbacks.push(value => {
          try {
            let x = onResolved(value)
            resolvePromise(newPromise, x, resolve, reject);
          } catch (err) {
            reject(err)
          }
        })
        this.onRejectedCallbacks.push(reason => {
          try {
            let x = onRejected(reason)
            resolve(x)
          } catch (err) {
            reject(err)
          }
        })
      })
    }
  }
}
複製代碼

根據以上代碼,咱們進行簡單的分析:

new myPromise(function (resolve, reject) {
    console.log(1)
    resolve(2)
}).then(function (success) {
    console.log(success)
})
複製代碼

上面的代碼中,首先構造函數初始化一些值

this.PromiseStatus = PENDING // 初始promise的狀態
this.PromiseValue = null // 初始promise resolve狀態時的value值
this.PromiseReason = null // 初始promise reject狀態時的value值
this.onResolvedCallbacks = [] // 存儲resolved狀態對應的onResolvedCallbacks事件數組
this.onRejectedCallbacks = [] // 存儲rejected狀態對應的onRejectedCallbacks事件數組
複製代碼

例如上面簡單的例子:其經歷的步驟有:

  1. 首先進入myPromise構造函數內,進入代碼塊二,當即執行myPromise傳進來的函數fn,這裏PromiseStatus=PENDING

    try { // 代碼塊二
        // 當即執行new Promise內的
        fn(resolve, reject)
    } catch (err) {
        reject(err)
    }
    複製代碼
  2. 執行fn時,進一步執行resolve函數,進入代碼塊一,這裏首先對resolve傳進來的參數進行了類型判斷,而後咱們能夠看到setTimeout代碼,這裏將setTimeout內部的事件放在eventLoop的最後。這裏PromiseStatus=PENDING

    setTimeout(() => {
        if (this.PromiseStatus == PENDING) {
          this.PromiseStatus = RESOLVED
          this.PromiseValue = value
          this.onResolvedCallbacks.map(cb => {
            this.PromiseValue = cb(this.PromiseValue)
          })
        }
      })
    複製代碼
  3. 觸發myPromise的then方法,進入到代碼塊三,進行條件判斷,進入到代碼塊三的 條件三,返回一個新的myPromise實例,這裏會當即執行下面代碼

    this.onResolvedCallbacks.push(value => {
        try {
            let x = onResolved(value)
            resolvePromise(newPromise, x, resolve, reject);
        } catch (err) {
            reject(err)
        }
    })
    複製代碼

    這個操做就是向onResolvedCallbacks裏push一個函數

  4. 上述操做結束後,再執行前面放在eventLoop最後的事件

    setTimeout(() => {
        if (this.PromiseStatus == PENDING) {
          this.PromiseStatus = RESOLVED
          this.PromiseValue = value
          this.onResolvedCallbacks.map(cb => {
            this.PromiseValue = cb(this.PromiseValue)
          })
        }
    })
    複製代碼

    這裏找到更改了PromiseStatus的狀態,並執行onResolvedCallbacks數組中的函數,這個函數是以前代碼塊三中的

    value => {
        try {
            let x = onResolved(value)
            resolvePromise(newPromise, x, resolve, reject);
        } catch (err) {
            reject(err)
        }
    }
    複製代碼

因此,promise的then方法是如何肯定在前面resolve或者reject執行完拿到結果再執行的,分兩種狀況:

a = new Promise((resolve, reject) => {
    resolve(1)
})
複製代碼
  • 對a只調用了一次then方法:這裏進入then方法,判斷狀態爲pending,直接返回一個new Promise(),像存放resolve狀態的數組內push一個函數f。在全部同步操做完成後,在eventLoop的末尾執行resolve內部的函數,從resolve狀態的數組中取出函數f並執行。
  • 對a被調用屢次then方法:在第二次調用then方法時,前面的promise狀態已經變爲resolved或者reject,這樣,進入then方法,判斷狀態爲resolved或者reject,就setTimeout,將then內部的函數放在eventLoop的末尾。
相關文章
相關標籤/搜索