JavaScript詳解使用Promise處理回調(diào)地獄與async?await修飾符
Promise
Promise能夠處理異步程序。
回調(diào)地獄
JS中或node中,都大量的使用了回調(diào)函數(shù)進(jìn)行異步操作,而異步操作什么時(shí)候返回結(jié)果是不可控的,如果我們希望幾個(gè)異步請(qǐng)求按照順序來執(zhí)行,那么就需要將這些異步操作嵌套起來,嵌套的層數(shù)特別多,就會(huì)形成回調(diào)地獄 或者叫做 橫向金字塔。
案例:有a.txt、b.txt、c.txt三個(gè)文件,使用fs模板按照順序來讀取里面的內(nèi)容,代碼:
// 將讀取的a、b、c里面的內(nèi)容,按照順序輸出
const fs = require('fs');
// 讀取a文件
fs.readFile('./a.txt','utf-8',(err,data)=>{
if(err) throw err;
console.log(data);
// 讀取b文件
fs.readFile('./b.txt','utf-8',(err,data)=>{
if(err) throw err;
console.log(data);
// 讀取c文件
fs.readFile('./c.txt','utf-8',(err,data)=>{
if(err) throw err;
console.log(data)
})
})
})案例中,循環(huán)嵌套的代碼越來越多,這就是 地獄回調(diào)
Promise簡(jiǎn)介
- Promise對(duì)象可以解決 回調(diào)地獄 的問題
- Promise是異步編程的一種解決方案,比傳統(tǒng)的解決方案(回調(diào)函數(shù)和事件)更合理更強(qiáng)大
- Promise可以理解為是一個(gè)容器,里面可以編寫異步程序的代碼
- 從語(yǔ)法上說,Promise是一個(gè)對(duì)象,使用的時(shí)候需要 new
Promise簡(jiǎn)單使用
Promise是“承諾”的意思,實(shí)例中,它里面的異步操作就相當(dāng)于一個(gè)承諾,而承諾就會(huì)有兩種結(jié)果,要么完成了承諾的內(nèi)容,要么失敗。
所以,使用Promise,分為兩大部分,首先是有一個(gè)承諾(異步操作),然后再兌現(xiàn)結(jié)果。
第一部分:定義"承諾"
// 實(shí)例化一個(gè)Promise,表示定義一個(gè)容器,需要給它傳遞一個(gè)函數(shù)作為參數(shù),而該函數(shù)又有兩個(gè)形參,通常用resolve和reject表示。該函數(shù)里面可以寫異步請(qǐng)求的代碼
// 換個(gè)角度,也可以理解為定下了一個(gè)承諾諾
let p = new Promise((resolve,reject)=>{
// 形參resolve,單詞意思是 完成
// 實(shí)參reject,單詞意思是 失敗
fs.readFile('./a.txt','utf-8',(err,data)=>{
if(err){
// 失敗,就告訴別人,承諾失敗了
reject(err);
}else{
// 成功,就告訴別人,承諾實(shí)現(xiàn)了
resolve(data);
}
}
})第二部分:獲取"承諾"的結(jié)果
// 通過調(diào)用 p 的then方法,可以獲取到上述 "承諾" 的結(jié)果
// then方法有兩個(gè)函數(shù)類型的參數(shù),參數(shù)1表示承諾成功時(shí)調(diào)用的函數(shù),參數(shù)2可選,表示承諾失敗時(shí)調(diào)用的函數(shù)
// p.then(
// (data)=>{}, // 函數(shù)類型的參數(shù),用于獲取承諾成功后的數(shù)據(jù)
// (err)=>{} // 函數(shù)類型的參數(shù),用于承諾失敗后的錯(cuò)誤信息
//)
p.then(
(data)=>{
console.log(data);
},
(err)=>{
console.log(err);
}
)三種狀態(tài)
- 最初狀態(tài):pending,等待中,此時(shí)的promise結(jié)果為undefined
- 當(dāng)resolve (value) 調(diào)用時(shí),達(dá)到最終狀態(tài)之一:fulfilled,(成功的)完成,此時(shí)可以獲取結(jié)果value
- 當(dāng)reject (error) 調(diào)用時(shí),達(dá)到最終狀態(tài)之一:rejected,失敗,此時(shí)可以獲取錯(cuò)誤信息error
當(dāng)達(dá)到最終的fulfilled 或 rejected 時(shí),promise的狀態(tài)就不會(huì)再改變了。
特點(diǎn)
當(dāng)調(diào)用 resolve的時(shí)候,Promise 將到達(dá)最終的狀態(tài)。 達(dá)到最終狀態(tài)之后,Promise的狀態(tài)就不會(huì)再改變了。
多次調(diào)用resolve函數(shù),只有第一次有效,其他的調(diào)用都無效。
const fs = require('fs');
// 1.創(chuàng)建Promise對(duì)象:(承諾)
let p = new Promise((resolve,reject)=>{
resolve(123);
resolve(456); // 這次的調(diào)用無效
});
// 2.獲取異步任務(wù)的結(jié)果
// p.then(函數(shù)1,[函數(shù)2]);
p.then(res =>{
console.log(res); // 123
},err =>{
console.log(err);
})then方法的鏈?zhǔn)秸{(diào)用
- 前一個(gè)then里面返回的字符串,會(huì)被下一個(gè)then方法接收到。但是沒有意義;
- 前一個(gè)then里面返回的Promise對(duì)象,并且調(diào)用resolve的時(shí)候傳遞了數(shù)據(jù),數(shù)據(jù)會(huì)被下一個(gè)then接收到
- 前一個(gè)then里面如果沒有調(diào)用resolve,則后續(xù)的then不會(huì)接收到任何值
const fs = require('fs');
// promise 承諾
let p1 = new Promise((resolve, reject) => {
fs.readFile('./a.txt', 'utf-8', (err, data) => {
err ? reject(err) : resolve(data.length);
});
});
let p2 = new Promise((resolve, reject) => {
fs.readFile('./b.txt', 'utf-8', (err, data) => {
err ? reject(err) : resolve(data.length);
});
});
let p3 = new Promise((resolve, reject) => {
fs.readFile('./c.txt', 'utf-8', (err, data) => {
err ? reject(err) : resolve(data.length);
});
});
p1.then(a => {
console.log(a);
return p2;
}).then(b => {
console.log(b); // 接收上面?zhèn)鬟f的值p2
return p3;
}).then(c => {
console.log(c) // 接收上面?zhèn)鬟f的值p3
}).catch((err) => {
console.log(err); // 如果錯(cuò)誤,打印報(bào)錯(cuò)信息
});catch方法可以統(tǒng)一獲取到 錯(cuò)誤信息
封裝按順序異步讀取文件的函數(shù)
function myReadFile(path) {
return new Promise((resolve, reject) => {
fs.readFile(path, 'utf-8', (err, data) => {
err ? reject(err) : resolve(data.length);
})
});
}
myReadFile('./a.txt')
.then(a => {
console.log(a);
return myReadFile('./b.txt');
})
.then(b => {
console.log(b);
return myReadFile('./c.txt');
})
.then(c => {
console.log(c)
})
.catch((err) => {
console.log(err);
});使用第三方模塊讀取文件
- npm init - y
- npm i then-fs 安裝then-fs模塊
- then-fs 將 內(nèi)置的fs模塊封裝了,讀取文件后,返回 Promise 對(duì)象,省去了我們自己封裝
// then-fs 模塊是第三方模塊,需要 npm install then-fs 下載安裝的
const fs = require('then-fs');
// then-fs 對(duì)內(nèi)置的fs模塊進(jìn)行了重寫的封裝。調(diào)用方法后,返回Promise對(duì)象
let p1 = fs.readFile('./files/a.txt', 'utf-8'); //
let p2 = fs.readFile('./files/b.txt', 'utf-8');
let p3 = fs.readFile('./files/c.txt', 'utf-8');
// 通過then獲取結(jié)果
p1.then(res => {
console.log(res.length);
return p2;
}).then(res => {
console.log(res.length);
return p3;
}).then(res => {
console.log(res.length);
})async和await 修飾符
ES6 — ES2015
async 和 await 是 ES2017 中提出來的。
異步操作是 JavaScript 編程的麻煩事,麻煩到一直有人提出各種各樣的方案,試圖解決這個(gè)問題。
從最早的回調(diào)函數(shù),到 Promise 對(duì)象,再到 Generator 函數(shù),每次都有所改進(jìn),但又讓人覺得不徹底。它們都有額外的復(fù)雜性,都需要理解抽象的底層運(yùn)行機(jī)制。
異步I/O不就是讀取一個(gè)文件嗎,干嘛要搞得這么復(fù)雜?異步編程的最高境界,就是根本不用關(guān)心它是不是異步。
async 函數(shù)就是隧道盡頭的亮光,很多人認(rèn)為它是異步操作的終極解決方案。
ES2017 提供了async和await關(guān)鍵字。await和async關(guān)鍵詞能夠?qū)惒秸?qǐng)求的結(jié)果以返回值的方式返回給我們。
async 用于修飾一個(gè) function
- async 修飾的函數(shù),總是返回一個(gè) Promise 對(duì)象
- 函數(shù)內(nèi)的返回值,將自動(dòng)包裝在 resolved 的 promise 中
await 只能出現(xiàn)在 async 函數(shù)內(nèi)
- await 讓 JS 引擎等待直到promise完成并返回結(jié)果
- 語(yǔ)法:let value = await promise對(duì)象; // 要先等待promise對(duì)象執(zhí)行完畢,才能得到結(jié)果
- 由于await需要等待promise執(zhí)行完畢,所以await會(huì)暫停函數(shù)的執(zhí)行,但不會(huì)影響其他同步任務(wù)
對(duì)于錯(cuò)誤處理,可以選擇在async函數(shù)后面使用 .catch() 或 在promise對(duì)象后使用 .catch()
const fs = require('fs');
// 將異步讀取文件的代碼封裝
function myReadFile (path) {
return new Promise((resolve, reject) => {
fs.readFile(path, 'utf-8', (err, data) => {
err ? reject(err) : resolve(data.length);
});
}).catch(err => {
console.log(err);
});
}
async function abc () {
let a = await myReadFile('./a.txt');
let b = await myReadFile('./b.txt');
let c = await myReadFile('./c.txt');
console.log(b);
console.log(a);
console.log(c);
}
abc();錯(cuò)誤處理
前提是得到幾個(gè)Promise對(duì)象,代碼如下:
let fs = require('fs');
let p1 = new Promise('./files/a.txt','utf-8');
let p2 = new Promise('./files/bbb.txt','utf-8'); // 注意:這里故意寫錯(cuò)路徑
let p3 = new Promise('./files/c.txt','utf-8');獲取Promise的結(jié)果,可以通過then獲取。也可以通過async和await獲取。
如果使用then獲取結(jié)果,那么錯(cuò)誤如何處理?在鏈?zhǔn)秸{(diào)用的尾端,加一個(gè)catch方法即可
// ----------------------------通過 then 獲取結(jié)果-----------------------------
p1.then(res=>{
console.log(res);
return p2;
}).then(res=>{
console.log(res);
return p3
}).catch(err=>{
console.log(err);
})如何使用async和await獲取結(jié)果,最好的錯(cuò)誤處理方案,就是使用 try...catch...
// ---------------------------通過 async/await 獲取結(jié)果--------------------
async function abc(){
try{ // 嘗試做一些事情
let r1 = await p1; // 正常獲取結(jié)果
let r2 = await p2; // 這里出錯(cuò)了,就會(huì)拋出錯(cuò)誤 throw err.
let r3 = await p3;
console.log(r1,r2,r3)
}catch(e){
console.log(e); // catch這里,會(huì)抓住前面try里面拋出的錯(cuò)誤
}
}
abc();小結(jié)
Promise是異步任務(wù)的一種解決方案
得到Promise對(duì)象
- 自己new Promise()
- 自己封裝函數(shù),函數(shù)中返回 Promise對(duì)象
- 使用第三方的模塊或者庫(kù)。比如 then-fs ,比如 axios
獲取結(jié)果
- 通過 then 方法獲取,并且通過 catch 捕獲錯(cuò)誤信息
- 通過 async和await的配合獲取結(jié)果,并且通過 try…catch…捕獲錯(cuò)誤
到此這篇關(guān)于JavaScript詳解使用Promise處理回調(diào)地獄與async await修飾符的文章就介紹到這了,更多相關(guān)JavaScript Promise回調(diào)地獄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
js實(shí)現(xiàn)字符全排列算法的簡(jiǎn)單方法
下面小編就為大家?guī)硪黄猨s實(shí)現(xiàn)字符全排列算法的簡(jiǎn)單方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05
JavaScript實(shí)現(xiàn)的encode64加密算法實(shí)例分析
這篇文章主要介紹了JavaScript實(shí)現(xiàn)的encode64加密算法,實(shí)例分析了javascript處理encode64編碼針對(duì)字符串加密的技巧,非常簡(jiǎn)潔實(shí)用,需要的朋友可以參考下2015-04-04
純js實(shí)現(xiàn)仿QQ郵箱彈出確認(rèn)框
仿QQ郵箱的彈出層,彈出確認(rèn)框,主要是用火狐的firebug把html和css扣了下來,沒有做封裝,就定義了一個(gè)拖動(dòng)事件. 大家可以封裝自己的彈出窗,嘿嘿!2015-04-04
showModalDialog模態(tài)對(duì)話框的使用詳解以及瀏覽器兼容
showModalDialog是jswindow對(duì)象的一個(gè)方法,和window.open一樣都是打開一個(gè)新的頁(yè)面。區(qū)別是:showModalDialog打開子窗口后,父窗口就不能獲取焦點(diǎn)了(也就是無法操作了)2014-01-01
JavaScript使用prototype原型實(shí)現(xiàn)的封裝繼承多態(tài)示例
這篇文章主要介紹了JavaScript使用prototype原型實(shí)現(xiàn)的封裝繼承多態(tài),涉及javascript prototype與面向?qū)ο蟪绦蛟O(shè)計(jì)相關(guān)操作技巧,需要的朋友可以參考下2018-08-08
BootStrap智能表單實(shí)戰(zhàn)系列(七)驗(yàn)證的支持
這篇文章主要介紹了BootStrap智能表單實(shí)戰(zhàn)系列(七)驗(yàn)證的支持 ,凡是涉及到用戶編輯信息然后保存的頁(yè)面,都涉及到一個(gè)數(shù)據(jù)是否符合要求的檢查,需要客服端和服務(wù)器端的校驗(yàn)的問題,本文介紹非常詳細(xì),具有參考價(jià)值,需要的朋友可以參考下2016-06-06
關(guān)于在Servelet中如何獲取當(dāng)前時(shí)間的操作方法
下面小編就為大家?guī)硪黄P(guān)于在Servelet中如何獲取當(dāng)前時(shí)間的操作方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-06-06

