node js 異步學(xué)習(xí)記錄

假設(shè)一個場景街佑,需要讀取兩個文件愉棱,然后把兩個文件的內(nèi)容倒序合并后輸出翎蹈,而且不能阻塞進程分别。

直接用回調(diào)來實現(xiàn)
var fs = require('fs');

var path1 = __dirname + '/package.json';
var path2 = __dirname + '/app.js';

console.log('start');

fs.readFile(path1, 'utf8', (err, content1) => {
    if (err) throw err;
    fs.readFile(path2, 'utf8', (err, content2) => {
        if (err) throw err;
        console.log(path2 + '\
' + content2 + '\
' + path1 + '\
' + content1);
    });
});

console.log('end');

如果有 n 個文件要讀取遍愿,那就是回調(diào)地獄(Callback Hell)。


Callback Hell
使用 Promise 實現(xiàn)

直接使用原生 Promise

var fs = require('fs');
var co = require('co');

var path1 = __dirname + '/package.json';
var path2 = __dirname + '/app.js';


console.log('start');

var readFilePromise = (path) => {
    return new Promise((resolve, reject) => {
        fs.readFile(path, 'utf8', (err, content) => {
            if (err) reject(err);
            resolve(content);
        });
    });
};

Promise.resolve().then(() => {
    return readFilePromise(path1).then((content1) => {
        return content1;
    });
}).then((result)=>{
    return readFilePromise(path2).then((content2)=>{
        return path2 + '\
' + content2 + '\
' + path1 + '\
' + result;
    });
}).then((result)=>{
    console.log(result);
});
console.log('end');

readFilePromise 把 readfile 的 callback 轉(zhuǎn)換為Promise耘斩,下面就可以直接用 then 方法了沼填。

這里直接用 bluebird

var fs = require('fs');
var Promise = require('bluebird');

var path1 = __dirname + '/package.json';
var path2 = __dirname + '/app.js';

console.log('start');

Promise.promisifyAll(fs);

Promise.resolve().then(() => {
    return fs.readFileAsync(path1, 'utf8').then((content) => {
        return content;
    });
}).then((result) => {
    return fs.readFileAsync(path2, 'utf8').then((content) => {
        return path2 + '\
' + content + '\
' + path1 + '\
' + result;
    });
}).then((result) => {
    console.log(result);
});

console.log('end');

這里的 fs.readFileAsync 方法已經(jīng)通過 bluebrid 轉(zhuǎn)換成了 Promise。

如果有 n 個文件需要煌往,那就需要加 n 個 then 方法倾哺。

改進的 Promise 實現(xiàn)方式

使用 Promise.all 方法

原生 Promise

var fs = require('fs');

var path1 = __dirname + '/package.json';
var path2 = __dirname + '/app.js';

console.log('start');

var readFilePromise = (path) => {
    return new Promise((resolve, reject) => {
        fs.readFile(path, 'utf8', (err, content) => {
            if (err) reject(err);
            resolve(content);
        });
    });
};

var files = [path1, path2];
var promises = [];
files.forEach((filePath) => {
    promises.push(readFilePromise(filePath));
});
Promise.all(promises).then((result) => {
    var contents = result.reverse();
    var out=[];
    files.reverse().forEach((filePath,index)=>{
        out.push(filePath);
        out.push(contents[index]);
    });
    console.log(out.join('\
'));
});
console.log('end');

bluebird 方式

var fs = require('fs');
var Promise = require('bluebird');

var path1 = __dirname + '/package.json';
var path2 = __dirname + '/app.js';

console.log('start');

Promise.promisifyAll(fs);

var files = [path1, path2];
var fileReadPromise = [];
files.forEach((filePath) => {
    fileReadPromise.push(fs.readFileAsync(filePath, 'utf8'));
});
Promise.all(fileReadPromise).then((result) => {
    var contents = result.reverse();
    var out=[];
    files.reverse().forEach((filePath,index)=>{
        out.push(filePath);
        out.push(contents[index]);
    });
    console.log(out.join('\
'));
});

console.log('end');

這樣的話,不管有多少文件刽脖,只要放入 files 數(shù)組即可。

兩種 Promise 實現(xiàn)是有區(qū)別的忌愚,第一種方式是順序讀取文件曲管,讀完一個再讀另外一個,第二種方式是同時開始讀取硕糊,全部完成后返回院水。

使用Generator實現(xiàn)

Generator 概念

var fs = require('fs');

var path1 = __dirname + '/package.json';
var path2 = __dirname + '/app.js';

console.log('start');

var thunkReadFile = function (path) {
    return function (cb) {
        fs.readFile(path, 'utf8', (err, content) => {
            cb(err, content);
        });
    };
};

function* readAllFile() {
    var content1 = yield thunkReadFile(path1);
    var content2 = yield thunkReadFile(path2);
    console.log(path2 + '\
' + content2 + '\
' + path1 + '\
' + content1);
}


var rf = readAllFile();
var fn1 = rf.next().value;
fn1(function (err, con1) {
    if (err) rf.throw(err);
    var fn2 = rf.next(con1).value;
    fn2(function (err, con2) {
        if (err) rf.throw(err);
        rf.next(con2);
    });
});
console.log('end');

這樣寫不就還是 Callback Hell, 這是為了了解Generator.

function* 定義了一個 Generator function, 那么 rf 就是 Generator腊徙,它會有 next 方法,返回一個{value : (object),done: (boolean)}的對象檬某,其中的 value 就是 yield 后面返回的值撬腾。每次調(diào)用 next 就會向下執(zhí)行一個 yield,直到?jīng)]有 yield 后返回的對象里 done就為true恢恼,而每次調(diào)用 next 的參數(shù)會作為 yield 的返回值民傻。

以上面的代碼來說,

var fn = rf.next().value;

這里執(zhí)行后就到了第一個 yield 停住

var content1 = yield thunkReadFile(path1);

那么 next 返回的對象中的 value 就是 thunkReadFile(path1)场斑,這里已經(jīng)賦值給了 fn1漓踢。這里要注意 yield 還沒有返回,content1還沒有值漏隐。

第二次執(zhí)行 next 的時候

var fn2 = rf.next(con1).value;

得到了 thunkReadFile(path2)喧半,同時將第一個 yield 的值通過 next 的參數(shù)返回,content1現(xiàn)在是第一個文件的內(nèi)容.

第三次執(zhí)行 next 的時候

rf.next(con2);

將第二個 yield 的值返回給 content2,同時整個 Generator function 執(zhí)行完畢青责,輸出結(jié)果挺据。

當(dāng)然也可以用 Promise 來實現(xiàn)這個過程。但是有更簡單的方法脖隶,那就是 CO 庫吴菠。

CO 實現(xiàn)
var fs = require('fs');
var co = require('co');

var path1 = __dirname + '/package.json';
var path2 = __dirname + '/app.js';

console.log('start');

var trunkReadFile = function (path) {
    return function (cb) {
        fs.readFile(path, 'utf8', (err, content) => {
            cb(err, content);
        });
    };
};

co(function* readAllFile() {
    var content1 = yield trunkReadFile(path1);
    var content2 = yield trunkReadFile(path2);
    console.log(path2 + '\
' + content2 + '\
' + path1 + '\
' + content1);
});
console.log('end');

這樣有 n 個文件同樣也都寫入數(shù)組處理即可。fn1那部分其實就是 CO 的基本原理浩村,但是 CO 是用 Promise 實現(xiàn)的做葵。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市心墅,隨后出現(xiàn)的幾起案子酿矢,更是在濱河造成了極大的恐慌,老刑警劉巖怎燥,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瘫筐,死亡現(xiàn)場離奇詭異,居然都是意外死亡铐姚,警方通過查閱死者的電腦和手機策肝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來隐绵,“玉大人之众,你說我怎么就攤上這事∫佬恚” “怎么了棺禾?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長峭跳。 經(jīng)常有香客問我膘婶,道長缺前,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任悬襟,我火速辦了婚禮衅码,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘脊岳。我一直安慰自己逝段,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布逸绎。 她就那樣靜靜地躺著惹恃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪棺牧。 梳的紋絲不亂的頭發(fā)上巫糙,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音颊乘,去河邊找鬼参淹。 笑死,一個胖子當(dāng)著我的面吹牛乏悄,可吹牛的內(nèi)容都是我干的浙值。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼檩小,長吁一口氣:“原來是場噩夢啊……” “哼开呐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起规求,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤筐付,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后阻肿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瓦戚,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年丛塌,在試婚紗的時候發(fā)現(xiàn)自己被綠了较解。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡赴邻,死狀恐怖印衔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情乍楚,我是刑警寧澤当编,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站徒溪,受9級特大地震影響忿偷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜臊泌,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一鲤桥、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧渠概,春花似錦茶凳、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至猪狈,卻和暖如春箱沦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背雇庙。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工谓形, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人疆前。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓寒跳,卻偏偏與公主長得像,于是被迫代替她去往敵國和親竹椒。 傳聞我的和親對象是個殘疾皇子童太,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 異步編程對JavaScript語言太重要。Javascript語言的執(zhí)行環(huán)境是“單線程”的胸完,如果沒有異步編程书释,根本...
    呼呼哥閱讀 7,298評論 5 22
  • 弄懂js異步 講異步之前,我們必須掌握一個基礎(chǔ)知識-event-loop舶吗。 我們知道JavaScript的一大特點...
    DCbryant閱讀 2,695評論 0 5
  • 本文首發(fā)在個人博客:http://muyunyun.cn/posts/7b9fdc87/ 提到 Node.js, ...
    牧云云閱讀 1,679評論 0 3
  • 本文的示例代碼參考這里的async 目錄 引言 callbackasync ?PromisePromise對象bl...
    諾之林閱讀 498評論 1 5
  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持征冷,譯者再次奉上一點點福利:阿里云產(chǎn)品券,享受所有官網(wǎng)優(yōu)惠誓琼,并抽取幸運大...
    HetfieldJoe閱讀 6,372評論 9 19