ES6 系列七:Promise

"Code tailor",爲前端開發者提供技術相關資訊以及系列基礎文章,微信關注「小和山的菜鳥們」公衆號,及時獲取最新文章。

前言

在開始學習以前,咱們想要告訴您的是,本文章是對阮一峯《ECMAScript6 入門》一書中 "Promise" 章節的總結,若是您已掌握下面知識事項,則可跳過此環節直接進入題目練習javascript

  • 什麼是 Promise?
  • Promise 如何建立和使用
  • 出現起因及優缺點

若是您對某些部分有些遺忘,👇🏻 已經爲您準備好了!前端

學習連接

Promise 的學習java

彙總總結

概念

一個 Promise 對象表明一個在這個 Promise 被建立出來時不必定已知的值。它讓您可以把異步操做最終的成功返回值或者失敗緣由和相應的處理程序關聯起來。 這樣使得異步方法能夠像同步方法那樣返回值:異步方法並不會當即返回最終的值,而是會返回一個 Promise,以便在將來某個時候把值交給使用者。

三種狀態:es6

  • 待定(pending): 初始狀態,既沒有被兌現,也沒有被拒絕。
  • 已兌現(fulfilled): 意味着操做成功完成。
  • 已拒絕(rejected): 意味着操做失敗。

建立和使用

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('foo')
  }, 300)
})

promise1
  .then((value) => {
    console.log(value) // expected output: "foo"
  })
  .catch((error) => {
    //發生錯誤時調用
    console.log(error)
  })
  .finally(() => {
    //不管正確或錯誤,都會執行
    console.log('執行完畢')
  })

console.log(promise1)
// expected output: [object Promise]
  • new Promise (executor)
建立一個新的 Promise 對象。該構造函數主要用於包裝尚未添加 promise 支持的函數。參數( executor)爲兩個: resolve 函數,運算成功完成時調用; reject 函數,出錯時調用
const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('foo')
  }, 300)
})

promise1.then((value) => {
  console.log(value)
  // expected output: "foo"
})

console.log(promise1)
// expected output: [object Promise]

經常使用方法

  • Promise.all(iterable)
Promise.all() 方法接收一個 Promise 的 iterable 類型(注: ArrayMapSet 都屬於 ES6iterable 類型)的輸入,而且只返回一個 Promise 實例。 resolve 回調執行是在全部輸入的 Promiseresolve 回調都結束,或者輸入的 iterable 裏沒有 Promise 了的時候。reject 回調執行是隻要任何一個輸入的 Promisereject 回調執行或者輸入不合法的 Promise 就會當即拋出錯誤,而且 reject 的是第一個拋出的錯誤信息。
const promise1 = Promise.resolve(3)
const promise2 = 42
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo')
})

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values)
})
// expected output: Array [3, 42, "foo"]
  • Promise.allSettled(iterable)
Promise.allSettled()方法返回一個在全部給定的 Promise 都已經 fulfilledrejected 後的 Promise,並帶有一個對象數組,每一個對象表示對應的 Promise 結果。當您有多個彼此不依賴的異步任務成功完成時,或者您老是想知道每一個 Promise 的結果時,一般使用它。
const promise1 = Promise.resolve(3)
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'))
const promises = [promise1, promise2]

Promise.allSettled(promises).then((results) =>
  results.forEach((result) => console.log(result.status)),
)

// expected output:
// "fulfilled"
// "rejected"
  • Promise.race(iterable)
Promise.race(iterable) 方法返回一個 Promise,一旦迭代器中的某個 Promise 解決或拒絕,返回的 Promise 就會解決或拒絕。
const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one')
})

const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'two')
})

Promise.race([promise1, promise2]).then((value) => {
  console.log(value)
  // Both resolve, but promise2 is faster
})
// expected output: "two"

出現起因及優缺點

優勢:ajax

  • 狀態改變後,就不會再變,任什麼時候候均可以獲得這個結果
  • 能夠將異步操做以同步操做的流程表達出來,避免了層層嵌套的回調函數
  • 必定程度上解決了回調地獄的可讀性問題

缺點:數組

  • 沒法取消 promise
  • 當處於 pending 狀態時,沒法得知目前進展到哪個階段
  • 代碼冗餘,有一堆任務時也會使語義不清晰

異步 callback 帶來的」回調地獄「,邏輯混亂,耦合性高,可讀性差催生了 Promise,更多知識請點擊這裏JavaScript 的異步發展史promise

題目自測

一:如下代碼最後輸出什麼?微信

const first = () =>
  new Promise((resolve, reject) => {
    console.log(3)
    let p = new Promise((resolve, reject) => {
      console.log(7)
      setTimeout(() => {
        console.log(5)
        resolve(6)
      }, 0)
      resolve(1)
    })
    resolve(2)
    p.then((arg) => {
      console.log(arg)
    })
  })

first().then((arg) => {
  console.log(arg)
})
console.log(4)

二:紅燈三秒亮一次,綠燈一秒亮一次,黃燈 2 秒亮一次;如何讓三個燈不斷交替重複亮燈?(用 Promise 實現)三個亮燈函數已經存在:異步

function red() {
  console.log('red')
}
function green() {
  console.log('green')
}
function yellow() {
  console.log('yellow')
}

三:實現 mergePromise 函數,把傳進去的數組按順序前後執行,而且把返回的數據前後放到數組 data 中。函數

const timeout = (ms) =>
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve()
    }, ms)
  })

const ajax1 = () =>
  timeout(2000).then(() => {
    console.log('1')
    return 1
  })

const ajax2 = () =>
  timeout(1000).then(() => {
    console.log('2')
    return 2
  })

const ajax3 = () =>
  timeout(2000).then(() => {
    console.log('3')
    return 3
  })

const mergePromise = (ajaxArray) => {
  // 在這裏實現你的代碼
}

mergePromise([ajax1, ajax2, ajax3]).then((data) => {
  console.log('done')
  console.log(data) // data 爲 [1, 2, 3]
})

// 要求分別輸出
// 1
// 2
// 3
// done
// [1, 2, 3]

題目解析

1、

Answer:

// 運行結果:

// => 3
// => 7
// => 4
// => 1
// => 2
// => 5

這道題主要理解 js 執行機制。

第一輪事件循環,先執行宏任務,主 script,new Promise 當即執行,輸出 3,執行 p 這個 new Promise 操做,輸出 7,發現 setTimeout ,將回調函數放入下一輪任務隊列(Event Quene),p 的 then ,暫且命名爲 then1,放入微任務隊列,且 first也有 then,命名爲 then2,放入微任務隊列。執行 console.log(4) ,輸出 4,宏任務執行結束。

再執行微任務,執行 then1 ,輸出 1,執行 then2 ,輸出 3.

第一輪事件循環結束,開始執行第二輪。第二輪事件循環先執行宏任務裏面的,也就是 setTimeout 的回調,輸出 5.resolve(6) 不會生效,由於 p 的 Promise 狀態一旦改變就不會再變化了。

2、

Answer:

var light = function (timmer, cb) {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      cb()
      resolve()
    }, timmer)
  })
}

var step = function () {
  Promise.resolve()
    .then(function () {
      return light(3000, red)
    })
    .then(function () {
      return light(2000, green)
    })
    .then(function () {
      return light(1000, yellow)
    })
    .then(function () {
      step()
    })
}

step()

紅燈三秒亮一次,綠燈一秒亮一次,黃燈 2 秒亮一次,意思就是 3 秒,執行一次 red 函數,2 秒執行一次 green 函數,1 秒執行一次 yellow 函數,不斷交替重複亮燈,意思就是按照這個順序一直執行這 3 個函數,這步能夠就利用遞歸來實現。

固然,採用其餘方法能完成所示效果,方法不少,這邊只提供一種方式。

3、

Answer:

// 保存數組中的函數執行後的結果
var data = []

// Promise.resolve方法調用時不帶參數,直接返回一個resolved狀態的 Promise 對象。
var sequence = Promise.resolve()

ajaxArray.forEach(function (item) {
  // 第一次的 then 方法用來執行數組中的每一個函數,
  // 第二次的 then 方法接受數組中的函數執行後返回的結果,
  // 並把結果添加到 data 中,而後把 data 返回。
  // 這裏對 sequence 的從新賦值,實際上是至關於延長了 Promise 鏈
  sequence = sequence.then(item).then(function (res) {
    data.push(res)
    return data
  })
})

// 遍歷結束後,返回一個 Promise,也就是 sequence, 他的 [[PromiseValue]] 值就是 data,
// 而 data(保存數組中的函數執行後的結果) 也會做爲參數,傳入下次調用的 then 方法中。
return sequence

首先 ajax1ajax2ajax3 都是函數,只是這些函數執行後會返回一個 Promise,按題目的要求咱們只要順序執行這三個函數就行了,而後把結果放到 data 中,可是這些函數裏都是異步操做,想要按順序執行,而後輸出 1,2,3 並無那麼簡單,看個例子。

function A() {
  setTimeout(function () {
    console.log('a')
  }, 3000)
}

function B() {
  setTimeout(function () {
    console.log('b')
  }, 1000)
}

A()
B()

// b
// a

例子中咱們是按順序執行的 A,B 可是輸出的結果倒是 b,a 對於這些異步函數來講,並不會按順序執行完一個,再執行後一個。這道題就是考用 Promise 控制異步流程,咱們要想辦法,讓這些函數,一個執行完以後,再執行下一個。

相關文章
相關標籤/搜索