国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

使用Promise解決多層異步調(diào)用的簡單學(xué)習(xí)心得

2019-11-20 10:01:48
字體:
供稿:網(wǎng)友

前言

第一次接觸到Promise這個(gè)東西,是2012年微軟發(fā)布Windows8操作系統(tǒng)后抱著作死好奇的心態(tài)研究用html5寫Metro應(yīng)用的時(shí)候。當(dāng)時(shí)配合html5提供的WinJS庫里面的異步接口全都是Promise形式,這對那時(shí)候剛剛畢業(yè)一點(diǎn)javascript基礎(chǔ)都沒有的我而言簡直就是天書。我當(dāng)時(shí)想的是,微軟又在腦洞大開的瞎搗鼓了。

結(jié)果沒想到,到了2015年,Promise居然寫進(jìn)ES6標(biāo)準(zhǔn)里面了。而且一項(xiàng)調(diào)查顯示,js程序員們用這玩意用的還挺high。

諷刺的是,作為早在2012年就在Metro應(yīng)用開發(fā)接口里面廣泛使用Promise的微軟,其自家瀏覽器IE直到2015年壽終正寢了都還不支持Promise,看來微軟不是沒有這個(gè)技術(shù),而是真的對IE放棄治療了。。。

現(xiàn)在回想起來,當(dāng)時(shí)看到Promise最頭疼的,就是初學(xué)者看起來匪夷所思,也是最被js程序員廣為稱道的特性:then函數(shù)調(diào)用鏈。

then函數(shù)調(diào)用鏈,從其本質(zhì)上而言,就是對多個(gè)異步過程的依次調(diào)用,本文就從這一點(diǎn)著手,對Promise這一特性進(jìn)行研究和學(xué)習(xí)。

Promise解決的問題

考慮如下場景,函數(shù)延時(shí)2秒之后打印一行日志,再延時(shí)3秒打印一行日志,再延時(shí)4秒打印一行日志,這在其他的編程語言當(dāng)中是非常簡單的事情,但是到了js里面就比較費(fèi)勁,代碼大約會寫成下面的樣子:

var myfunc = function() {    setTimeout(function() {    console.log("log1");    setTimeout(function() {      console.log("log2");      setTimeout(function() {        console.log("log3");      }, 4000);    }, 3000);   }, 2000);}

由于嵌套了多層回調(diào)結(jié)構(gòu),這里形成了一個(gè)典型的金字塔結(jié)構(gòu)。如果業(yè)務(wù)邏輯再復(fù)雜一些,就會變成令人聞風(fēng)喪膽的回調(diào)地獄。

如果意識比較好,知道提煉出簡單的函數(shù),那么代碼差不多是這個(gè)樣子:

var func1 = function() {  setTimeout(func2, 2000);};var func2 = function() {  console.log("log1");  setTimeout(func3, 3000);};var func3 = function() {  console.log("log2");  setTimeout(func4, 4000);};var func4 = function() {  console.log("log3");};

這樣看起來稍微好一點(diǎn)了,但是總覺得有點(diǎn)怪怪的。。。好吧,其實(shí)我js水平有限,說不上來為什么這樣寫不好。如果你知道為什么這樣寫不太好所以發(fā)明了Promise,請告訴我。

現(xiàn)在讓我們言歸正傳,說說Promise這個(gè)東西。

Promise的描述

這里請?jiān)试S我引用MDN對Promise的描述:

Promise 對象用于延遲(deferred) 計(jì)算和異步(asynchronous ) 計(jì)算.。一個(gè)Promise對象代表著一個(gè)還未完成,但預(yù)期將來會完成的操作。

Promise 對象是一個(gè)返回值的代理,這個(gè)返回值在promise對象創(chuàng)建時(shí)未必已知。它允許你為異步操作的成功或失敗指定處理方法。 這使得異步方法可以像同步方法那樣返回值:異步方法會返回一個(gè)包含了原返回值的 promise 對象來替代原返回值。

Promise對象有以下幾種狀態(tài):

•pending: 初始狀態(tài), 非 fulfilled 或 rejected。
•fulfilled: 成功的操作。
•rejected: 失敗的操作。

pending狀態(tài)的promise對象既可轉(zhuǎn)換為帶著一個(gè)成功值的fulfilled 狀態(tài),也可變?yōu)閹е粋€(gè)失敗信息的 rejected 狀態(tài)。當(dāng)狀態(tài)發(fā)生轉(zhuǎn)換時(shí),promise.then綁定的方法(函數(shù)句柄)就會被調(diào)用。(當(dāng)綁定方法時(shí),如果 promise對象已經(jīng)處于 fulfilled 或 rejected 狀態(tài),那么相應(yīng)的方法將會被立刻調(diào)用, 所以在異步操作的完成情況和它的綁定方法之間不存在競爭條件。)

更多關(guān)于Promise的描述和示例可以參考MDN的Promise條目,或者M(jìn)SDN的Promise條目。

嘗試使用Promise解決我們的問題

基于以上對Promise的了解,我們知道可以使用它來解決多層回調(diào)嵌套后的代碼蠢笨難以維護(hù)的問題。關(guān)于Promise的語法和參數(shù)上面給出的兩個(gè)鏈接已經(jīng)說的很清楚了,這里不重復(fù),直接上代碼。

我們先來嘗試一個(gè)比較簡單的情況,只執(zhí)行一次延時(shí)和回調(diào):

new Promise(function(res, rej) {  console.log(Date.now() + " start setTimeout");  setTimeout(res, 2000);}).then(function() {  console.log(Date.now() + " timeout call back");});

看起來和MSDN里的示例也沒什么區(qū)別,執(zhí)行結(jié)果如下:

$ node promisTest.js1450194136374 start setTimeout1450194138391 timeout call back

那么如果我們要再做一個(gè)延時(shí)呢,那么我可以這樣寫:

new Promise(function(res, rej) {  console.log(Date.now() + " start setTimeout 1");  setTimeout(res, 2000);}).then(function() {  console.log(Date.now() + " timeout 1 call back");  new Promise(function(res, rej) {    console.log(Date.now() + " start setTimeout 2");    setTimeout(res, 3000);  }).then(function() {    console.log(Date.now() + " timeout 2 call back");  })});

似乎也能正確運(yùn)行:

$ node promisTest.js1450194338710 start setTimeout 11450194340720 timeout 1 call back1450194340720 start setTimeout 21450194343722 timeout 2 call back

不過代碼看起來蠢萌蠢萌的是不是,而且隱約又在搭金字塔了。這和引入Promise的目的背道而馳。

那么問題出在哪呢?正確的姿勢又是怎樣的?

答案藏在then函數(shù)以及then函數(shù)的onFulfilled(或者叫onCompleted)回調(diào)函數(shù)的返回值里面。

首先明確的一點(diǎn)是,then函數(shù)會返回一個(gè)新的Promise變量,你可以再次調(diào)用這個(gè)新的Promise變量的then函數(shù),像這樣:

new Promise(...).then(...)  .then(...).then(...).then(...)...

而then函數(shù)返回的是什么樣的Promies,取決于onFulfilled回調(diào)的返回值。

事實(shí)上,onFulfilled可以返回一個(gè)普通的變量,也可以是另一個(gè)Promise變量。

如果onFulfilled返回的是一個(gè)普通的值,那么then函數(shù)會返回一個(gè)默認(rèn)的Promise變量。執(zhí)行這個(gè)Promise的then函數(shù)會使Promise立即被滿足,執(zhí)行onFulfilled函數(shù),而這個(gè)onFulfilled的入?yún)ⅲ词巧弦粋€(gè)onFulfilled的返回值。

而如果onFulfilled返回的是一個(gè)Promise變量,那個(gè)這個(gè)Promise變量就會作為then函數(shù)的返回值。

關(guān)于then函數(shù)和onFulfilled函數(shù)的返回值的這一系列設(shè)定,MDN和MSDN上的文檔都沒有明確的正面描述,至于ES6官方文檔ECMAScript 2015 (6th Edition, ECMA-262)。。。我的水平有限實(shí)在看不懂,如果哪位高手能解釋清楚官方文檔里面對著兩個(gè)返回值的描述,請一定留言指教!!!

所以以上為我的自由發(fā)揮,語言組織的有點(diǎn)拗口,上代碼看一下大家就明白了。

首先是返回普通變量的情況:

new Promise(function(res, rej) {  console.log(Date.now() + " start setTimeout 1");  setTimeout(res, 2000);}).then(function() {  console.log(Date.now() + " timeout 1 call back");  return 1024;}).then(function(arg) {  console.log(Date.now() + " last onFulfilled return " + arg);  });

以上代碼執(zhí)行結(jié)果為:

$ node promisTest.js1450277122125 start setTimeout 11450277124129 timeout 1 call back1450277124129 last onFulfilled return 1024

有點(diǎn)意思對不對,但這不是關(guān)鍵。關(guān)鍵是onFulfilled函數(shù)返回一個(gè)Promise變量可以使我們很方便的連續(xù)調(diào)用多個(gè)異步過程。比如我們可以這樣來嘗試連續(xù)做兩個(gè)延時(shí)操作:

new Promise(function(res, rej) {  console.log(Date.now() + " start setTimeout 1");  setTimeout(res, 2000);}).then(function() {  console.log(Date.now() + " timeout 1 call back");  return new Promise(function(res, rej) {    console.log(Date.now() + " start setTimeout 2");    setTimeout(res, 3000);  });}).then(function() {  console.log(Date.now() + " timeout 2 call back");});

執(zhí)行結(jié)果如下:

$ node promisTest.js1450277510275 start setTimeout 11450277512276 timeout 1 call back1450277512276 start setTimeout 21450277515327 timeout 2 call back

如果覺得這也沒什么了不起,那再多來幾次也不在話下:

 

new Promise(function(res, rej) {  console.log(Date.now() + " start setTimeout 1");  setTimeout(res, 2000);}).then(function() {  console.log(Date.now() + " timeout 1 call back");  return new Promise(function(res, rej) {    console.log(Date.now() + " start setTimeout 2");    setTimeout(res, 3000);  });}).then(function() {  console.log(Date.now() + " timeout 2 call back");  return new Promise(function(res, rej) {    console.log(Date.now() + " start setTimeout 3");    setTimeout(res, 4000);  });}).then(function() {  console.log(Date.now() + " timeout 3 call back");  return new Promise(function(res, rej) {    console.log(Date.now() + " start setTimeout 4");    setTimeout(res, 5000);  });}).then(function() {  console.log(Date.now() + " timeout 4 call back");});

 

$ node promisTest.js1450277902714 start setTimeout 11450277904722 timeout 1 call back1450277904724 start setTimeout 21450277907725 timeout 2 call back1450277907725 start setTimeout 31450277911730 timeout 3 call back1450277911730 start setTimeout 41450277916744 timeout 4 call back

可以看到,多個(gè)延時(shí)的回調(diào)函數(shù)被有序的排列下來,并沒有出現(xiàn)喜聞樂見的金字塔狀結(jié)構(gòu)。雖然代碼里面調(diào)用的都是異步過程,但是看起來就像是全部由同步過程構(gòu)成的一樣。這就是Promise帶給我們的好處。

如果你有把

主站蜘蛛池模板: 唐河县| 扶余县| 乐山市| 方正县| 谢通门县| 腾冲县| 花莲市| 游戏| 邛崃市| 托克逊县| 巴塘县| 华池县| 太白县| 道孚县| 永泰县| 额济纳旗| 临汾市| 肇东市| 汝州市| 曲麻莱县| 柳江县| 大城县| 许昌市| 清涧县| 吴忠市| 莫力| 河曲县| 东宁县| 兰坪| 都匀市| 巴南区| 虞城县| 龙江县| 玉树县| 闵行区| 万山特区| 新沂市| 余干县| 静宁县| 张家港市| 吉水县|