1. 程式人生 > >Node.js系列--非同步流程控制

Node.js系列--非同步流程控制

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">	</span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">非同步程式設計是Node.js中最大的特點,在Node.js的程式設計實踐中我們為了使得其發揮特性,將所有的東西都寫成非同步方式,特別是非同步回撥方式。在Node.js原生程式碼中,只有fs模組即提供了同步版本又提供了非同步版本。現在我們來認識一下Node.js的非同步流程控制。</span>

一、 callback

在Node.js中,無論是自定義API還是原生方法到處都可以看到callback的身影,說道callback就要說說Node.js的非同步程式設計機制了,Node.js是一個底層由C++擴充套件的javascript執行時環境,它使得javascript可以在瀏覽器沙箱之外執行。由於javascript大多數時候(被熟知的)是執行在瀏覽器裡面,用於提升頁面互動性,製作特效等的指令碼語言,本身不具備太多操作檔案,作業系統資源的能力,這些都是通過C++擴充套件的,並且用google鼎鼎大名的V8引擎來執行javascript程式碼。javascript程式在執行的時候是單執行緒的,就是一段程式碼無法被分割到多個執行緒執行,然後在對結果進行歸一處理。但是在瀏覽器中整個javascript的runtime不是單執行緒的,因為瀏覽器能夠通過建立多執行緒執行多個javascript程式,比如ajax,在瀏覽器中ajax並不會阻塞當前的頁面的其它javascript程式碼的執行,因為兩者是獨立執行的,伺服器返回結果再由瀏覽器通過ajax物件將資料匯入javascript的runtime當中。在javascript中非同步程式設計,即在執行某個處理函式的時候,並沒有等待執行結果,而是任務排程器將程式的控制權交給了其它處理邏輯,當之前的處理完成後再將程式的控制權交還給那個邏輯,這樣就有了javascript中的“當....則...."這樣的非同步回撥邏輯。

//瀏覽器總ajax非同步回撥的例子
//1. ajax請求/hello
//2. 當請求得到響應則輸出結果
$.get('/hello', function(data){
     console.log(data)
})
</pre><pre name="code" class="javascript">//node.js中非同步回撥的例子
var fs = require('fs');
fs.readFile('hello.txt', function(error, text){
    if(error) throw error;
    console.log(text);
})
上面的示例就是典型的非同步回撥的例子,我們用匿名的function函式作為callback引數

// 自定義函式

functiton myReadFile(file, enc, callback){
    fs.readFile(file, enc, function(error, data){

       if(error){

      return callback(error);

       }
return callback(null, data); 

   })
}

上面的例子是我們自己定義的函式,同樣 的在呼叫的時候也是一樣的要傳入callback引數,接收函式的處理結果。
        

二、async

這裡的async只是列舉了一個例子,他並不是一種程式設計正規化,而是一個工具集合,它同樣使用的是callback方式,但是它能讓我們的程式的流程更加的清晰,可控制。這個是主要的,特別是很好的解決了非同步回撥的深層巢狀問題, 它提供了很多的方法,包括對map的處理。在編寫Node.js程式時所要用到的流程控制幾乎都已經涉及到了,這裡簡單的舉個例子,更多的可以檢視npm網站async文件。

var async = require('async')

async.map(fileList, function(file, callback){
    var stream = fs.createReadStream(file.filePath);
    (oss.ossClient).putObject({
        bucket: bucket,
        object: file.objectPath,
        srcFile: stream,
        contentType:file.contentType,
        contentLength: file.length
}, function (uploadOssError, result) {
        //上傳執行完成無論成功與否均需刪除本地檔案
fs.unlink(file.filePath, function(unLinkError1){
            if (unLinkError1){
                console.log('傳輸完成,檔案刪除失敗');
            }
            if(uploadOssError || result.statusCode !== 200){
                //console.log('檔案上傳至oss失敗');
console.log(uploadOssError.message);
                return callback(uploadOssError || new Error('upload file to oss fail.'));
            }
        });
        //傳輸完成的檔案需要刪除
return callback(null);
    });
},function(error){
    if(error){
        return next(error);
    }
    req.oss = ossPath;
    //需要將上傳的資訊傳到下一個中介軟體
return next(null);
});

三、promise

promise 是避免callback的程式設計實踐, 但是它所帶來的不僅僅是避免callback,以及callback造成的巢狀過深的問題,更多的是為了解決Node.js或者說是javascript非同步程式設計中的程式碼可靠性問題,因為深層巢狀只是一個表面的問題。其深層是所隱含的問題是程式碼缺乏可控性和可靠性。

        promise也是本文要著重講的內容。Promise A+ 規範: https://promisesaplus.com/ 

        關於promise的實現,各不相同,但是按照Promise 規範實現的就是Promise, 關於Promise的感念,原理大概是這樣的:

        某一個處理都是一個promise, 這個任務有pending(表示正在發生), fulfilled(表示已經履行),rejected(表示被拒絕)三種狀態,這個任務的執行將對其狀態產生影響,其狀態也會相應的發生改變。但是最終狀態必然是fulfilled或者rejected二者之一。fulfilled表示成功,rejected表示失敗。而且任務的狀態不可能從fulfilled變成rejected或者pending, 也不可能從rejectd變成fulfilled或者pending。這就保證了邏輯處理的可靠性。從變成形式來看。不同的狀態對應著不同的處理函式。對錯誤的處理也比較統一,具備一致性。下面具體講一下在程式設計中的使用。

1. Promise是一個物件,包含了其狀態,以及相應狀態的處理方法。

2. 使用then方法並接收兩個引數(fulfilled, rejected)對應著邏輯處理結果

3. then返回一個新的Promise物件。

4. then的返回是新的Promise物件,所以形成一個Promise鏈,從而可以進行鏈式操作。

Promise規範中定義的東西差不多就是這些,但是可以在此基礎上對程式設計過程中常見的一些使用場景進行擴充套件,包括了對不同邏輯的支援,特別是並行任務,上下關聯任務等等。在Node.js的Promise系列0模組中,比較知名的就是Q,是一個標準的Promise實現。還提供了很多比較有用的方法。下一篇我們就來詳細講解一下這個模組,以及所提供的方法及其用法。