摘要:以往的異步方法無外乎回調函數和。出錯了出錯了總結接口遍歷器對象除了具有方法,還可以具有方法和方法。函數調用函數,返回一個遍歷器對象,代表函數的內部指針。
引言
接觸過Ajax請求的會遇到過異步調用的問題,為了保證調用順序的正確性,一般我們會在回調函數中調用,也有用到一些新的解決方案如Promise相關的技術。
在異步編程中,還有一種常用的解決方案,它就是Generator生成器函數。顧名思義,它是一個生成器,它也是一個狀態機,內部擁有值及相關的狀態,生成器返回一個迭代器Iterator對象,我們可以通過這個迭代器,手動地遍歷相關的值、狀態,保證正確的執行順序。
Iterator接口什么是Iterator接口
遍歷器(Iterator)就是這樣一種機制。它是一種接口,為各種不同的數據結構提供統一的訪問機制。任何數據結構只要部署Iterator接口,就可以完成遍歷操作(即依次處理該數據結構的所有成員)。
Iterator的作用
為各種數據結構,提供一個統一的、簡便的訪問接口
使得數據結構的成員能夠按某種次序排列
ES6 創造了一種新的遍歷命令for...of循環,Iterator接口主要供for...of消費。
Iterator實現
function makeIterator(array) { var nextIndex = 0; return { next: function() { return nextIndex < array.length ? {value: array[nextIndex++], done: false} : {value: undefined, done: true}; } }; } var it = makeIterator(["a", "b"]); it.next() // { value: "a", done: false } it.next() // { value: "b", done: false } it.next() // { value: undefined, done: true }
原生具備Iterator接口的數據結構
Array
Map
Set
String
TypedArray
函數的 arguments 對象
NodeList 對象
查看一下Map下面的所掛載的Iterator。
let map = new Map(); console.log(map.__proto__);
輸出結果:
clear:? clear() constructor:? Map() delete:? delete() entries:? entries() forEach:? forEach() get:? () has:? has() keys:? keys() set:? () size:(...) values:? values() Symbol(Symbol.iterator):? entries() Symbol(Symbol.toStringTag):"Map" get size:? size() __proto__:Object
如何為Object部署一個Iterator接口
function iteratorObject(obj){ let keys = Object.keys(obj); let index = -1; return { next(){ index++; return index通過上面的方法可以簡單的為Object部署了一個Iterator接口。
Generator函數Generator是ES6的新特性,通過yield關鍵字,可以讓函數的執行流掛起,那么便為改變執行流程提供了可能。
Generator語法dome:
function * greneratorDome(){ yield "Hello"; yield "World"; return "ending"; } let grenDome = greneratorDome(); console.log(grenDome)上面的代碼中定義了一個Generator函數,獲取到了函數返回的對象。下面是其輸出結果。
原型鏈:
greneratorDome {} __proto__:Generator __proto__:Generator constructor:GeneratorFunction {prototype: Generator, constructor: ?, Symbol(Symbol.toStringTag): "GeneratorFunction"} next:? next() return:? return() throw:? throw() Symbol(Symbol.toStringTag):"Generator" __proto__:Object [[GeneratorStatus]]:"suspended" [[GeneratorFunction]]:?* greneratorDome() [[GeneratorReceiver]]:Window [[GeneratorLocation]]:test.html:43 [[Scopes]]:Scopes[3] 通過上面的輸出結果可以看的出來,沿著原型鏈向上查找就存在一個next方法,這個方法與Iterator接口返回的結果是大同小異的。
繼續延續dome代碼,并使用next方法向下執行。
function * greneratorDome(){ yield "Hello"; yield "World"; return "Ending"; } let grenDome = greneratorDome(); console.log(grenDome.next()); // {value: "Hello", done: false} console.log(grenDome.next()); // {value: "World", done: false} console.log(grenDome.next()); // {value: "Ending", done: true} console.log(grenDome.next()); // {value: undefined, done: true}在最開始的地方有提到過Generator函數,最后返回的是一個Iterator對象,這也就不難理解了。
異步的Generatordome
function a (){ setTimeout(() => { alert("我是后彈出"); },1000) } function b (){ alsert("我是先彈出"); } function * grenDome (){ yield a(); yield b(); } let gren = grenDome(); gren.next(); gren.next(); // 輸出結果 // 我是先彈出 // 我是后彈出結合Promise
function a (){ return new Promise((resolve,reject) => { setTimeOut(() => { console.log(1) resolve("a"); }) }) } function b (){ return new Promise((resolve,reject) => { console.log(2) resolve("b"); }) } function * grenDome (){ yield a(); yield b(); return new Promise((resolve,reject) => { resolve("grenDome內部") }) } let gren = grenDome(); // console.log(gren.next()) // {value: Promise, done: false} // console.log(gren.next()) // {value: Promise, done: false} // console.log(gren.next()) // {value: Promise, done: true} // console.log(gren.next()) // {value: undefined, done: true} gren.next().value.then((res) => { console.log(res); // a函數 }) gren.next().value.then((res) => { console.log(res); // b函數 }) gren.next().value.then((res) => { console.log(res); // grenDome內部 }) // 輸出結果 // a // b // grenDome內部在上面的代碼中有一點是需要注意的,在grenDome函數里面最后return出去了一個Promise,但是在輸出的時候雖然done屬性已經為true但是value里面仍然會存有一個promise對象,實際上done表示的是對應yield關鍵字的函數已經遍歷完成了。
Async/AwaitAsync/await 是Javascript編寫異步程序的新方法。以往的異步方法無外乎回調函數和Promise。但是Async/await建立于Promise之上,換句話來說使用了Generator函數做了語法糖。
async函數就是隧道盡頭的亮光,很多人認為它是異步操作的終極解決方案。
什么是Async/Awaitasync顧名思義是“異步”的意思,async用于聲明一個函數是異步的。而await從字面意思上是“等待”的意思,就是用于等待異步完成。并且await只能在async函數中使用。
Async/Await語法function timeout(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); }; async function asyncPrint(value, ms) { await timeout(ms); console.log(value); }; asyncPrint("hello world",2000); // 在2000ms之后輸出`hello world`返回Promse對象通常async、await都是跟隨Promise一起使用的。為什么這么說呢?因為async返回的都是一個Promise對象同時async適用于任何類型的函數上。這樣await得到的就是一個Promise對象,如果不是Promise對象的話那async返回的是什么就是什么。
async function f() { return "hello world"; } f().then(v => console.log(v)); // hello worldasync函數返回一個Promise對象,可以使用then方法添加回調函數。當函數執行的時候,一旦遇到await就會先返回,等到異步操作完成,再接著執行函數體內后面的語句。
function a(){ return new Promise((resolve,reject) => { console.log("a函數") resolve("a函數") }) } function b (){ return new Promise((resolve,reject) => { console.log("b函數") resolve("b函數") }) } async function dome (){ let A = await a(); let B = await b(); return Promise.resolve([A,B]); } dome().then((res) => { console.log(res); });執行機制前面已經說過await是等待的意思,之后等前面的代碼執行完成之后才會繼續向下執行。
function a(){ return new Promise((resolve,reject) => { resolve("a"); console.log("a:不行") }) } function b (){ return new Promise((resolve,reject) => { resolve("b"); console.log("b:不行"); }) } async function dome (){ await a(); await b(); console.log("雖然我在后面,但是我想要先執行可以么?") } dome(); // 輸出結果 // a:不行 // b:不行 // 雖然我在后面,但是我想要先執行可以么?另外一個列子
function timeout1(ms) { return new Promise((resolve) => { setTimeout(() => { console.log("timeout1") resolve(); },ms); }); }; function timeout2(ms) { return new Promise((resolve) => { setTimeout(() => { console.log("timeout2"); resolve(); },ms); }); }; async function asyncPrint() { await timeout1(1000); await timeout2(2000); }; asyncPrint().then((res) => { console.log(res); }).catch((err) => { console.log(err) }) // 1s 后輸出timeout1 // 3s 后輸出timeout2 // undefinedasync、await錯誤處理JavaScript異步請求肯定會有請求失敗的情況,上面也說到了async返回的是一個Promise對象。既然是返回一個Promise對象的話那處理當異步請求發生錯誤的時候我們就要處理reject的狀態了。
在Promise中當請求reject的時候我們可以使用catch。為了保持代碼的健壯性使用async、await的時候我們使用try catch來處理錯誤。
async function f() { await Promise.reject("出錯了"); await Promise.resolve("hello world"); } async function b() { try { await f(); } catch(err) { console.log(err); } } b(); // 出錯了總結Iterator接口
遍歷器對象除了具有next方法,還可以具有return方法和throw方法。如果你自己寫遍歷器對象生成函數,那么next方法是必須部署的,return方法和throw方法是否部署是可選的。
Es6提供很多API都是基于Iterator接口,比如解構,for...of循環,拓展運算等。
Generator函數
調用Generator函數,返回一個遍歷器對象,代表Generator函數的內部指針。以后每次調用遍歷器對象的next方法,就會返回一個有著value和done兩個屬性的對象。
value屬性表示當前的內部狀態的值,是yield語句后面那個表達式的值;done屬性是一個布爾值,表示是否遍歷結束Async/Await
Async/await是近些年來JavaScript最具革命性的新特性之一。他讓讀者意識到使用Promise存在的一些問題,并提供了自身來代替Promise的方案。他使得異步代碼變的不再明顯,我們好不容易已經學會并習慣了使用回調函數或者then來處理異步。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/108516.html
摘要:從開始,就在引入新功能,來幫助更簡單的方法來處理異步編程,幫助我們遠離回調地獄。而則是為了更簡潔的使用而提出的語法,相比這種的實現方式,更為專注,生來就是為了處理異步編程。 從Promise開始,JavaScript就在引入新功能,來幫助更簡單的方法來處理異步編程,幫助我們遠離回調地獄。 Promise是下邊要講的Generator/yield與async/await的基礎,希望你已...
摘要:而函數的命令后面則可以是或者原始類型的值,,,但這時等同于同步操作返回值是。拋出的錯誤而會被方法回調函數接收到。 ES7 提出的async 函數,終于讓 JavaScript 對于異步操作有了終極解決方案。No more callback hell。async 函數是 Generator 函數的語法糖。使用 關鍵字 async 來表示,在函數內部使用 await 來表示異步。想較于 G...
摘要:結果輸出可以看出函數返回的是一個對象,如果函數中一個直接量,函數會封裝成對象返回,而如果沒有返回值時,函數會返回在沒有結合時,函數會立即執行,返回一個對象。 JS中的異步操作從最初的回調函數演進到Promise,再到Generator,都是逐步的改進,而async函數的出現仿佛看到了異步方案的終點,用同步的方式寫異步。showImg(https://segmentfault.com/i...
摘要:標準引入了函數,使得異步操作變得更加方便。在異步處理上,函數就是函數的語法糖。在實際項目中,錯誤處理邏輯可能會很復雜,這會導致冗余的代碼。的出現使得就可以捕獲同步和異步的錯誤。如果有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。 async ES2017 標準引入了 async 函數,使得異步操作變得更加方便。 在異步處理上,async 函數就是 Generator 函數的語法糖。 ...
摘要:為了更加方便的處理異步操作問題,現在最新的前端框架生態都開始用上了和,有的甚至已經開始使用最新的語法了,這兩樣都是基于自動執行的原理。這里就簡單理解下自執行及語法原理一函數函數指的是能將執行結果傳入回調函數,并將該回調函數返回的函數。 為了更加方便的處理異步操作問題,現在最新的前端框架生態都開始用上了Generator和yield,有的甚至已經開始使用最新的async、await語法了...
閱讀 3157·2021-09-30 09:47
閱讀 2012·2021-09-22 16:04
閱讀 2280·2021-09-22 15:44
閱讀 2538·2021-08-25 09:38
閱讀 542·2019-08-26 13:23
閱讀 1229·2019-08-26 12:20
閱讀 2813·2019-08-26 11:59
閱讀 1081·2019-08-23 18:40