這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)ES6中Promise和Generator的區(qū)別是什,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
成都創(chuàng)新互聯(lián)是專業(yè)的伊寧網(wǎng)站建設(shè)公司,伊寧接單;提供成都做網(wǎng)站、網(wǎng)站設(shè)計(jì),網(wǎng)頁設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行伊寧網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來合作!
Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案“回調(diào)函數(shù)和事件”更合理和更強(qiáng)大。
所謂Promise,簡單說就是一個容器,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果。
從語法上說,Promise 是一個對象,從它可以獲取異步操作的消息。
Promise有兩個特點(diǎn):
對象的狀態(tài)不受外界影響。
Promise對象代表一個異步操作,有三種狀態(tài):Pending(進(jìn)行中)、Resolved(已完成,又稱 Fulfilled)和Rejected(已失敗)。
只有異步操作的結(jié)果,可以決定當(dāng)前是哪一種狀態(tài),任何其他操作都無法改變這個狀態(tài)。
一旦狀態(tài)改變,就不會再變,任何時候都可以得到這個結(jié)果。
Promise對象的狀態(tài)改變,只有兩種可能:從Pending變?yōu)镽esolved和從Pending變?yōu)镽ejected。
這與事件(Event)完全不同,事件的特點(diǎn)是,如果你錯過了它,再去監(jiān)聽,是得不到結(jié)果的。
Promise將異步操作以同步操作的流程表達(dá)出來,避免了層層嵌套的回調(diào)函數(shù)。
Promise對象提供統(tǒng)一的接口,使得控制異步操作更加容易。
無法取消Promise,一旦新建它就會立即執(zhí)行,無法中途取消。
如果不設(shè)置回調(diào)函數(shù),Promise內(nèi)部拋出的錯誤,不會反應(yīng)到外部。
當(dāng)處于Pending狀態(tài)時,無法得知目前進(jìn)展到哪一個階段(剛剛開始還是即將完成)。
Promise對象是一個構(gòu)造函數(shù),用來生成Promise實(shí)例:
var promise = new Promise(function(resolve, reject) { // ... some code if (/* 異步操作成功 */){ resolve(value); } else { reject(error); } } );
promise可以接then操作,then操作可以接兩個function參數(shù),第一個function的參數(shù)就是構(gòu)建Promise的時候resolve的value,第二個function的參數(shù)就是構(gòu)建Promise的reject的error。
promise.then(function(value) { // success }, function(error) { // failure } );
我們看一個具體的例子:
function timeout(ms){ return new Promise(((resolve, reject) => { setTimeout(resolve,ms,'done'); })) } timeout(100).then(value => console.log(value));
Promise中調(diào)用了一個setTimeout方法,并會定時觸發(fā)resolve方法,并傳入?yún)?shù)done。
最后程序輸出done。
Promise一經(jīng)創(chuàng)建就會立馬執(zhí)行。但是Promise.then中的方法,則會等到一個調(diào)用周期過后再次調(diào)用,我們看下面的例子:
let promise = new Promise(((resolve, reject) => { console.log('Step1'); resolve(); })); promise.then(() => { console.log('Step3'); }); console.log('Step2'); 輸出: Step1 Step2 Step3
then方法返回的是一個新的Promise實(shí)例(注意,不是原來那個Promise實(shí)例)。因此可以采用鏈?zhǔn)綄懛ǎ磘hen方法后面再調(diào)用另一個then方法.
getJSON("/users.json").then(function(json){ return json.name; }).then(function(name){ console.log(name); });
上面的代碼使用then方法,依次指定了兩個回調(diào)函數(shù)。第一個回調(diào)函數(shù)完成以后,會將返回結(jié)果作為參數(shù),傳入第二個回調(diào)函數(shù)
Promise.prototype.catch方法是.then(null, rejection)的別名,用于指定發(fā)生錯誤時的回調(diào)函數(shù)。
getJSON("/users.json").then(function(json){ return json.name; }).catch(function(error){ console.log(error); });
Promise 對象的錯誤具有“冒泡”性質(zhì),會一直向后傳遞,直到被捕獲為止。也就是說,錯誤總是會被下一個catch語句捕獲
getJSON("/users.json").then(function(json){ return json.name; }).then(function(name){ console.log(name); }).catch(function(error){ //處理前面所有產(chǎn)生的錯誤 console.log(error); });
Promise.all方法用于將多個Promise實(shí)例,包裝成一個新的Promise實(shí)例
var p = Promise.all([p1,p2,p3]);
只有p1、p2、p3的狀態(tài)都變成fulfilled,p的狀態(tài)才會變成fulfilled,此時p1、p2、p3的返回值組成一個數(shù)組,傳遞給p的回調(diào)函數(shù)。
只要p1、p2、p3之中有一個被rejected,p的狀態(tài)就變成rejected,此時第一個被reject的實(shí)例的返回值,會傳遞給p的回調(diào)函數(shù)。
Promise.race方法同樣是將多個Promise實(shí)例,包裝成一個新的Promise實(shí)例
var p = Promise.race([p1,p2,p3]);
只要p1、p2、p3之中有一個實(shí)例率先改變狀態(tài),p的狀態(tài)就跟著改變。那個率先改變的 Promise 實(shí)例的返回值,就傳遞給p的回調(diào)函數(shù).
Promise.resolve()將現(xiàn)有對象轉(zhuǎn)為Promise對象.
Promise.resolve('js'); //等價于 new Promise(resolve => resolve('js'));
那么什么樣的對象能夠轉(zhuǎn)化成為Promise對象呢?
參數(shù)是一個Promise實(shí)例
參數(shù)是一個thenable對象
參數(shù)不是具有then方法的對象,或根本就不是對象
不帶有任何參數(shù)
Promise.reject(reason)方法也會返回一個新的 Promise 實(shí)例,該實(shí)例的狀態(tài)為rejected
var p = Promise.reject('error'); //等價于 var p = new Promise((resolve,reject) => reject('error'));
Promise.reject()方法的參數(shù),會原封不動地作為reject的理由,變成后續(xù)方法的參數(shù)。這一點(diǎn)與Promise.resolve方法不一致
Promise對象的回調(diào)鏈,不管以then方法或catch方法結(jié)尾,要是最后一個方法拋出錯誤,都有可能無法捕捉到(因?yàn)镻romise內(nèi)部的錯誤不會冒泡到全局)。因此,我們可以提供一個done方法,總是處于回調(diào)鏈的尾端,保證拋出任何可能出現(xiàn)的錯誤
asyncFunc().then(f1).catch(f2).then(f3).done();
finally方法用于指定不管Promise對象最后狀態(tài)如何,都會執(zhí)行的操作。它與done方法的最大區(qū)別,它接受一個普通的回調(diào)函數(shù)作為參數(shù),該函數(shù)不管怎樣都必須執(zhí)行.
server.listen(1000).then(function(){ //do something }.finally(server.stop);
Generator 函數(shù)是 ES6 提供的一種異步編程解決方案
從語法上,首先可以把它理解成,Generator函數(shù)是一個狀態(tài)機(jī),封裝了多個內(nèi)部狀態(tài)
執(zhí)行 Generator 函數(shù)會返回一個遍歷器對象.
形式上,Generator 函數(shù)是一個普通函數(shù),但是有兩個特征。一是,function關(guān)鍵字與函數(shù)名之間有一個星號;二是,函數(shù)體內(nèi)部使用yield語句,定義不同的內(nèi)部狀態(tài)。
舉個例子:
function * helloWorldGenerator(){ yield 'hello'; yield 'world'; return 'ending'; } var gen = helloWorldGenerator();
輸出結(jié)果:
console.log(gen.next()); console.log(gen.next()); console.log(gen.next()); { value: 'hello', done: false } { value: 'world', done: false } { value: 'ending', done: true }
遍歷器對象的next方法的運(yùn)行邏輯如下:
(1)遇到y(tǒng)ield語句,就暫停執(zhí)行后面的操作,并將緊跟在yield后面的那個表達(dá)式的值,作為返回的對象的value屬性值。
(2)下一次調(diào)用next方法時,再繼續(xù)往下執(zhí)行,直到遇到下一個yield語句。
(3)如果沒有再遇到新的yield語句,就一直運(yùn)行到函數(shù)結(jié)束,直到return語句為止,并將return語句后面的表達(dá)式的值,作為返回的對象的value屬性值。
(4)如果該函數(shù)沒有return語句,則返回的對象的value屬性值為undefined。
注意,yield句本身沒有返回值,或者說總是返回undefined。
next方法可以帶一個參數(shù),該參數(shù)就會被當(dāng)作上一個yield語句的返回值。
function * f() { for( let i =0; true; i++){ let reset = yield i; if(reset){ i = -1; } } } let g = f(); console.log(g.next()); console.log(g.next()); console.log(g.next(true));
輸出結(jié)果:
{ value: 0, done: false } { value: 1, done: false } { value: 0, done: false }
可以看到最后的一步,我們使用next傳入的true替代了i的值,最后導(dǎo)致i= -1 + 1 = 0.
我們再看一個例子:
function * f2(x){ var y = 2 * ( yield ( x + 1)); var z = yield (y / 3); return (x + y + z); } var r1= f2(5); console.log(r1.next()); console.log(r1.next()); console.log(r1.next()); var r2= f2(5); console.log(r2.next()); console.log(r2.next(12)); console.log(r2.next(13));
輸出結(jié)果:
{ value: 6, done: false } { value: NaN, done: false } { value: NaN, done: true } { value: 6, done: false } { value: 8, done: false } { value: 42, done: true }
如果next不傳值的話,yield本身是沒有返回值的,所以我們會得到NaN。
但是如果next傳入特定的值,則該值會替換該yield,成為真正的返回值。
如果在 Generator 函數(shù)內(nèi)部,調(diào)用另一個 Generator 函數(shù),默認(rèn)情況下是沒有效果的
function * a1(){ yield 'a'; yield 'b'; } function * b1(){ yield 'x'; a1(); yield 'y'; } for(let v of b1()){ console.log(v); }
輸出結(jié)果:
x y
可以看到,在b1中調(diào)用a1是沒有效果的。
將上面的例子修改一下:
function * a1(){ yield 'a'; yield 'b'; } function * b1(){ yield 'x'; yield * a1(); yield 'y'; } for(let v of b1()){ console.log(v); }
輸出結(jié)果:
x a b y
Generator函數(shù)的暫停執(zhí)行的效果,意味著可以把異步操作寫在yield語句里面,等到調(diào)用next方法時再往后執(zhí)行。這實(shí)際上等同于不需要寫回調(diào)函數(shù)了,因?yàn)楫惒讲僮鞯暮罄m(xù)操作可以放在yield語句下面,反正要等到調(diào)用next方法時再執(zhí)行。所以,Generator函數(shù)的一個重要實(shí)際意義就是用來處理異步操作,改寫回調(diào)函數(shù)。
我們看一個怎么通過Generator來獲取一個Ajax的結(jié)果。
function * ajaxCall(){ let result = yield request("http://www.flydean.com"); let resp = JSON.parse(result); console.log(resp.value); } function request(url){ makeAjaxCall(url, function(response){ it.next(response); }); } var it = ajaxCall(); it.next();
我們使用一個yield來獲取異步執(zhí)行的結(jié)果。但是我們?nèi)绾螌⑦@個yield傳給result變量呢?要記住yield本身是沒有返回值的。
我們需要調(diào)用generator的next方法,將異步執(zhí)行的結(jié)果傳進(jìn)去。這就是我們在request方法中做的事情。
什么是異步應(yīng)用呢?
所謂"異步",簡單說就是一個任務(wù)不是連續(xù)完成的,可以理解成該任務(wù)被人為分成兩段,先執(zhí)行第一段,然后轉(zhuǎn)而執(zhí)行其他任務(wù),等做好了準(zhǔn)備,再回過頭執(zhí)行第二段。
比如,有一個任務(wù)是讀取文件進(jìn)行處理,任務(wù)的第一段是向操作系統(tǒng)發(fā)出請求,要求讀取文件。然后,程序執(zhí)行其他任務(wù),等到操作系統(tǒng)返回文件,再接著執(zhí)行任務(wù)的第二段(處理文件)。這種不連續(xù)的執(zhí)行,就叫做異步。
相應(yīng)地,連續(xù)的執(zhí)行就叫做同步。由于是連續(xù)執(zhí)行,不能插入其他任務(wù),所以操作系統(tǒng)從硬盤讀取文件的這段時間,程序只能干等著。
ES6誕生以前,異步編程的方法,大概有下面四種。 回調(diào)函數(shù) 事件監(jiān)聽 發(fā)布/訂閱 Promise 對象
fs.readFile(fileA, 'utf-8', function(error,data){ fs.readFile(fileB, 'utf-8', function(error,data){ } })
如果依次讀取兩個以上的文件,就會出現(xiàn)多重嵌套。代碼不是縱向發(fā)展,而是橫向發(fā)展,很快就會亂成一團(tuán),無法管理。因?yàn)槎鄠€異步操作形成了強(qiáng)耦合,只要有一個操作需要修改,它的上層回調(diào)函數(shù)和下層回調(diào)函數(shù),可能都要跟著修改。這種情況就稱為"回調(diào)函數(shù)地獄"(callback hell)。
Promise 對象就是為了解決這個問題而提出的。它不是新的語法功能,而是一種新的寫法,允許將回調(diào)函數(shù)的嵌套,改成鏈?zhǔn)秸{(diào)用。
let readFile = require('fs-readfile-promise'); readFile(fileA).then(function(){ return readFile(fileB); }).then(function(data){ console.log(data); })
在講Thunk函數(shù)之前,我們講一下函數(shù)的調(diào)用有兩種方式,一種是傳值調(diào)用,一種是傳名調(diào)用。
"傳值調(diào)用"(call by value),即在進(jìn)入函數(shù)體之前,就計(jì)算x + 5的值(等于6),再將這個值傳入函數(shù)f。C語言就采用這種策略。
“傳名調(diào)用”(call by name),即直接將表達(dá)式x + 5傳入函數(shù)體,只在用到它的時候求值。
編譯器的“傳名調(diào)用”實(shí)現(xiàn),往往是將參數(shù)放到一個臨時函數(shù)之中,再將這個臨時函數(shù)傳入函數(shù)體。這個臨時函數(shù)就叫做 Thunk 函數(shù)。
舉個例子:
function f(m){ return m * 2; } f(x + 5);
上面的代碼等于:
var thunk = function () { return x + 5; } function f(thunk){ return thunk() * 2; }
在 JavaScript 語言中,Thunk函數(shù)替換的不是表達(dá)式,而是多參數(shù)函數(shù),將其替換成一個只接受回調(diào)函數(shù)作為參數(shù)的單參數(shù)函數(shù)。
怎么解釋呢?
比如nodejs中的:
fs.readFile(filename,[encoding],[callback(err,data)])
readFile接收3個參數(shù),其中encoding是可選的。我們就以兩個參數(shù)為例。
一般來說,我們這樣調(diào)用:
fs.readFile(fileA,callback);
那么有沒有辦法將其改寫成為單個參數(shù)的function的級聯(lián)調(diào)用呢?
var Thunk = function (fn){ return function (...args){ return functon (callback){ return fn.call(this,...args, callback); } } } var readFileThunk = Thunk(fs.readFile); readFileThunk(fileA)(callback);
可以看到上面的Thunk將兩個參數(shù)的函數(shù)改寫成為了單個參數(shù)函數(shù)的級聯(lián)方式?;蛘哒fThunk是接收一個callback并執(zhí)行方法的函數(shù)。
這樣改寫有什么用呢?Thunk函數(shù)現(xiàn)在可以用于 Generator 函數(shù)的自動流程管理。
之前在講Generator的時候,如果Generator中有多個yield的異步方法,那么我們需要在next方法中傳入這些異步方法的執(zhí)行結(jié)果。
手動傳入異步執(zhí)行結(jié)果當(dāng)然是可以的。但是有沒有自動執(zhí)行的辦法呢?
let fs = require('fs'); let thunkify = require('thunkify'); let readFileThunk = thunkify(fs.readFile); let gen = function * (){ let r1 = yield readFileThunk('/tmp/file1'); console.log(r1.toString()); let r2 = yield readFileThunk('/tmp/file2'); console.log(r2.toString()); } let g = gen(); function run(fn){ let gen = fn(); function next (err, data){ let result = gen.next(data); if(result.done) return; result.value(next); } next(); } run(g);
gen.next返回的是一個對象,對象的value就是Thunk函數(shù),我們向Thunk函數(shù)再次傳入next callback,從而出發(fā)下一次的yield操作。
有了這個執(zhí)行器,執(zhí)行Generator函數(shù)方便多了。不管內(nèi)部有多少個異步操作,直接把 Generator 函數(shù)傳入run函數(shù)即可。當(dāng)然,前提是每一個異步操作,都要是Thunk函數(shù),也就是說,跟在yield命令后面的必須是Thunk函數(shù)。
上述就是小編為大家分享的ES6中Promise和Generator的區(qū)別是什了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
文章標(biāo)題:ES6中Promise和Generator的區(qū)別是什
本文鏈接:http://m.rwnh.cn/article26/gspccg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供搜索引擎優(yōu)化、自適應(yīng)網(wǎng)站、網(wǎng)站策劃、網(wǎng)站制作、云服務(wù)器、品牌網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)