一溯祸、Promise的起因
當(dāng)項(xiàng)目有需求時叹俏,需要封裝一個方法,給一個要讀取文件的路徑嚷往,使用這個方法去讀取文件并把內(nèi)容返回葛账。
例:在一個file文件夾里有三個文檔,需要將里面內(nèi)容讀取出來
分別在1皮仁,2籍琳,3文件里保存111,222贷祈,333趋急,然后創(chuàng)建js文件
1.普通讀取文件的方式
const fs = require('fs')
const path = require('path')
fs.readFile(path.join(__dirname, './files/1.txt'), 'utf-8', (err, dataStr) => {
if (err) throw err
console.log(dataStr) //111
})
結(jié)果輸出111
2.利用異步方式讀取文件
a.利用return
function getFileByPath(fpath, callback) {
fs.readFile(fpath, 'utf-8', (err, dataStr) => {
if (err) throw(err)
return dataStr
})
}
var result = getFileByPath(path.join(__dirname, './files/1.txt'))
console.log(result) //undefined
結(jié)果如下:
原因:此時return沒有在主函數(shù)內(nèi)部,一個方法沒有return势誊,這個方法默認(rèn)返回undefined
b.利用callback
function getFileByPath(fpath, callback) {
fs.readFile(fpath, 'utf-8', (err, dataStr) => {
if (err) throw(err)
callback(dataStr)
})
}
getFileByPath(path.join(__dirname, './files/1.txt'), (err, dataStr) => {
console.log('callback調(diào)用的方法-----'+dataStr)
})
結(jié)果輸出為:
A.callback對異常結(jié)果的處理
我們可以規(guī)定: callback 中呜达,有兩個參數(shù);第一個參數(shù)粟耻,是 失敗的結(jié)果查近;第二個參數(shù)是成功的結(jié)果;
如果成功了挤忙,返回的結(jié)果霜威,應(yīng)該位于 callback 參數(shù)的第二個位置,此時册烈, 第一個位置 由于沒有出錯戈泼,所以,放一個 null;
如果失敗了矮冬,則 第一個位置放 Error對象谈宛,第二個位置放置一個 undefined
function getFileByPath(fpath, callback) {
fs.readFile(fpath, 'utf-8', (err, dataStr) => {
if (err) return callback(err)
callback(null, dataStr)
})
}
getFileByPath(path.join(__dirname, './files/1.txt'), (err, dataStr) => {
if (err) return console.log(err.message)
console.log(dataStr)//111
})
結(jié)果輸出111
若路徑錯誤的話,結(jié)果輸出
ENOENT: no such file or directory, open
B.定義兩個callback將正確的路徑和錯誤的路徑分開
const fs = require('fs')
const path = require('path')
//定義兩個callback將正確的路徑和錯誤的路徑分開
function getFileByPath(fpath, succCb, errCb) {
fs.readFile(fpath, 'utf-8', (err, dataStr) => {
if (err) return errCb(err)
succCb(dataStr)
})
}
getFileByPath(path.join(__dirname, './files/1.txt'), function (data) {
console.log(data + '成功了Lナ稹_郝肌!')
}, function (err) {
console.log('失敗的結(jié)果琼牧,我們使用失敗的回調(diào)處理了一下:' + err.message)
})
當(dāng)路徑正確時:
當(dāng)路徑錯誤時:
C.當(dāng)出現(xiàn)需求時:先讀取文件1恢筝,再讀取文件2,最后再讀取文件3
const fs = require('fs')
const path = require('path')
function getFileByPath(fpath, succCb, errCb) {
fs.readFile(fpath, 'utf-8', (err, dataStr) => {
if (err) return errCb(err)
succCb(dataStr)
})
}
getFileByPath(path.join(__dirname, './files/1.txt'), function (data) {
console.log(data)
getFileByPath(path.join(__dirname, './files/2.txt'), function (data) {
console.log(data)
getFileByPath(path.join(__dirname, './files/3.txt'), function (data) {
console.log(data)
})
})
})
結(jié)果如下:
當(dāng)出現(xiàn)回調(diào)地獄時巨坊,可以利用ES6的promise來解決
二撬槽、promise的基本介紹
本質(zhì):是單純的為了解決回調(diào)地獄問題;并不能幫我們減少代碼量趾撵;
將回調(diào)地獄的調(diào)用關(guān)系侄柔,改變?yōu)榇?lián)關(guān)系
1.
Promise 是一個 構(gòu)造函數(shù),既然是構(gòu)造函數(shù)占调, 那么暂题,我們就可以 new Promise() 得到一個 Promise 的實(shí)例;
2.
在 Promise 上究珊,有兩個函數(shù)薪者,分別叫做 resolve(成功之后的回調(diào)函數(shù)) 和 reject(失敗之后的回調(diào)函數(shù))
3.
在 Promise 構(gòu)造函數(shù)的 Prototype 屬性上,有一個 .then() 方法剿涮,也就說言津,只要是 Promise 構(gòu)造函數(shù)創(chuàng)建的實(shí)例,都可以訪問到 .then() 方法
4.
Promise 表示一個 異步操作取试;每當(dāng)我們 new 一個 Promise 的實(shí)例悬槽,這個實(shí)例,就表示一個具體的異步操作瞬浓;
5.
既然 Promise 創(chuàng)建的實(shí)例陷谱,是一個異步操作,那么瑟蜈,這個 異步操作的結(jié)果,只能有兩種狀態(tài):
-
5.1 狀態(tài)1: 異步執(zhí)行成功了渣窜,需要在內(nèi)部調(diào)用 成功的回調(diào)函數(shù) resolve 把結(jié)果返回給調(diào)用者铺根;
-
5.2 狀態(tài)2: 異步執(zhí)行失敗了,需要在內(nèi)部調(diào)用 失敗的回調(diào)函數(shù) reject 把結(jié)果返回給調(diào)用者乔宿;
-
5.3 由于 Promise 的實(shí)例位迂,是一個異步操作,所以,內(nèi)部拿到 操作的結(jié)果后掂林,無法使用 return 把操作的結(jié)果返回給調(diào)用者臣缀; 這時候,只能使用回調(diào)函數(shù)的形式泻帮,來把 成功 或 失敗的結(jié)果精置,返回給調(diào)用者;
6.
我們可以在 new 出來的 Promise 實(shí)例上锣杂,調(diào)用 .then() 方法脂倦,【預(yù)先】 為 這個 Promise 異步操作,指定 成功(resolve) 和 失斣(reject) 回調(diào)函數(shù)赖阻;
1.形式上和具體的promise異步操作的區(qū)別
注意:這里 new 出來的 promise, 只是代表 【形式上】的一個異步操作踱蠢;
什么是形式上的異步操作:就是說火欧,我們只知道它是一個異步操作,但是做什么具體的異步事情茎截,目前還不清楚
// 這是一個具體的異步操作苇侵,其中,使用 function 指定一個具體的異步操作
var promise = new Promise(function(){
// 這個 function 內(nèi)部寫的就是具體的異步操作<诨ⅰP铺础!
})
2.創(chuàng)建一個具體的異步操作
const fs = require('fs')
var promise = new Promise(function () {
fs.readFile('./files/2.txt', 'utf-8', (err, dataStr) => {
if (err) throw err
console.log(dataStr) //222
})
})
注意:每當(dāng) new 一個 Promise 實(shí)例的時候霎俩,就會立即執(zhí)行這個異步操作中的代碼哀军;即,當(dāng)前操作會立即執(zhí)行
也就是說打却,new 的時候杉适,除了能夠得到 一個 promise 實(shí)例之外,還會立即調(diào)用 我們?yōu)?Promise 構(gòu)造函數(shù)傳遞的那個 function柳击,執(zhí)行這個 function 中的 異步操作代碼猿推;
3.利用函數(shù)封裝,之后再進(jìn)行調(diào)用
使promise不用立即執(zhí)行
function getFileByPath(fpath) {
var promise = new Promise(function () {
fs.readFile(fpath, 'utf-8', (err, dataStr) => {
if (err) throw err
console.log(dataStr)
})
})
}
getFileByPath('./files/2.txt') //222
三捌肴、通過.then指定成功或失敗的回調(diào)
function getFileByPath(fpath) {
return new Promise(function (resolve, reject) {
fs.readFile(fpath, 'utf-8', (err, dataStr) => {
if (err) return reject(err)
resolve(dataStr)
})
})
}
getFileByPath('./files/2.txt')
.then(function (data) {
console.log(data) //222
}, function (err) {
console.log(err.message)
})
不管是成功還是失敗都會返回讀取到的內(nèi)容
1.解決回調(diào)地獄
需求:先讀取文件1蹬叭,在讀取2,最后讀取3
注意:通過 .then 指定 回調(diào)函數(shù)的時候状知,成功的回調(diào)函數(shù)秽五,必須傳,但是饥悴,失敗的回調(diào)坦喘,可以省略不傳
const fs = require('fs')
function getFileByPath(fpath) {
return new Promise(function (resolve, reject) {
fs.readFile(fpath, 'utf-8', (err, dataStr) => {
if (err) return reject(err)
resolve(dataStr)
})
})
}
//錯誤示范
getFileByPath('./files/1.txt')
.then(function (data) {
console.log(data)
getFileByPath('./files/2.txt')
.then(function (data) {
console.log(data)
getFileByPath('./files/3.txt')
.then(function (data) {
console.log(data)
})
})
})
這是一個錯誤示范
正確做法:
在上一個 .then 中盲再,返回一個新的 promise 實(shí)例,可以繼續(xù)用下一個 .then 來處理
const fs = require('fs')
function getFileByPath(fpath) {
return new Promise(function (resolve, reject) {
fs.readFile(fpath, 'utf-8', (err, dataStr) => {
if (err) return reject(err)
resolve(dataStr)
})
})
}
//正確做法
getFileByPath('./files/1.txt')
.then(function (data) {
console.log(data)
// 讀取文件2
return getFileByPath('./files/2.txt')
})
.then(function (data) {
console.log(data)
return getFileByPath('./files/3.txt')
})
.then(function (data) {
console.log(data)
})
結(jié)果如下:
四瓣铣、捕獲異常的兩種方式
1.如果前面的 Promise 執(zhí)行失敗答朋,我們不想讓后續(xù)的Promise 操作被終止,可以為每個 promise 指定失敗的回調(diào)
如果前面的 Promise 執(zhí)行失敗了棠笑,但是不要影響后續(xù) promise 的正常執(zhí)行梦碗,此時,我們可以單獨(dú)為 每個 promise腐晾,通過 .then 指定一下失敗的回調(diào)
const fs = require('fs')
function getFileByPath(fpath) {
return new Promise(function (resolve, reject) {
fs.readFile(fpath, 'utf-8', (err, dataStr) => {
if (err) return reject(err)
resolve(dataStr)
})
})
}
getFileByPath('./files/11.txt')
.then(function (data) {
console.log(data)
// 讀取文件2
return getFileByPath('./files/2.txt')
}, function (err) {
console.log('這是失敗的結(jié)果:' + err.message)
// return 一個 新的 Promise
return getFileByPath('./files/2.txt')
})
.then(function (data) {
console.log(data)
return getFileByPath('./files/3.txt')
})
.then(function (data) {
console.log(data)
}).then(function (data) {
console.log(data)
})
console.log('OKOKOK')
結(jié)果如下:
2.利用catch去處理promise拋出的異常
如果 后續(xù)的Promise 執(zhí)行叉弦,依賴于 前面 Promise 執(zhí)行的結(jié)果,如果前面的失敗了藻糖,則后面的就沒有繼續(xù)執(zhí)行下去的意義了淹冰,此時,我們想要實(shí)現(xiàn)巨柒,一旦有報(bào)錯樱拴,則立即終止所有 Promise的執(zhí)行;
const fs = require('fs')
function getFileByPath(fpath) {
return new Promise(function (resolve, reject) {
fs.readFile(fpath, 'utf-8', (err, dataStr) => {
if (err) return reject(err)
resolve(dataStr)
})
})
}
//catch處理異常
getFileByPath('./files/1.txt')
.then(function (data) {
console.log(data)
// 讀取文件2
return getFileByPath('./files/22.txt')
})
.then(function (data) {
console.log(data)
return getFileByPath('./files/3.txt')
})
.then(function (data) {
console.log(data)
})
.catch(function (err) { console.log('這是自己的處理方式:' + err.message)
})
catch 的作用: 如果前面有任何的 Promise 執(zhí)行失敗洋满,則立即終止所有 promise 的執(zhí)行晶乔,并馬上進(jìn)入 catch 去處理 Promise中 拋出的異常;
五牺勾、JQuery中Ajax使用promise指定成功的回調(diào)函數(shù)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<input type="button" value="獲取數(shù)據(jù)" id="btn">
<script src="./node_modules/jquery/dist/jquery.min.js"></script>
<script>
$(function () {
$('#btn').on('click', function () {
$.ajax({
url: './data.json',
type: 'get',
dataType: 'json'
})
.then(function (data) {
console.log(data)
})
})
});
</script>
</body>
</html>