Node.js非同步程式設計~超級詳細哦

weixin_46370867發表於2021-01-02

下面是對Node.js非同步程式設計的整理,希望可以幫助到有需要的小夥伴~

同步API,非同步API

同步API:只有當API執行完成後,才能繼續執行下一個API

非同步API:當前API的執行不會阻塞後續程式碼的執行

// 同步程式設計
/* 
console.log("before");
console.log("after"); 
// before
// after
*/

// 非同步程式設計
console.log('before');
setTimeout(
    () => {
        console.log('last')
    },2000);

console.log('after');
/* 
before
after
last
// before執行後,2秒鐘後再執行last,在這2秒鐘的時間裡after執行了
*/

同步API,非同步API的區別

獲取返回值的方式不同

同步API可以從返回值中拿到API執行的結果

非同步API必須通過回撥函式拿到非同步操作的執行結果

通過回撥函式獲取非同步函式的返回值

回撥函式:

別人呼叫自己定義的函式

// 回撥函式的定義
function getData (callback) {}
// 回撥函式的呼叫
getData ( ()=>{} );// getData()裡面的實參()=>{} 對應的是 callback形參;即在getData()函式內部呼叫了()=>{}函式

通過回撥函式獲取非同步函式的返回值示例:

/* function getMsg () {
    setTimeout(function () {
        return {
            msg:'hello node.js'
        }
    },2000)
}

const msg = getMsg();
console.log(msg); // undefined

// 2秒後才可以執行定時器裡面的內容 
// 所以返回值是函式的預設返回值undefined */


// 可以通過回撥函式獲取到非同步操作的執行結果
function getMsg (callback) {
    setTimeout(function () {
        callback({
            msg:'hello node.js'
        })
    },2000)
}

getMsg(function (data){
    console.log(data); // { msg: 'hello node.js' }
})

程式碼執行順序不同

Node.js從上而下依次執行程式碼

遇到同步API就拿到同步程式碼執行區去執行

遇到非同步API就拿到非同步程式碼執行區,但是不會執行非同步程式碼,

當所有的同步程式碼執行完畢後,再到非同步程式碼執行區依次執行程式碼

當非同步程式碼執行完畢後,系統會去回撥函式佇列中找非同步API對應的回撥函式,把回撥函式放到同步程式碼執行去區執行

同步API從上到下依次執行,前面程式碼會阻塞後面程式碼的執行

for (var i=0;i<10;i++) {
    console.log(i)
}
console.log("迴圈外")
// 0 1 2 3 4 5 6 7 8 9 迴圈外

非同步API不會等待API執行完成後再向下執行程式碼

console.log('程式碼開始執行')
setTimeout(function(){
    console.log('2s後執行')
},2000)

setTimeout(function(){
    console.log('0s後執行')
})

console.log('程式碼結束執行')

/* 程式碼開始執行
程式碼結束執行
0s後執行
2s後執行 */

在這裡插入圖片描述

Node.js中的非同步API

非同步程式設計的返回結果是通過回撥函式獲取的

在非同步程式設計中,前面程式碼的執行不會阻礙後面程式碼的執行,但是有時候後面程式碼的執行會依賴前面程式碼的執行結果,這時可以通過多個回撥函式來完成。

讀取檔案就是通過回撥函式返回結果的,如果依次讀取多個檔案,就需要多次呼叫回撥函式

示例如下:

// 依次讀取1.txt、 2.txt 、 3.txt檔案

// 匯入檔案模組
const fs = require('fs');
// 讀取檔案
fs.readFile('./1.txt','utf8',(err,result1) => {
    console.log(result1)
    fs.readFile('./2.txt','utf8',(err,result2) => {
        console.log(result2)
        fs.readFile('./3.txt','utf8',(err,result3) => {
            console.log(result3)
        })
    })
})
/* 
// 結果:
1
2
3 
*/

如果需要寫很多個回撥函式的話,就會造成回撥地獄,promise可以解決回撥地獄問題。

Promise

Promise出現的目的是解決Node.js非同步程式設計中回撥地獄的問題。

Promise是一個建構函式

Promise語法

let promise = new Promise((resolve,reject) => {
    setTimeout ( () => {
        if (true) {
            resolve({name:'張三'})
        } else {
            reject('失敗了')
        }
    },2000)
})
promise.then(result => console.log(result)); // {name:'張三'}
       .catch(error => console.log(error)); // 失敗了
  • resolve:將非同步API的執行結果傳遞到整個Promise的外面(resolve是一個方法)
  • reject:如果非同步API執行失敗,就把失敗的結果傳遞到整個Promise的外面 (reject是一個方法)

通過Promise物件的then方法獲取到非同步的執行結果。

通過Promise解決回撥地獄

// 依次讀取1.txt、 2.txt 、 3.txt檔案

// 匯入檔案模組
const fs = require('fs');
const { resolve } = require('path');
// 讀取檔案
/* fs.readFile('./1.txt','utf8',(err,result1) => {
    console.log(result1)
    fs.readFile('./2.txt','utf8',(err,result2) => {
        console.log(result2)
        fs.readFile('./3.txt','utf8',(err,result3) => {
            console.log(result3)
        })
    })
}) */

function p1() {
    return new Promise((resolve,reject) => {
        fs.readFile('./1.txt','utf8',(err,result) => {
            resolve(result)
        })
    })
}

function p2(){
    return new Promise((resolve,reject) => {
        fs.readFile('./2.txt','utf8',(err,result) => {
            resolve(result);
        })
    })
}

function p3(){
    return new Promise((resolve,reject) => {
        fs.readFile('./3.txt','utf8',(err,result) => {
            resolve(result);
        })
    })
}

p1().then((r1) => {
    console.log(r1);
    return p2();
})
.then((r2) => {
    console.log(r2);
    return p3();
}) 
.then((r3) => {
    console.log(r3);
})

// 相當於

/* p1().then((r1) => {
    console.log(r1);
});

p2().then((r2) => {
    console.log(r2);
});

p3().then((r3) => {
    console.log(r3);
}); */

/* 
// 結果:
1
2
3
*/

非同步函式

使用Promise可以解決回撥地獄的問題,但是會產生程式碼冗餘,可以使用ES 7提供的回撥函式解決回撥地獄的問題並且程式碼更加簡單明瞭。

非同步函式是非同步程式設計語法的終極解決方案,它可以讓我們將非同步程式碼寫成同步的形式,讓程式碼不再有回撥函式巢狀,使程式碼變得清晰明瞭。

const fn = async () => {};

async關鍵字:

  1. 普通函式定義前加async關鍵字普通函式變成非同步函式
  2. 非同步函式預設返回promise物件
  3. 在非同步函式內部使用return關鍵字進行結果返回,結果會被包裹在promise物件中
  4. return關鍵字代替了resolve方法
  5. 在非同步函式內部使用throw關鍵字丟擲程式異常
  6. 呼叫非同步函式再鏈式呼叫then方法獲取非同步函式執行結果
  7. 呼叫非同步函式再鏈式呼叫catch方法獲取非同步函式執行的錯誤資訊

await關鍵字:

  1. await關鍵字只能出現在非同步函式中
  2. await promise ;await後面只能寫promise物件寫其他型別的API是不可以的
  3. await關鍵字可是暫停非同步函式向下執行直到promise返回結果

示例:

const fs = require('fs');
// fs中readFile()方法的返回結果不是Promise型別的,不能使用非同步函式中的await關鍵字
// uitl模組的promisify()方法可以將返回的結果改為Primise型別
// 沒有呼叫promisify方法,沒有寫promisify()
// 改在現有非同步函式的API,讓其返回promise物件,從而支援非同步函式語法
const promisify = require('util').promisify;
// 呼叫了promisify()方法,但沒有呼叫readFile()方法
// 呼叫promisify方法改造現有非同步API,讓其返回promise物件
const readFile = promisify(fs.readFile);

async function run() {
    // 呼叫readFile()方法,該方法的返回結果回改成promise型別
    let r1 = await readFile('./1.txt','utf8')
    let r2 = await readFile('./2.txt','utf8')
    let r3 = await readFile('./3.txt','utf8')
    console.log(r1)
    console.log(r2)
    console.log(r3)
}

run();

// 返回結果:
// 1
// 2
// 3

end~
對Node.js非同步程式設計的介紹到這裡就結束啦~
希望可以對你有所幫助~
如果有錯誤,請大佬指正,萬分感謝!

相關文章