es6——promise(解決回撥地獄的問題)
阿新 • • 發佈:2018-12-18
注意:裡面一些執行報錯結果,是為了演示,故意寫錯地址
首先,提出一個需求:要封裝一個方法,使得能夠讀取檔案,並將內容返回
先建立 3個 .txt 檔案1.txt/2.txt/3.txt
,作為我們要讀取的檔案,內容自己瞎編
- fs 和 path 模組:
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);
});
- 升級: 我們可以規定一下,callback 中,有兩個引數,第一個引數,是失敗的結果;第二個引數是成功的結果 同時規定:如果成功後,返回的結果,應該位於 callback 引數的第二個位置,此時,第一個位置由於沒有出錯,所以放一個 null;如果失敗了,則第一個位置放置 Error 物件,第二個位置放置 undefined
function readFile(fpath, callback) {
fs.readFile(fpath, 'utf-8', (err, dataStr) => {
// 如果報錯了,則進入 if 分支
if (err) return callback(err);
// return 會返回 undefined
// return dataStr;
callback(null, dataStr);
})
}
/*var result = readFile(path.join(__dirname, './files/1.txt'));
console.log(result);*/
readFile(path.join(__dirname, './files/1.txt'), (err, dataStr) => {
// console.log(dataStr);
if (err) return console.log(err.message);
console.log(dataStr);
});
其次,需求升級:先讀取檔案1,再讀取檔案2,最後讀取檔案3
const fs = require('fs');
const path = require('path');
// 為了方便初學者理解,在這裡我把原來 callback 分為 succCB 和 errCB
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);
});
});
});
現在引入主題:Promise
使用 ES6 中的 Promise,來解決回撥地獄的問題; 注意:promise 只是單純的解決回撥地獄問題,並不是解決程式碼量;
- Promise 是一個建構函式,可以 new Promise() 得到一個 Promise 的例項;
- 在 Promise 上,有兩個函式,分別叫做 resolve(成功後的回撥函式) 和 reject(失敗後的回撥函式)
- 在 Promise 建構函式的 Prototype 屬性上,有一個 .then() 方法,也就是說,只要是 Promise 建構函式建立的例項,都可以訪問到 .then() 方法
- Promise 表示一個非同步操作;每當我們 new 一個 Promise 的例項,這個例項,就表示一個具體的非同步操作;
- 既然 Promise 建立的例項,是一個非同步操作,那麼這個非同步操作的結果,只能有兩種狀態:
- 狀態1:非同步執行成功了,需要在內部呼叫 成功的回撥函式 resolve 把結果返回給呼叫者;
- 狀態2:非同步執行失敗了,需要在內部呼叫 失敗的回撥函式 reject 把結果返回給呼叫者;
- 由於 Promise 的例項,是一個非同步操作,所以,內部拿到操作的結果後,無法使用 return 把操作結果返回給呼叫者;這時候,只能使用回撥函式的形式,來把成功或失敗的結果,返回給呼叫者;
- 可以在 new 出來的 Promise 例項上,呼叫 .then() 方法,【預先】為這個 Promise 非同步操作,指定 成功(resolve) 和 失敗(reject) 回撥函式;
下面程式碼解釋原理:
1
// 注意:這裡 new 出來的 promise,只是代表 【形式上】的一個非同步操作(空殼);
let promise = new Promise();
2
const fs = require('fs');
// 每當 new 一個 promise 例項的時候,就會立即執行這個非同步操作中的程式碼
// 也就是說,new 的時候,除了能夠得到一個 promise 例項之外,還會立即呼叫我們
// 為 Promise 建構函式傳遞的那個function,執行這個 function 中的非同步操作程式碼;
let promise = new Promise(function () {
fs.readFile('./files/3.txt', 'utf-8', (err, dataStr) => {
if (err) throw err;
console.log(dataStr);
})
});
3
const fs = require('fs');
// 可以自己決定是否呼叫 Promise
function getFileByPath(fpath) {
let promise = new Promise(function () {
fs.readFile(fpath, 'utf-8', (err, dataStr) => {
if (err) throw err;
console.log(dataStr);
})
})
}
getFileByPath('./files/3.txt');
4
const fs = require('fs');
// 可以報具體錯誤
function getFileByPath(fpath) {
let promise = new Promise(function (resolve, reject) {
//*2
fs.readFile(fpath, 'utf-8', (err, dataStr) => {
/*if (err) throw err;
console.log(dataStr);*/
if (err) reject(err);
resolve(dataStr);
})
});
return promise;
}
let p=getFileByPath('./files/.txt');
// 第一個是 resolve,第二個是 reject
//*1
p.then(function (data) {
console.log('執行成功:'+data);
},function (err) {
console.log('執行失敗:'+err.message);
});
注意:執行步驟是先*1
再*2
// 優化寫法
getFileByPath('./files/.txt').then(function (data) {
console.log('執行成功:' + data);
}, function (err) {
console.log('執行失敗:' + err.message);
});
解決回撥地獄的問題:
const fs = require('fs');
function getFileByPath(fpath) {
let promise = new Promise(function (resolve, reject) {
fs.readFile(fpath, 'utf-8', (err, dataStr) => {
if (err) reject(err);
resolve(dataStr);
})
});
return promise;
}
// 注意:通過 .then 指定回撥函式的時候,成功的函式,
// 必須傳,但是失敗的回撥可以省略
// 在上一個 .then 中,返回一個新的 promise 例項,可以繼續用下一個 .then 來處理
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);
});