真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

JavaScript的異步編程之Promise

Promise

一種更優(yōu)的異步編程統(tǒng)一 方法,如果直接使用傳統(tǒng)的回調(diào)函數(shù)去完成復(fù)雜操作就會形成回調(diào)深淵

網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)!專注于網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、成都微信小程序、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了東蘭免費(fèi)建站歡迎大家使用!

// 回調(diào)深淵
$.get('/url1'() => {
  $.get('/url2'() => {
    $.get('/url3'() => {
      $.get('/url4'() => {
        $.get('/url5'() => {
          // 大概就是這樣子的
        })
      })
    })
  })
})

CommonJS 社區(qū)提出了 Promise 規(guī)范,在ES2015中被標(biāo)準(zhǔn)化,成為語言規(guī)范。當(dāng)?shù)却隣顟B(tài)改編程成功或者失敗之后就再也不能再被改變了,成功的時(shí)候觸發(fā)onFulfilled 回調(diào),失敗的時(shí)候觸發(fā)onRejected 回調(diào)

Promise 簡單使用

new Promise 傳入一個(gè)回調(diào)函數(shù),這個(gè)回調(diào)函數(shù)兩個(gè)參數(shù),第一個(gè)把Promise 改成為成功的狀態(tài),第二個(gè)參數(shù)把Promise改變成失敗的狀態(tài),捕獲成功和異常可以使用.then.catch方法,這兩個(gè)方法返回的也是一個(gè)Promise對象

// 演示
const promsie = new Promise((resolve, reject) => {
  reject(1)
})

promsie.then((value) => {
  console.log(value)
}, (err) => {
  // end 執(zhí)行完之后才會執(zhí)行這個(gè)
  console.log(err)
})

// end 會先執(zhí)行
console.log('end')

不管Promise中有沒有異步操作,then方法中的回調(diào)函數(shù)依然會進(jìn)入回調(diào)隊(duì)列中排隊(duì),會等同步代碼執(zhí)行完之后才會執(zhí)行

Promise寫一個(gè)請求函數(shù)

function ajax (url) {
  return new Promise((resove, reject) => {
    var xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    // 新方法可以直接接受一個(gè)j對象
    xhr.responseType = 'json'
    xhr.onload = function () {
      if (this.status === 200) {
        resove(this.response)
      } else {
        reject(new Error(this.statusText))
      }
    }
    xhr.send()
  })
}

ajax('/json1.json').then(ret => {
  console.log(ret)
}).catch(err => {
  console.log(err)
})

如果需要多個(gè)連續(xù)的請求可以使用鏈?zhǔn)秸{(diào)用

ajax('/json1.json').then(ret => {
  return ajax('/json2.json')
}).then(ret => {
  return ajax('/json3.json')
}).then(ret => {
  return ajax('/json4.json')
})

這種鏈?zhǔn)秸{(diào)用是不是很熟悉,在jqeury中也有鏈?zhǔn)秸{(diào)用,jquery中是返回了本身這個(gè)對象所以可以實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,那么在Promise中是不是這樣呢

 let promsie1 = ajax('/json1.json')
    
 let promise2 = promsie1.then(ret => {
   console.log(ret)
 }).catch(err => {
   console.log(err)
 })

 console.log(promsie1 === promise2) // false

let a  = $("body").attr('class', 'body')
let b = a.prop('disabled', true)
console.log(a === b) // true

經(jīng)過測試發(fā)現(xiàn),Promise返回的是一個(gè)全新的Promise對象,返回全新的Promise對象的目的就是為了實(shí)現(xiàn)Promise的鏈條,每個(gè).then方法負(fù)責(zé)不同的任務(wù),互不干擾,如果不斷的鏈?zhǔn)秸{(diào)用then方法,這里的每個(gè)then方法都在為上一個(gè)then方法返回的Promise對象去添加狀態(tài)明確后的回調(diào),這些Promise會依次執(zhí)行,而且我們可以在then方法中去手動(dòng)返回一個(gè)Promise回調(diào)。如果then方法中的回調(diào)函數(shù)返回了值,則會給下一個(gè)then方法的回調(diào)函數(shù)傳遞這個(gè)返回的值,如果沒有返回那么默認(rèn)返回的就是undefined總結(jié)一下就是

  • Promise對象的then方法會返回一個(gè)全新的Promise對象
  • 后面的then方法就是在為上一個(gè)then返回的Promise注冊回調(diào)
  • 前面的then方法中的回調(diào)函數(shù)的返回值回作為后面then方法回調(diào)的參數(shù)
  • 如果回調(diào)中返回的是Promise, 那后面的then方法的回調(diào)會等待他的結(jié)束

捕獲異常

onRejected 回調(diào)會在Promise執(zhí)行異?;蛘邟伋龅漠惓r(shí)觸發(fā), 捕獲異常有兩種方式,第一種, then(成功處理的回調(diào)函數(shù), 異常處理的回調(diào)函數(shù))then方法中傳遞兩個(gè)回調(diào)函數(shù),第二種用.catch 方法去捕獲異常,catch方法其實(shí)就是then方法的別名,相當(dāng)于then方法第一個(gè)參數(shù)傳undefined

// then(成功處理的回調(diào)函數(shù), 異常處理的回調(diào)函數(shù))
ajax('/json1.json').then(ret => {
 	console.log(err)
}, err => {
  console.log(err)
})


// catch
ajax('/json1.json').then(ret => {
 	console.log(err)
}).catch(err => {
  console.log(err)
})
// catch
ajax('/json1.json').then(ret => {
 	console.log(err)
}).then(undefined,err => {
  console.log(err)
})

這兩種方式還是有很大的差異,catch 其實(shí)是在給上一個(gè)then返回的Promise 捕獲異常,但是如果是同一個(gè)鏈條下的Promise的錯(cuò)誤會向下傳遞直到有catch方法捕獲,而then方法傳遞兩個(gè)回調(diào)函數(shù)的捕獲異常的方式只會捕獲誰上一個(gè)Promise的錯(cuò)誤

ajax('/json1.json').then(ret => {
  console.log(ret)
}).then(undefined, err => {
  console.log(err)
}).then(ret => {
  console.log(ret)
}).then(ret => {
  console.log(ret)
})

// catch 捕獲異常
ajax('/json1.json').then(ret => {
  console.log(ret)
}).catch(err => {
  // 這里能捕獲之前的所有Promise的異常
})

// 傳遞then 第二個(gè)參數(shù)捕獲異常
ajax('/json1.json').then(ret => {
  console.log(ret)
}).then(undefined, err => {
  console.log(err)
  throw new Error('故意的異常')
}, (err) => {
  // 這里能捕獲故意的錯(cuò)誤
}).then(ret => {
  console.log(ret)
}).then(ret => {
  console.log(ret)
}).catch(err => {
  // 這個(gè)時(shí)候已經(jīng)捕獲不到異常了,因?yàn)樯弦粋€(gè)故意的異常已經(jīng)被捕獲了,根據(jù)then方法會返回一個(gè)Promise所以捕獲異常之后會返回一個(gè)成功的Promise
})

還可以全局捕獲異常, 這種全局方式捕獲異常是不推薦使用的,應(yīng)該在代碼塊中明確的去捕獲對應(yīng)的異常

// 瀏覽器環(huán)境中
window.addEventListener('unhandledrejection', event => {
  console.log(event.reason, event.promise)
 	// reason 失敗原因,
  // promise 失敗的Promise
  event.preventDefault()
}, false)

// nodejs中 
process.on('unhandledRejection', (reason, promise) => {
  console.log(reason, promise)
  // reason 失敗原因,
  // promise 失敗的Promise
})

如果需要無論成功和錯(cuò)誤都需要執(zhí)行則可以用finally來實(shí)現(xiàn)

ajax('/json1.json')
  .then(ret => {
    console.log('成功執(zhí)行這個(gè)')
  }).catch(err => {
    console.log("失敗執(zhí)行這個(gè)")
  })
  .finally(function() {
    console.log("成功和失敗都會執(zhí)行這個(gè)")
});

Promise 靜態(tài)方法

Promise.resolve

快速的一個(gè)值轉(zhuǎn)化為一個(gè)Promise 對象, 這種方式和 new Promise 返回一個(gè)值是等價(jià)的

Promise.resolve({
  data: "hahah"
})

new Promise((resolve) => {
  resolve({
    data: "hahah"
  })
})

如果傳入的是一個(gè)Promise對象會原封不動(dòng)的把這個(gè)對象返回

function ajax (url) {
  return new Promise((resove, reject) => {
    var xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    // 新方法可以直接接受一個(gè)j對象
    xhr.responseType = 'json'
    xhr.onload = function () {
      if (this.status === 200) {
        resove(this.response)
      } else {
        reject(new Error(this.statusText))
      }
    }
    xhr.send()
  })
}

let promise1 = ajax('/url')

let promise2 = Promise.resolve(promise1)

console.log(promise1 === promise2) // true

如果傳入的是一個(gè)對象,并且這個(gè)對象也有一個(gè)跟Promise一樣的then方法,也就是說這個(gè)方也也可以接收到onFulfilled, onRejected 兩個(gè)回調(diào),并且可以調(diào)用回調(diào)傳遞參數(shù),這種有then方法的對象實(shí)現(xiàn)了一個(gè)thenable的接口,支持這種對象的原因是因?yàn)樵?code>Promise還沒有被普及之前,很多時(shí)候都是第三方的庫實(shí)現(xiàn)的Promise

Promise.resolve({
  then (onFulfilled, onRejected) {
    onFulfilled('123')
  }
}).then(ret => {
  console.log(ret) // 123
})

Promise.reject

快速創(chuàng)建一個(gè)一定是失敗的Promise對象,這個(gè)方法的參數(shù)就是Promise失敗的原因

Promise.reject("嘿嘿,這就是錯(cuò)誤的理由").catch(err => {
  console.log(err) // 嘿嘿,這就是錯(cuò)誤的理由
})

Promise.all

接收一個(gè)數(shù)組,這些元素都是一個(gè)Promise對象,這個(gè)方法會返回一個(gè)全新的Promise對象,當(dāng)內(nèi)部所有Promise的都完成之后Promise.all返回的Promise對象才會完成。這個(gè)時(shí)候Promise.all返回的Promise對象拿到的結(jié)果是一個(gè)數(shù)組,這個(gè)數(shù)組中包含了每一個(gè)Promise返回的結(jié)果。值得注意的是只有數(shù)組中的所有Promise都成功了結(jié)束了,Promise.all返回的Promise對象才會成功結(jié)束。如果數(shù)組中有一個(gè)Promise失敗的結(jié)束了,那么Promise.all返回的Promise對象也會以失敗的結(jié)束

Promise.all([
  ajax('/url1'),
  ajax('/url2'),
  ajax('/url3'),
  ajax('/url4'),
]).then(values => {
  console.log(values)
}).catch(err => {
  console.log(err)
})

Promise.race

Promise.all方法一樣也是接收一個(gè)數(shù)組,這些元素都是一個(gè)Promise對象,這個(gè)方法會返回一個(gè)全新的Promise對象,但是與Promise.all方法不同的是Promise.all是等待所有任務(wù)的結(jié)束而結(jié)束, Promise.race只會等待第一個(gè)結(jié)束的任務(wù)而結(jié)束

const request = ajax('/api/???')
const timeout = new Promise((resolve, reject) => {
  setTimeout(() =>  reject('timeout'), 5000);
})

Promise.race([
  request,
  timeout
]).then(ret => {
  console.log(ret)
}).catch(err => {
  console.log(err)
})

上面代碼中,如果接口在5秒之前接口返回了,那么我們可以正常的得到返回結(jié)果,如果5秒還沒有返回,那么請求就沒有辦法把結(jié)果返回回來了,因?yàn)?code>timeout這個(gè)Promise會在5秒后以失敗的方式結(jié)束,而Promise.race就是以第一個(gè)結(jié)束的Promise而結(jié)束

Promise.allSettled

Promise.all、Promise.race方法一樣也是接收一個(gè)數(shù)組,這些元素都是一個(gè)Promise對象,這個(gè)方法會返回一個(gè)全新的Promise對象,與他們不同的是無論這些Promise執(zhí)行是成功還是失敗都是等這些Promise都完成了之后才會完成,當(dāng)有多個(gè)彼此不依賴的異步任務(wù)成功完成時(shí),或者總是想知道每個(gè)promise的結(jié)果時(shí),通常使用它

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)));

// > "fulfilled"
// > "rejected"

Promise.any

Promise.race方法一樣也是接收一個(gè)數(shù)組,這些元素都是一個(gè)Promise對象,這個(gè)方法會返回一個(gè)全新的Promise對象,不同的是只要有一個(gè)Promise執(zhí)行是成功的就算成功,只有全部都失敗了才會失敗。這個(gè)全新的PromiseonFulfilled的回調(diào)函數(shù)的參數(shù)為第一個(gè)成功完成的Promise所傳遞的數(shù)據(jù)

const alwaysError = new Promise((resolve, reject) => {
  reject("失敗就失敗下一個(gè)成功");
});

const two = new Promise((resolve, reject) => {
  setTimeout(resolve, 30, "我是第二個(gè)完成的Promise");
});

const three = new Promise((resolve, reject) => {
  setTimeout(resolve, 70, "我是第三個(gè)個(gè)完成的Promise");
});

const one = new Promise((resolve, reject) => {
  setTimeout(resolve, 10, "我是最先完成的Promise");
});

Promise.any([two, three, alwaysError, one]).then((value) => {
  console.log(value); // 我是最先完成的Promise
  // 這個(gè)value是最先完成的Promise傳遞的值也就是=>我是最先完成的Promise
})

Promise 執(zhí)行時(shí)序問題

宏任務(wù),微任務(wù)

測試執(zhí)行順序

console.log('global start')

Promise.resolve().then(ret => {
  console.log('promise')
})

console.log('global end')
// outlog
// 1. global start
// 2. global end
// 3. promise

鏈?zhǔn)秸{(diào)用多個(gè)執(zhí)行看執(zhí)行順序

console.log('global start')

Promise.resolve().then(ret => {
  console.log('promise1')
}).then(ret => {
  console.log('promise2')
}).then(ret => {
  console.log('promise3')
})

console.log('global end')

// outlog
// 1. global start
// 2. global end
// 3. promise1
// 4. promise2
// 5. promise3

加入setTimeout

console.log('global start')

setTimeout(() => {
  console.log('settimeout')
}, 0);

Promise.resolve().then(ret => {
  console.log('promise1')
}).then(ret => {
  console.log('promise2')
}).then(ret => {
  console.log('promise3')
})

console.log('global end')
// 1. global start
// 2. global end
// 3. promise1
// 4. promise2
// 5. promise3
// 6. settimeout

沒想到吧,Promise的異步時(shí)序執(zhí)行優(yōu)點(diǎn)特殊。舉個(gè)例子、假如我們?nèi)ャy行ATM辦理存款,辦完之后突然想起要轉(zhuǎn)一筆賬,這時(shí)候肯定會直接辦理轉(zhuǎn)賬業(yè)務(wù),不會到后面重新排隊(duì)再轉(zhuǎn)賬。這個(gè)例子中我們排隊(duì)就像在javascipt中的等待執(zhí)行的任務(wù)一樣,我們隊(duì)伍中的每一個(gè)人都對應(yīng)著回調(diào)回列中的一個(gè)任務(wù)、?;卣{(diào)隊(duì)列中任務(wù)稱之為宏任務(wù),而宏任務(wù)執(zhí)行過程中可以臨時(shí)加上一些額外需求,這些額外的需求可以選擇作為一個(gè)新的宏任務(wù)進(jìn)行到隊(duì)列中排隊(duì)。上面的setTimeout就會作為宏任務(wù)再次到回調(diào)隊(duì)列中排隊(duì),也可以跟我們剛的例子一樣作為當(dāng)前任務(wù)的微任務(wù)直接在當(dāng)前任務(wù)結(jié)束之后立即執(zhí)行。Promise的回調(diào)會作為微任務(wù)執(zhí)行,會在本輪調(diào)用的末尾去執(zhí)行,所以說上面代碼會先打印promise1,promise2,promise3在打印settimeout

微任務(wù)是在后來才被引入到js中的,他的目的是為了提高整體的響應(yīng)能力,目前的絕大多數(shù)異步調(diào)用都是作為宏任務(wù)執(zhí)行。Promise、MutationObservernodejs 中的process.nextTick會作為微任務(wù)在本輪調(diào)用的末尾執(zhí)行

更多內(nèi)容微信公眾號搜索充饑的泡飯
小程序搜一搜開水泡飯的博客


本文名稱:JavaScript的異步編程之Promise
網(wǎng)頁鏈接:http://weahome.cn/article/dsojisi.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部