2017-03-11 52 views
0

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

一些研究互聯網正在給2級主要的解決方案後:

-Export的職能

反過來,這:

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); 

使用承諾

我更傾向於功能的出口,但互聯網是說承諾是處理異步調用更好的選擇。

我不想進入一些東西,以後不得不改變我的編碼習慣/風格以符合標準慣例,它更好地開始正確的道路。

所以,我應該剛開始使用承諾或是出口的功能是一個很好的解決方案?

+0

「readFileSync」如何? – blackmiaool

+1

@blackmiaool:不要在NodeJS中使用同步函數,而沒有真正的原因。 –

回答

2

您還有第三種選擇,現在可以利用這個純粹出於輿論的境界:承諾+ async/await

ES2017(出來六月規範)將採用async/await,提供更簡單用於在簡單用例中使用承諾的語法,並且NodeJS已經在當前版本的Node v7中支持它們(本文撰寫時爲v7.7.2)。

隨着承諾和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(因爲它們在後臺管理承諾)。另外請注意,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對於簡單用例會帶來許多問題。 (對於更復雜的用例,您仍然需要回到明確的承諾處理。)

然而,是否將我們帶出意見領域還是另一個問題。自然而然,保持你的函數小和可組合是一件好事,不管你是否使用promise。


關於p在上面:有各種不同的庫了那裏promisifying使用節點的標準回調機制寫入的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; 

...但你可以發現,採取更徹底的辦法庫。

+0

在沒有'async' /'await'的例子中,你應該移動包含'console'的'.then'。日誌('附加文本!');'上一級。否則,與使用_callback hell_時嵌套級別相同。 –

+0

@ t.niese:謝謝。我沒有很好地介紹該代碼塊。我的意思是說,'async' /'await'有效地轉化到,並不是說這就是你怎麼會想* *與承諾去做。我已更新。 –

0

Promises非常好,因爲它們爲代碼提供了一個更「同步」的結構,即返回數據,而不是提供用於延續的異步回調。

假設你想寫一個讀入文件並提取數據的函數。隨着回調你可以這樣寫:

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

     return cb(txt); 
    }); 
} 

這是很好的,但它促進了你所提到的一切(回調地獄,缺乏準備,等...)

使用的承諾,你可以這樣寫:

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

(大多數API有返回的承諾,如果沒有,你可以用回調的API,使他們返回的承諾的實現)

當您使用的承諾,你的方法交流(這就是爲什麼我說承諾會給代碼提供更多「同步」結構的原因),他們返回的內容不是實際結果,而是一個承諾或一個處理結果,以便將來使用。 這樣做,因爲當你有一個承諾,你可以等待的結果,就像您有回調做了回調一個很大的好處:

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

你可以做的事情,在回調世界會導致回調地獄一樣的承諾後做一些回報:

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

一次等待幾個承諾:

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

Handli NG錯誤更優雅(而不是處處傳遞一個「犯錯」的說法):

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

你已經可以看到使用過的承諾促進回調一個更優雅的方式來寫異步代碼。

承諾可以,或者下載模塊,如bluebirdq

祝你好運,如果你正在使用JavaScript ES6的新版本中使用。

+0

感謝有一個很大的啓示和信息的答案。 – FrenchMajesty