iT邦幫忙

0

Node.js - 如何利用Promise來達成render網頁為最後一個步驟(已解決)

感謝邦友weiclin提供的方向, 測試成功了!!/images/emoticon/emoticon07.gif
這邊將改寫後的code貼在下方

var PA = new Array()  // 新增一個陣列變數for Promise.All 

for (var i = 0; i < result.recordset.length; i++) {
    (function (index) {
        PA[index] = this['p' + index] = new Promise((resolve, reject) => { //增加動態陣列與值
            smb2Books.readFile(dir + result.recordset[index].Photo_Month + '\\' + result.recordset[index].ISBN + '.jpg', function (err, content) {
                if (err) throw err
                imgTemp = Buffer.from(content).toString('base64')
                result.recordset[index].imgData = imgTemp
                resolve(result.recordset)
            });
        });
    })(i);
}
Promise.all(PA).then( values => { //改為 Promise.All
    res.render('booklist', {
        Data: result.recordset
    })
});

原文:
會有這個需求是因為我用到readdir配合readFile這項異步的function
必須等到這個function結束後才render網頁, 否則會有資料還沒讀取完全就render的狀況
其中有用到smb2 module, 類似讀取檔案的fs module

這是剛開始時第一版的寫法, 要去讀取shared folder的圖片檔案

smb2Books.readdir(dir, function (err, files) {
    if (err) {
        throw err;
    }
    for (var i = 0; i < result.recordset.length; i++) {
        (function (index) {
            smb2Books.readFile(dir + result.recordset[index].Photo_Month + '\\' + result.recordset[index].ISBN + '.jpg', function (err, content) {
                if (err) throw err
                imgTemp = Buffer.from(content).toString('base64')
                result.recordset[index].imgData = imgTemp
            });
        })(i);
    }
});
setTimeout(function () {
    res.render('booklist', {
        Data: result.recordset
    })
}, 200); //若資料量一多, 必須再將延遲調高(not smart), 後續要改寫成同步方法, 等待callback function皆執行完再render

如最後一行註解, 若我不設定setTimeout的話, 網頁會直接render沒有圖片的資料
雖然setTimeout可以達成我的需求, 但這個作法我實在很不喜歡

接下來爬文後改寫的第二版

smb2Books.readdir(dir, function (err, files) {
    if (err) {
        throw err;
    }

    function delay() {
        return new Promise(function (resolve, reject) {
            for (var i = 0; i < result.recordset.length; i++) {
                (function (index) {
                    smb2Books.readFile(dir + result.recordset[index].Photo_Month + '\\' + result.recordset[index].ISBN + '.jpg', function (err, content) {
                        if (err) throw err
                        imgTemp = Buffer.from(content).toString('base64')
                        result.recordset[index].imgData = imgTemp
                        resolve(result.recordset)
                    });
                })(i);
            }
        })
    }
    delay().then(function (value) {
        res.render('booklist', {
            Data: value
        })
    });
});

我每次重新整理, 第一個圖片必定能讀到
但幾乎沒法讀到最後一個圖片, 甚至有時從第三張圖片開始就讀不到了

想請問

  1. 我這個Promise的寫法是正確的嗎?
  2. 有沒有辦法達成全部圖片都讀完再render, 而不使用setTimeout?

謝謝看完文章的你/images/emoticon/emoticon13.gif

weiclin iT邦高手 4 級 ‧ 2019-03-18 19:35:37 檢舉
每個 readFile 都用一個 Promise, 再用 all 去等全部執行完
https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
rchin iT邦新手 5 級 ‧ 2019-03-19 10:11:34 檢舉
@weiclin 感謝你!! 測試成功了, 可達到這個頁面的render需求, 順便更加熟悉動態陣列與變數的產生方式

尚未有邦友回答

立即登入回答