1. 程式人生 > >es6——promise(解決回撥地獄的問題)

es6——promise(解決回撥地獄的問題)

注意:裡面一些執行報錯結果,是為了演示,故意寫錯地址

首先,提出一個需求:要封裝一個方法,使得能夠讀取檔案,並將內容返回

先建立 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 只是單純的解決回撥地獄問題,並不是解決程式碼量;

  1. Promise 是一個建構函式,可以 new Promise() 得到一個 Promise 的例項;
  2. 在 Promise 上,有兩個函式,分別叫做 resolve(成功後的回撥函式) 和 reject(失敗後的回撥函式)
  3. 在 Promise 建構函式的 Prototype 屬性上,有一個 .then() 方法,也就是說,只要是 Promise 建構函式建立的例項,都可以訪問到 .then() 方法
  4. Promise 表示一個非同步操作;每當我們 new 一個 Promise 的例項,這個例項,就表示一個具體的非同步操作;
  5. 既然 Promise 建立的例項,是一個非同步操作,那麼這個非同步操作的結果,只能有兩種狀態:
    • 狀態1:非同步執行成功了,需要在內部呼叫 成功的回撥函式 resolve 把結果返回給呼叫者;
    • 狀態2:非同步執行失敗了,需要在內部呼叫 失敗的回撥函式 reject 把結果返回給呼叫者;
    • 由於 Promise 的例項,是一個非同步操作,所以,內部拿到操作的結果後,無法使用 return 把操作結果返回給呼叫者;這時候,只能使用回撥函式的形式,來把成功或失敗的結果,返回給呼叫者;
  6. 可以在 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);
    });

在這裡插入圖片描述