簡體   English   中英

Node.js回調到方法還是Promise?

[英]Nodejs callbacks to methods or promises?

我是Node的新手,我看到一個回調地獄模式出現在我的應用程序中,這使得它很難閱讀。

經過一番研究,互聯網提供了2種主要解決方案:

-導出功能

把這個:

var fs = require('fs');

var myFile = '/tmp/test';  
fs.readFile(myFile, 'utf8', function(err, txt) {  
    if (err) return console.log(err);

    txt = txt + '\nAppended something!';
    fs.writeFile(myFile, txt, function(err) {
        if(err) return console.log(err);
        console.log('Appended text!');
    });
});

到這個:

var fs = require('fs');

function notifyUser(err) {  
    if(err) return console.log(err);
    console.log('Appended text!');
};

function appendText(err, txt) {  
    if (err) return console.log(err);

    txt = txt + '\nAppended something!';
    fs.writeFile(myFile, txt, notifyUser);
}

var myFile = '/tmp/test';  
fs.readFile(myFile, 'utf8', appendText); 

使用承諾

我更傾向於函數的輸出,但是Internet表示,諾言是處理異步調用的更好的選擇。

我不想涉足某些領域,以后不得不改變我的編碼習慣/樣式以符合標准約定,最好從正確的道路入手。

因此,我應該只使用Promise還是函數導出是一個很好的解決方案?

現在,您還有第三個選擇,可以將其排除在純觀點之外:Promises + async / await

ES2017(將於6月發布的規范)將具有async / await ,這將提供更簡單的語法以在簡單的用例中使用Promise,並且NodeJS已在當前版本的Node v7(在撰寫本文時為v7.7.2)中支持它們。

使用promise和async / await ,您的代碼如下所示:

const p = require(/*...some theoretical promisifier...*/).promisifier;
const fs = require('fs');

async function go() {
    const myFile = '/home/tjc/temp/test';  
    let txt = await p(fs.readFile, myFile, 'utf8');
    txt = txt + '\nAppended something!';
    await p(fs.writeFile, myFile, txt);
    console.log('Appended text!');
}

go().catch(error => {
    console.log(error);
});

它仍然是異步的,它只是簡單的承諾使用情況的語法更加簡單,讓你有代碼,而不反映中介邏輯then回調函數。

請注意,您只能在async函數中使用await (因為它們在后台管理promise)。 還要注意,NodeJS很快就會對未處理的承諾拒絕感到驚訝,因此請確保我們在go()上進行了catch

我相信以上內容大致可轉換為以下內容:

const p = require(/*...some theoretical promisifier...*/).promisifier;
const fs = require('fs');

function go() {
    const myFile = '/home/tjc/temp/test';  
    return p(fs.readFile, myFile, 'utf8').then(txt => {
        txt = txt + '\nAppended something!';
        return p(fs.writeFile, myFile, txt).then(() => {
            console.log('Appended text!');
        });
    });
}

go().catch(error => {
    console.log(error);
});

...但是,當然,如果您自己編寫,則會采用不同的組織方式:

const p = require(/*...some theoretical promisifier...*/).promisifier;
const fs = require('fs');

function go() {
    const myFile = '/home/tjc/temp/test';  
    return p(fs.readFile, myFile, 'utf8')
        .then(txt => {
            txt = txt + '\nAppended something!';
            return p(fs.writeFile, myFile, txt);
        })
        .then(() => {
            console.log('Appended text!');
        });
}

go().catch(error => {
    console.log(error);
});

如您所見,就清楚而言, async / await為簡單的用例帶來了很多async (對於更復雜的用例,您仍然必須依靠顯式的Promise處理。)

但是,這是否使我們脫離了觀點領域呢? 當然,無論您是否使用諾言,使函數小巧且可組合都是一件好事。


關於上面的p :有各種各樣的庫供使用Node的標准回調機制編寫的API簡化。 在上面,我使用的是理論上的方法。 這是上面p的一個非常非常非常簡單的實現:

const p = (f, ...args) => {
    return new Promise((resolve, reject) => {
        args.push((err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
        f(...args);
    });
};
exports.promisifier = p;

...但是您可以找到采用更全面方法的庫。

承諾之所以出色,是因為它們為代碼提供了一個更加“同步”的結構,即返回數據而不是為繼續提供異步回調。

假設您要編寫一個函數,該函數可讀取文件並提取其數據。 使用回調,您將編寫:

function readMyFile(cb) {
    fs.readFile('/tmp/test', function (err, txt) {
        if (err || !txt) {
            return cb(null);
        }

        return cb(txt);
    });
}

很好,但是它可以促進您提到的所有內容(回調地獄,准備不足等)。

使用promise,您將編寫:

function readMyFile() {
    return fs.readFileAsync('/tmp/test');
}

(大多數API都有一個返回promise的實現,如果沒有,您可以包裝回調API使其返回promise)

當您使用promise時,您的方法實際上會返回某些內容(這就是我說promise使代碼具有更多“同步”結構的原因),它們返回的不是實際結果,而是一個promise或包裝該結果的句柄將來使用。 與回調相比,這有很大的好處,因為當您做出承諾時,您可以像對待回調一樣等待結果:

promise = readMyFile();
promise.then(function (data) {
    ...
});

您可以做一些在回調世界中會導致回調地獄的事情,例如在promise返回后做一些事情:

 promise.then(sendDataToServe)
       .then(storeServerReplyInDatabase);

一次等待幾個承諾:

Promise.all([promiseA, promiseB])
       .then(function (resA, resB) {
        ...
    });

更優雅地處理錯誤(而不是在各處傳遞“ err”參數):

Promise.all([promiseA, promiseB])
       .then(function (resA, resB) {
        ...
    })
    .catch(function (err) {
        console.error(err);
    });

您已經看到,在回調上使用Promise可以促進一種更為優雅的編寫異步代碼的方式。

如果您使用的是JavaScript ES6的較新版本,或者通過下載bluebirdq等模塊,則可以使用Promise

祝好運。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM