多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > php教程 > NodeJs 暢談異步

NodeJs 暢談異步

來源:程序員人生   發布時間:2016-11-17 09:42:25 閱讀次數:2645次

我們都知道NodeJs是基于事件回調解理事件的,那我們常常會有這樣的疑問,比如我們會使用db或redis或fs等模塊去存儲我們的信息,當我們需要實時返回1個數據并作出處理的時候,異步的通知顯得不是那末人性化了,此時我們需要將異步變成同步,在此進程中我們有這樣幾個知識點要掌握以下,包括JavaScript中的生成器generartor函數,配合它的是yield關鍵字的暫停,thunk和promise的使用,和co模塊和框架thunkify的配合使用。

生成器Generartor

生成器函數是寫成:function* functionName(){} 的情勢,其本質也是1個函數,所以它具有普通函數所具有的所有特性。除此以外,它還具有以下有用特性:

1. 履行生成器函數后返回1個生成器(Generator),且生成用具有throw()方法,可手動拋出1個異常,也常被用于判斷是不是是生成器;

2. 在生成器函數內部可使用yield(或yield*),函數履行到yield的時候都會暫停履行,并返回yield的右值(函數上下文,如變量的綁定等信息會保存),通過生成器的next()方法會返回1個對象,含當前yield右側表達式的值(value屬性),和generator函數是不是已履行完(done屬性)等的信息。每次履行next()方法,都會從上次履行的yield的地方往下,直到遇到下1個yield并返回包括相干履行信息的對象后暫停,然后等待下1個next()的履行;

3. 生成器的next()方法返回的是包括yield右側表達式值及是不是履行終了信息的對象;而next()方法中的參數是上1個暫停處yield的返回值。var a = yield ... 該參數的值將賦值給a變量。

實例1:

function test(){ return 'b'; } function* func(){ var a = yield 'a'; console.log('gen:',a);// gen: undefined var b = yield test(); console.log('gen:',b);// gen: undefined } var func1 = func(); var a = func1.next(); console.log('next:', a);// next: { value: 'a', done: false } var b = func1.next(); console.log('next:', b);// next: { value: 'b', done: false } var c = func1.next(); console.log('next:', c);// next: { value: undefined, done: true }

根據上面說過的第3條履行準則:“生成器的next()方法返回的是包括yield右側表達式值及是不是履行終了信息的對象;而next()方法的參數是上1個暫停處yield的返回值”,由于我們沒有往生成器的next()中傳入任何值,所以:var a = yield ‘a’;中a的值為undefined。
那我們可以將例子略微修改下:

function test(){ return 'b'; } function* func(){ var a = yield 'a'; console.log('gen:',a);// gen:1 var b = yield test(); console.log('gen:',b);// gen:2 } var func2 = func(); var a = func2.next(); console.log('next:', a);// next: { value: 'a', done: false } var b = func2.next(1); console.log('next:', b);// next: { value: 'b', done: false } var c = func2.next(2); console.log('next:', c);// next: { value: undefined, done: true }

關于yield

普通yield實例:

function* outer() { yield 'begin'; yield inner(); yield 'end'; } function* inner() { yield 'inner'; } var it = outer(), v; v = it.next().value; console.log(v); // -> 輸出:begin v = it.next().value; console.log(v); // -> 輸出:{} console.log(v.toString()); // -> 輸出:[object Generator] v = it.next().value; console.log(v); // -> 輸出:end

代理yield實例:

function* outer() { yield 'begin'; /* * 這行等價于 yield 'inner';就是把inner里面的代碼替換過來。同時取得的rt恰好就是inner的返回值 */ var rt = yield* inner(); console.log(rt); // -> 輸出:return from inner yield 'end'; } function* inner() { yield 'inner'; return 'return from inner'; } var it = outer(), v; v = it.next().value; console.log(v); // -> 輸出:begin v = it.next().value; console.log(v); // -> 輸出:inner v = it.next().value; console.log(v); // -> 輸出:end

根據文檔的描寫,yield* 后面接受1個 iterable object 作為參數,然后去迭代(iterate)這個迭代器(iterable object),同時 yield* 本身這個表達式的值就是迭代器迭代完成時(done: true)的返回值。調用 generator function 會返回1個 generator object,這個對象本身也是1種 iterable object,所以,我們可使用 yield* generator_function() 這類寫法。
啊,好繞。在我實際的使用進程中,yield* 1般用來在1個 generator 函數里“履行”另外一個 generator 函數,并可以獲得其返回值。

關于yield和co

co(function* () { // yield promise var a = yield Promise.resolve(1); console.log(a); // -> 輸出:1 // yield thunk var b = yield later(10); console.log(b); // -> 輸出:10 // yield generator function var c = yield fn; console.log(c); // -> 輸出:fn_1 // yield generator var d = yield fn(5); console.log(d); // -> 輸出:fn_5 // yield array var e = yield [ Promise.resolve('a'), later('b'), fn, fn(5) ]; console.log(e); // -> 輸出:['a', 'b', 'fn_1', 'fn_5'] // yield object var f = yield { 'a': Promise.resolve('a'), 'b': later('b'), 'c': fn, 'd': fn(5) }; console.log(f); // -> 輸出:{a: 'a', b: 'b', c: 'fn_1', d: 'fn_5'} function* fn(n) { n = n || 1; var a = yield later(n); return 'fn_' + a; } function later(n, t) { t = t || 1000; return function(done) { setTimeout(function() { done(null, n); }, t); }; } }).catch(function(e) { console.error(e); });

通過上面的代碼,我們看清了1個事實,那就是co模塊中yield后面的只能是promise、generator、thunk函數等,并且他只是同步的,在之前我們討論過,如果我們想要這句代碼 var a = yield 'value' 的a變量賦值,那末我們需要在調用next的時候傳入參數值,但是在co模塊中,他會將promise的通知值(也就是resolve)、thunk函數的灰調函數的值賦值給a變量。

promise函數

Promise對象是CommonJS工作組提出的1種規范,目的是為異步操作提供統1接口。
那末,甚么是Promises?
首先,它是1個對象,也就是說與其他JavaScript對象的用法,沒有甚么兩樣;其次,它起到代理作用(proxy),充當異步操作與回調函數之間的中介。它使得異步操作具有同步操作的接口,使得程序具有正常的同步運行的流程,回調函數沒必要再1層層嵌套。
簡單說,它的思想是,每個異步任務立刻返回1個Promise對象,由因而立刻返回,所以可以采取同步操作的流程。這個Promises對象有1個then方法,允許指定回調函數,在異步任務完成后調用。
比如,異步操作f1返回1個Promise對象,它的回調函數f2寫法以下。(new Promise(f1)).then(f2);

// 傳統寫法 step1(function (value1) { step2(value1, function(value2) { step3(value2, function(value3) { step4(value3, function(value4) { // ... }); }); }); }); // Promises的寫法 (new Promise(step1)) .then(step2) .then(step3) .then(step4);

從上面代碼可以看到,采取Promises接口以后,程序流程變得非常清楚,10分易讀。
注意,為了便于理解,上面代碼的Promise對象的生成格式,做了簡化,真實的語法請參照下文。
總的來講,傳統的回調函數寫法使得代碼混成1團,變得橫向發展而不是向下發展。Promises規范就是為了解決這個問題而提出的,目標是使用正常的程序流程(同步),來處理異步操作。它先返回1個Promise對象,后面的操作以同步的方式,寄存在這個對象上面。等到異步操作有了結果,再履行前期存放在它上面的其他操作。
Promises本來只是社區提出的1個構想,1些外部函數庫率先實現了這個功能。ECMAScript 6將其寫入語言標準,因此目前JavaScript語言原生支持Promise對象。

關于Promise接口,前面說過,Promise接口的基本思想是,異步任務返回1個Promise對象。他只有3種狀態pending(未完成)、resolved(已完成)、rejected(失敗),那末當它返回時,只有成功和失敗兩個概念了,履行成功Promise對象傳回1個值,狀態變成resolved,異步操作失敗,Promise對象拋出1個毛病,狀態變成rejected。

Promise對象使用then方法添加回調函數。then方法可以接受兩個回調函數,第1個是異步操作成功時(變成resolved狀態)時的回調函數,第2個是異步操作失敗(變成rejected)時的回調函數(可以省略)。1旦狀態改變,就調用相應的回調函數。

po.then(console.log, console.error);
上面代碼中,Promise對象po使用then方法綁定兩個回調函數:操作成功時的回調函數console.log,操作失敗時的回調函數console.error(可以省略)。這兩個函數都接受異步操作傳回的值作為參數。固然了,then可支持鏈式編程。

上面代碼中,po的狀態1旦變成resolved,就順次調用后面每個then指定的回調函數,每步都必須等到前1步完成,才會履行。最后1個then方法的回調函數console.log和console.error,用法上有1點重要的區分。console.log只顯示回調函數step3的返回值,而console.error可以顯示step1、step2、step3當中任意1個產生的毛病。也就是說,假定step1操作失敗,拋出1個毛病,這時候step2和step3都不會再履行了(由于它們是操作成功的回調函數,而不是操作失敗的回調函數)。Promises對象開始尋覓,接下來第1個操作失敗時的回調函數,在上面代碼中是console.error。這就是說,Promises對象的毛病有傳遞性。

ES6提供了原生的Promise構造函數,用來生成Promise實例。

var promise = new Promise(function(resolve, reject) { // 異步操作的代碼 if (/* 異步操作成功 */){ resolve(value); } else { reject(error); } });
這里我們可能有點和then的用法沖突了,其實沒有,下面我們給出1個ajax配合使用promise的用法:
function search(term) { var url = 'http://example.com/search?q=' + term; var xhr = new XMLHttpRequest(); var result; var p = new Promise(function (resolve, reject) { xhr.open('GET', url, true); xhr.onload = function (e) { if (this.status === 200) { result = JSON.parse(this.responseText); resolve(result); } }; xhr.onerror = function (e) { reject(e); }; xhr.send(); }); return p; } search("paramValue").then(console.log, console.error);

此時很明白了吧。。。。。。

那我們說了這么多,我們也想promise配合co模塊使用1次,將其異步變成同步的使用方法就能夠了,實例以下:

var fs = require('fs'); var readFile = function (fileName){ return new Promise(function (resolve, reject){ fs.readFile(fileName, function(error, data){ if (error) reject(error); resolve(data); }); }); }; var gen = function* (){ var f1 = yield readFile('/etc/fstab'); var f2 = yield readFile('/etc/shells'); console.log(f1.toString()); console.log(f2.toString()); };
到此,明白了吧,這個配合co模塊實現了同步的操作了吧

關于promise的更多詳細講授,參照 http://javascript.ruanyifeng.com/advanced/promise.html官方,http://www.ruanyifeng.com/blog/2015/05/co.html阮1峰

這其中有幾點要注意的也就是,首先promise是1個將異步金字塔管理成為then的調用,他會立即返回1個狀態,當其中1個履行成功后,then去履行下1個異步回調函數,請注意它的3個狀態,其中1個api Promise.resolve(value),這個api只是的立刻馬上返回1個value給return使用。

thunk函數

co模塊可以將我們的生成器自動的去履行,而不需要我們手動進行next履行,但是在co的利用中,為了能像寫同步代碼那樣書寫異步代碼,比較多的使用方式是使用thunk函數(但不是唯1方式,還可以是:Promise)。比如讀取文件內容的1步函數fs.readFile()方法,轉化為thunk函數的方式以下:

function readFile(path, encoding){ return function(cb){ fs.readFile(path, encoding, cb); }; }

thunk函數具有以下兩個要素:

1. 有且只有1個參數是callback的函數;

2. callback的第1個參數是error。

其實去看看這兩點,在node中的回調函數都是可以寫成thunk函數去履行的。使用thunk函數,同時結合co我們就能夠像寫同步代碼那樣來寫書寫異步代碼,先來個例子感受下:

var co = require('co'), fs = require('fs'), Promise = require('es6-promise').Promise; function readFile(path, encoding){ return function(cb){ // thunk函數 fs.readFile(path, encoding, cb); }; } co(function* (){// 外面不可見,但在co內部其實已轉化成了promise.then().then()..鏈式調用的情勢 var a = yield readFile('a.txt', {encoding: 'utf8'}); console.log(a); // a var b = yield readFile('b.txt', {encoding: 'utf8'}); console.log(b); // b var c = yield readFile('c.txt', {encoding: 'utf8'}); console.log(c); // c return yield Promise.resolve(a+b+c); }).then(function(val){ console.log(val); // abc }).catch(function(error){ console.log(error); });
其實,對每次都去自己書寫1個thunk函數還是比較麻煩的,有1個框架thunkify可以幫我們輕松實現,修改后的代碼以下:
var co = require('co'), thunkify = require('thunkify'), fs = require('fs'), Promise = require('es6-promise').Promise; var readFile = thunkify(fs.readFile); co(function* (){// 外面不可見,但在co內部其實已轉化成了promise.then().then()..鏈式調用的情勢 var a = yield readFile('a.txt', {encoding: 'utf8'}); console.log(a); // a var b = yield readFile('b.txt', {encoding: 'utf8'}); console.log(b); // b var c = yield readFile('c.txt', {encoding: 'utf8'}); console.log(c); // c return yield Promise.resolve(a+b+c); }).then(function(val){ console.log(val); // abc }).catch(function(error){ console.log(error); });

辨別好thunkify的使用,它可以包括1個異步的具有回調函數的thunk函數,提供給co模塊使用。

那好,我們最后來1個NodeJs異步中的總結,首先由于生成器和yield可使程序暫停履行,但是其實不具有將異步函數的返回值返回給var a變量的能力,那末我們加入了co這個模塊來使用,co可以將yield后面的不管甚么回調異步的返回值return給變量a使用,但是有1個問題,yield后面如果有多個或更多的異步怎樣辦,我們就使用thunk和promise將所有的異步串行履行起來,最后加入co模塊,完善!再次推薦使用thunk,由于我們大多是情況下是需要1個異步函數的返回值,并且有thunkify框架的支持,而promise也是有1些異常的東西,比如,有時候你在拿去它的返回值的時候,不太容易,為何呢,then后如果寫了console.log,由于上1次的值傳入了log中,log并沒與傳出所以失去了結果,比如代碼好像是這樣的

var promise = new Promise(function(resolve, reject) { if (true){ //成功時,將2作為返回值,傳入下1個then的參數中,如果沒有下1個then,會將值村吃在promise._result中 resolve(2); } else { reject(error); } }); var result = promise._result;







生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 亚洲成人高清 | 中文国产成人精品少久久 | 一机毛片| 午夜大片免费男女爽爽影院久久 | 青青自拍视频一区二区三区 | 伊人中文在线 | 亚洲爽视频 | 夜夜影院未满十八勿进 | 日本欧美视频在线 | 最近中文字幕高清字幕在线视频 | 国产香蕉影视院 | 欧洲爱爱 | 大美香蕉伊在看欧美 | 东京干男人 | 亚洲三级自拍 | 美国免费高清一级毛片 | 国产精品成人观看视频网站 | 欧美激情_区二区三区 | 久久久久久久岛国免费观看 | 色噜噜视频影院 | 精品久久久久久久一区二区伦理 | 国产成人欧美 | 日本japanesexxxx人妖2 | 热灸灸这里只有精品 | 精品一区二区三区四区五区 | www免费看 | xxxx tube hd人妖| 欧美一区二区视频在线观看 | 欧美性受xxxx喷水性欧洲 | 国产免费一区二区三区免费视频 | 成人在线视频国产 | 国产亚洲精品久久久久久久久激情 | 亚洲清色 | 日韩欧美一区二区三区四区 | 亚洲午夜国产精品无卡 | 毛片的网址 | 国产不卡免费视频 | 伊人成人久久 | 美女福利在线 | 日本夜免费视频视频大片 | 国产精品深夜福利免费观看 |