javascript - Node.js中輸出順序為什麼跟預期的不一樣呢?
大家讲道理
大家讲道理 2017-05-31 10:40:11
0
2
645

考察下面的程式碼,它用於用於輸出指定目錄下的檔案

var fs=require('fs');

function sleep(numberMillis) {     //sleep方法,暂停numberMillis毫秒
    var now = new Date(); 
    var exitTime = now.getTime() + numberMillis; 
    while (true) { 
        now = new Date(); 
        if (now.getTime() > exitTime) 
            return; 
    } 
}

fs.readdir(__dirname,function(err,files){    //文件读取方法
    console.log('');
    if(!files.length){
        console.log('找不到文件 \n');
    }else{
        console.log('文件如下:');
        function file(i){
            var filename=files[i];
            fs.stat(__dirname+'/'+filename,function(err,stat){
                if(stat.isDirectory()){
                    console.log(' '+i+'文件夹:'+filename+'/...');
                }else{
                    console.log(' '+i+'文件:'+filename+'');
                }
            });
            i++;
            //sleep(1000);    //。。。。。。。。######暂停1s    
            if(i==files.length){
                console.log('输出完毕');
            }else{
                file(i);    //否则递归调用
            }
        }
        file(0);        //开始执行
    }
});

上述程式碼輸出的結果我無法理解,按照這個程式設計的邏輯,應該先是把目錄下所有檔案都列出來,然後計數器i==files.length後才輸出「輸出完畢」這個終結語。


如上圖所示,不但「輸出完畢」這個終結語最先輸出,而且列印順序完全是錯亂的,多次運行的列印順序也不穩定。

我在想是不是非同步呼叫有時間差,所以將上述程式碼sleep(1000)註解部分去掉,但是結果依然與我所預期不一致:「檔案如下」提示語先輸出,此後停止1s後,後續語句幾乎同時出,沒有停頓,而且「輸出完畢」這個終結語依然第一個被印出

綜上所述,我想知道的是

  1. 為什麼列印結果不穩定,為什麼「輸出完畢」這個提示語會先被輸出?

  2. 為什麼加上sleep(1000)這個暫停語句後,只有一開始停了1s,後面不再暫停而是同時輸出?

  3. 如何解決這個問題,即如何在修改盡可能少的情況下讓「輸出完畢」這個提示語最後輸出?

謝謝各位大佬賜教!

大家讲道理
大家讲道理

光阴似箭催人老,日月如移越少年。

全部回覆(2)
大家讲道理
  1. fs.stat是非同步操作,所以你的輸出檔案資訊都是非同步執行的,「輸出完畢」一定是先印出來的。想同步可以用這個https://nodejs.org/docs/lates...

  2. 在sleep裡加個console.log('sleep')看是不是 只輸出一次?

  3. 如果你想保證輸出文件資訊之後才輸出“輸出完畢”,第一點裡我提到的用fs.statSync估計修改比較少吧

Ty80

根據@Dont的回答,確實是非同步方法調用的原因,因為fs.stat的回調函數是在事件輪詢中被調用,它一般在主程式運行間隙時候被調用,被調用時間和順序不定。使用statSync方法可以實現同步:

(function file(i){
    var filename=files[i];
    function read(stat){
        if(stat.isDirectory()){
            console.log(' '+i+' 3[36m 文件夹:'+filename+'/...3[39m');
        }else{
            console.log(' '+i+' 3[36m 文件:'+filename+'3[39m');
        }
    };
    read(fs.statSync(__dirname+'/'+filename));
    i++;
    ................
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板