ES6 promise 用法小結(jié)

ES6 promise 用法小結(jié)

Js 是一?單線程語言纷闺,早期解決異步問題佃乘,大部分是通過回調(diào)函數(shù)進行犬金。

比如我們發(fā)送 ajax 請求念恍,就是常見的一個異步場景,發(fā)送請求后晚顷,一段時間服務(wù)器給我們響應(yīng)峰伙,然后才拿到結(jié)果。如果我們希望在異步結(jié)束之后執(zhí)行某個操作该默,就只能通過回調(diào)函數(shù)的方式進行操作

const request = function (callback) {
  setTimeout(function () {
    callback()
  }, 1000)
}
request(function () {
  console.log(123)
})
// 以上代碼執(zhí)行結(jié)果:1s 后輸出 123
// request 就是一個異步函數(shù)瞳氓,里面執(zhí)行的 setTimeout 會在 1s 之后調(diào)用傳入的 callback 函數(shù), 
// 如果后續(xù)還有內(nèi)容需要在異步函數(shù)結(jié)束時輸出,就需要多個異步函數(shù)進行嵌套栓袖,非常不利于后續(xù)的維護顿膨。
setTimeout(function () {
  console.log(123)
  setTimeout(function () {
    console.log(321)
    // ...
  }, 2000)
}, 1000)

為了使回調(diào)函數(shù)以更優(yōu)雅的方式進行調(diào)用锅锨,在 ES6 中引入了 promise,讓異步 操作的變得「同步化」恋沃。

1,Promise 基礎(chǔ)

通過 new Promise() 即可構(gòu)造一個 promise 實例必指,這個構(gòu)造函數(shù)接受一個函數(shù)囊咏,接受兩個參數(shù):resolvereject,代表改變實例的狀態(tài)到 已完成 或是 已拒絕

const promise = new Promise(function (resolve, reject) {
  console.log('promise called')
  setTimeout(function () {
    resolve()
  }, 3000)
})

promise.then(function () {
  console.log('promise resolve callback')
})

// 先打印出 promise called塔橡, 3s 后打印 promise resolve callback
function promise1() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log('1s后輸出')
      resolve()
    }, 1000)
  })
}

function promise2() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log('2s后輸出')
      resolve()
    }, 2000)
  })
}
// 以下兩個promise實例梅割,串聯(lián)起來即可寫為:
promise1().then(function () {
  return promise2()
})
或
promise1().then(promise2)

控制臺打印結(jié)果:1s之后出現(xiàn) 1s后輸出,再經(jīng)過2s出現(xiàn)2s后輸出葛家。實例中户辞,當(dāng)前promise如果狀態(tài)變?yōu)橐淹瓿?執(zhí)行resolve方法),就會去執(zhí)行 then 方法中的下一個 promise 函數(shù)癞谒。同樣的如果promise變成已拒絕狀態(tài)(執(zhí)行reject方法)底燎,就會進入后續(xù)的異常處理函數(shù)中。

function promise3() {
  return new Promise(function (resolve, reject) {
    var random = Math.random() * 10 // 隨機一個 1 - 10的數(shù)字
    setTimeout(function () {
      if (random >= 5) {
        resolve(random) // 把隨機生成的數(shù)字傳給了 resolve, 在 then 中可以拿到這個值
      } else {
        reject(random)  // 把隨機生成的數(shù)字傳給了 reject弹砚,在 then 中可以拿到這個值
      }
    }, 1000)
  })
}

var onResolve = function (val) {  
  console.log('已完成:輸出的數(shù)字是:', val)
}

var onReject = function (val) {
  console.log('已拒絕:輸出的數(shù)字是:', val)
}

// promise 的then也可以接受兩個參數(shù)双仍,第一個參數(shù)是 resolve 后執(zhí)行的,第二個參數(shù)是 reject 后執(zhí)行的
promise3().then(onResolve, onReject)

// 也可以通過 .catch 方法攔截狀態(tài)變?yōu)橐丫芙^時的 promise
promise3().catch(onReject).then(onResolve)

// 也可以通過 try catch 進行攔截狀態(tài)變?yōu)橐丫芙^的 promise
try {
  promise3().then(onResolve)
} catch (e) {
  onReject(e)
}

以上使用3種方式攔截最終變?yōu)椤敢丫芙^」?fàn)顟B(tài)的 promise桌吃,分別是使用 then 的第二個參數(shù)朱沃,使用 .catch 方法捕獲前方 promise 拋出的異常,使用 try catch 攔截代碼塊中 promise 拋出的異常

我們可以發(fā)現(xiàn)茅诱,在改變 promise 狀態(tài)時調(diào)用 resolvereject 函數(shù)的時候逗物,可以給下一步 then 中執(zhí)行的函數(shù)傳遞參數(shù)。

2瑟俭,封裝異步操作為promise

我們可以將任何接受回調(diào)的函數(shù)封裝為一個promise, 實例:

// 原函數(shù)
function func(callback) {
  setTimeout(function () {
    console.log('1s 后顯示')
    callback()
  }, 1000)
}

var callback = function () {
  console.log('在異步結(jié)束后打印')
}
// 用傳入回調(diào)函數(shù)的方式執(zhí)行
func(callback)

image

以上實例是最傳統(tǒng)的使用傳入回調(diào)函數(shù)的方式在異步結(jié)束后執(zhí)行函數(shù)翎卓。我們可以通過封裝 promise的方式,將這個異步函數(shù)變?yōu)?promise:

function func() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log('1s 后顯示')
      resolve()
    })
  })
}

var callback = function () {
  console.log('在異步結(jié)束后打印')
}

func().then(function () {
  callback()
})

再比如尔当,我們發(fā)送 ajax 請求也可以封裝為 promise:

function ajax(url, success, fail) {
  var client = new XMLHttpRequest();
  client.open('GET', url)
  client.onreadystatechange = function () {
    if (this.readyState !== 4) {
    // this.readyState擴展:
    // 0: 未初始化莲祸,還沒調(diào)用 send() 方法
    // 1: 載入,已調(diào)用send()方法椭迎,正在發(fā)送請求
    // 2: 載入完成锐帜,send()執(zhí)行完畢,已接受全部響應(yīng)內(nèi)容
    // 3: 交互畜号,正在解析響應(yīng)內(nèi)容
    // 4: 完成缴阎,響應(yīng)內(nèi)容解析完成,可以直接使用responseText數(shù)據(jù)
      return
    }
    if (this.status === 200) {
      success(this.response)
    } else {
      fail(new Error(this.statusText))
    }
  }
  client.send()
}

ajax('http://localhost:8080/home/swiper', function (res) {
  console.log('成功')
  console.log(res)
}, function (err) {
  console.log('失敗', err)
})

image

以上 ajax 請求简软,通過封裝 promise 的方式蛮拔,在原來的執(zhí)行回調(diào)函數(shù)的地方述暂,更改當(dāng)前 promise的狀態(tài),就可以通過鏈?zhǔn)秸{(diào)用:

function ajax(url) {
  return new Promise(function (resolve, reject) {
    var ct = new XMLHttpRequest();
    ct.open('GET', url)
    ct.onreadystatechange = function () {
      if (this.readyState !== 4) {
        return
      }
      if (this.status === 200) {
        resolve(this.response)
      } else {
        reject(new Error(this.statusText))
      }
    }
    ct.send()
  })
}

ajax('http://localhost:8080/home/swiper').catch(function () {
  console.log('失敗')
}).then(function (res) {
  console.log('成功')
  console.log(res)
})

我們可以把任何一個函數(shù)或者是異步函數(shù)改為promise建炫,尤其是異步函數(shù)畦韭,改為 promise中后即可進行鏈?zhǔn)秸{(diào)用,增強可讀性

3肛跌,小總結(jié)

  • 1艺配,promise 有三種狀態(tài),進行中(Pending) 衍慎、已完成(Fulfilled)转唉、已拒絕(Rejected),進行中狀態(tài)可以更改為已完成 或 已拒絕稳捆,已經(jīng)更改過狀態(tài)后無法繼續(xù)更改(例如從已完成改為已拒絕)赠法。

  • 2,ES6 中的 Promise 構(gòu)造函數(shù)乔夯,我們構(gòu)造之后需要傳入一個函數(shù)砖织,他接受兩個函數(shù)參數(shù),執(zhí)行第一個參數(shù)之后就會改變當(dāng)前 promise 為已完成狀態(tài)驯嘱,執(zhí)行第二個參數(shù)之后就會變?yōu)?已拒絕 狀態(tài)镶苞。

  • 3,必須有一個then方法用以訪問其當(dāng)前值和原因鞠评。promise的 then 方法接受兩個參數(shù):promise.then(onFulfilled, onRejected) 他們都是可選參數(shù)茂蚓,他們都是函數(shù)。如果 onFulfilledonRejected 不是函數(shù)剃幌,則需要忽略他們

  • 4聋涨,已拒絕的 promise,后續(xù)可以通過 .catch 方法或是 .then 方法的第二個參數(shù)或是 try catch 進行捕獲负乡。

  • 5牍白,then方法可以被同一個promise調(diào)用多次。

    • 當(dāng) promise 成功執(zhí)行的時候抖棘,所有的 onFulfilled 需按照其注冊順序依次回調(diào)
    • 當(dāng) promise 被拒絕執(zhí)行的時候茂腥,所有的 onRejected 需按照其注冊順序依次回調(diào)
      then 方法必須返回一個 promise 對象: promise2 = promise1.then(onFulfilled, onRejected)
    • 只要 onFulfilled 或者 onRejected 返回一個值 xpromise2 都會進入 onFulfilled 狀態(tài)
    • 如果 onFulfilled 或者 onRejected 拋出一個異常 e 切省,則 promise2 必須拒絕執(zhí)行最岗,并返回拒因 e
    • 如果 onFulfilled 不是函數(shù)且 promise1 狀態(tài)變?yōu)橐淹瓿桑?promise2 必須成功執(zhí)行并返回相同的值
    • 如果 onRejected 不是函數(shù)且 promise1 狀態(tài)變?yōu)橐丫芙^, promise2 必須執(zhí)行拒絕回調(diào)并返回相同的據(jù)因
var promise1 = new Promise((resolve, reject) => {
  reject()
})
promise1
  .then(null, function () {
    return 123
  })
  .then(null, null)
  .then(null, null)
  .then(
    () => {
      console.log('promise2 已完成')
    },
    () => {
      console.log('promise2 已拒絕')
    })

以上代碼輸出:promise2 已完成

以上代碼可改寫為:

var promise1 = new Promise(function (resolve, reject) { reject() })
var promise2 = promise1.then(null, function () { return 123 })
var promise3 = promise2.then(null, null) // 如果 onFulfilled 不是函數(shù)且 promise2 狀態(tài)變?yōu)橐淹瓿桑?promise3 必須成功執(zhí)行并返回和 promise2 相同的值, 即 123
var promise4 = promise3.then(null, null) // 同理朝捆,promise4 也能拿到 123 的值
promise4
  .then(val => {
    console.log('promise2 已完成', val)  // promise2 已完成 123
  }, () => {
    console.log('promise2 已拒絕')
  })

實例:

var promise1 = function () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(1)
      resolve()
    }, 1000)
  })
}

var promise2 = function () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(2)
      resolve()
    }, 2000)
  })
}

promise1()
  .then(function () {
    return promise2()  // 此處返回一個 promise 實例
  })
  .then(function () {
    console.log('已完成')
  }, function () {
    console.log('已拒絕')
  })
image

4, promise 構(gòu)造函數(shù)上的 靜態(tài)方法

  • 4.1, promise.resolve

返回一個 promise 實例般渡,并將它的狀態(tài)設(shè)置為已完成,同時將他的結(jié)果作為傳入 promise 實例的值

var promise = Promise.resolve(123)
promise.then(function (val) {
  console.log('已完成', val)
})
image

Promise.resolve 的參數(shù)也可以處理對象、函數(shù)等內(nèi)容

  • 4.2驯用,promise.reject

返回一個 promise 實例脸秽,并將它的狀態(tài)設(shè)置為已拒絕,同時也將他的結(jié)果作為原因傳入 onRejected 函數(shù)

var promise = Promise.reject(123)
promise.then(null, function (val) {
  console.log('已拒絕', val)
})
image
  • 4.3蝴乔,Promise.all

返回一個 promise 實例记餐,接受一個數(shù)組,里面含有多個 promise 實例薇正,當(dāng)所有 promise 實例都成 已完成 狀態(tài)時剥扣,進入已完成狀態(tài),否則進入已拒絕狀態(tài)铝穷。

var promise1 = function () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(1)
      resolve()
    }, 1000)
  })
}

var promise2 = function () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(2)
      resolve()
    }, 1000)
  })
}

Promise.all([promise1(), promise2()]).then(function () {
  console.log('全部 promise 均已完成')
})
image

以上代碼為多個 promise 同時進行,等待 1s 打印 1 之后佳魔,再等待 1s 就 會打印 2 和全部 promise 均已完成曙聂。

  • 4.4,Promise.race
    返回一個 promise 實例鞠鲜,接受一個數(shù)組宁脊,里面含有多個 promise 實例,當(dāng)有一個 promise 實例狀態(tài)改變時贤姆,就進入該狀態(tài)且不可改變榆苞。這里所有的 promise 實例為競爭關(guān)系,只選擇第一個進入改變狀態(tài)的 promise 的值霞捡。
var promise1 = function () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(1)
      resolve(1)
    }, 1000)
  })
}

var promise2 = function () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(2)
      resolve(2)
    }, 1000)
  })
}

Promise.race([promise1(), promise2()]).then(function (val) {
  console.log('有一個 promise 狀態(tài)已經(jīng)改變', val)
})
image

5, generator / async await

ES6 之后坐漏,我們可以使用 generator 和 async/await 來操作 promise,比起使用 promise 串行的調(diào)用來說碧信,從語法層面 讓調(diào)用關(guān)系 顯得更加串行赊琳。

function promise1() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(1)
      resolve()
    }, 1000)
  })
}

function promise2() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(2)
      resolve()
    }, 1000)
  })
}

// 使用 generator 函數(shù)
function* gen() {
  yield promise1()
  yield promise2()
}
var g = gen()
g.next()
g.next() // 1 2

// 使用 async/await 函數(shù)
(async function () {
  try {
    await promise1()
    await promise2()
    console.log('已完成')
  } catch (e) {
    console.log(e)
    console.log('已拒絕')
  }
}())  // 1 2 已完成
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市砰碴,隨后出現(xiàn)的幾起案子躏筏,更是在濱河造成了極大的恐慌,老刑警劉巖呈枉,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趁尼,死亡現(xiàn)場離奇詭異,居然都是意外死亡猖辫,警方通過查閱死者的電腦和手機酥泞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來住册,“玉大人婶博,你說我怎么就攤上這事。” “怎么了凡人?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵名党,是天一觀的道長。 經(jīng)常有香客問我挠轴,道長传睹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任岸晦,我火速辦了婚禮欧啤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘启上。我一直安慰自己邢隧,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布冈在。 她就那樣靜靜地躺著倒慧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪包券。 梳的紋絲不亂的頭發(fā)上纫谅,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天,我揣著相機與錄音溅固,去河邊找鬼付秕。 笑死,一個胖子當(dāng)著我的面吹牛侍郭,可吹牛的內(nèi)容都是我干的询吴。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼励幼,長吁一口氣:“原來是場噩夢啊……” “哼汰寓!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起苹粟,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤有滑,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后嵌削,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體毛好,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年苛秕,在試婚紗的時候發(fā)現(xiàn)自己被綠了肌访。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡艇劫,死狀恐怖吼驶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤蟹演,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布风钻,位于F島的核電站,受9級特大地震影響酒请,放射性物質(zhì)發(fā)生泄漏骡技。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一羞反、第九天 我趴在偏房一處隱蔽的房頂上張望布朦。 院中可真熱鬧,春花似錦昼窗、人聲如沸是趴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽右遭。三九已至,卻和暖如春缤削,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吹榴。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工亭敢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人图筹。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓帅刀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親远剩。 傳聞我的和親對象是個殘疾皇子扣溺,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 1. promise要解決的問題: 腦筋急轉(zhuǎn)彎:把牛關(guān)進冰箱里,要分幾步瓜晤? 很顯然锥余,這三個操作不能顛倒順序,否則任...
    月上秦少閱讀 1,596評論 0 3
  • Promise 對象 1.Promise對象是ES6對異步編程的一種解決方案痢掠,它有以下兩個特點: Promise對...
    Cryptic閱讀 998評論 1 5
  • 特點 Promise能將回調(diào)分離出來,在異步操作執(zhí)行之后,用鏈?zhǔn)椒椒▓?zhí)行回調(diào),雖然es5用封裝函數(shù)也能實現(xiàn),但是如...
    一二三kkxx閱讀 623評論 0 1
  • 什么是Promise Promise是異步編程的一種解決方案驱犹,比傳統(tǒng)的異步解決方案【回調(diào)函數(shù)】和【事件】更合理、更...
    前端的爬行之旅閱讀 301評論 0 0
  • Node的產(chǎn)生足画,大大推動了Javascript這門語言在服務(wù)端的發(fā)展雄驹,使得前端人員可以以很低的門檻轉(zhuǎn)向后端開發(fā)。 ...
    卻忘不掉你心言閱讀 302評論 0 0