摘要:讓我們使用它從數(shù)組中返回一個(gè)值數(shù)組在中,我們可以這樣做,這是一種更簡(jiǎn)單的方法最重要的部分是創(chuàng)建數(shù)組,該數(shù)組立即調(diào)用所有的我們?cè)谥骱瘮?shù)中等待這些。所以在我們真正等待完成之前,主函數(shù)就退出了。
原文:https://pouchdb.com/2015/03/0...
PouchDB最棘手的方面之一是它的API是異步的。在Stack Overflow、Github和IRC上,我看到了不少困惑的問(wèn)題,而且這些問(wèn)題通常是由對(duì)callbacks和promises的誤解造成的。 我們真的無(wú)能為力。PouchDB是對(duì)IndexedDB, WebSQL, LevelDB (in Node), and CouchDB (via Ajax)的抽象。所有這些API都是異步的;因此PouchDB必須是異步的。 然而,當(dāng)我想到優(yōu)雅的數(shù)據(jù)庫(kù)API時(shí),我仍然對(duì)LocalStorage的簡(jiǎn)單性感到震驚: if (!localStorage.foo) { localStorage.foo = "bar"; }; console.log(localStorage.foo); 要使用LocalStorage,您只需將它當(dāng)作一個(gè)神奇的javascript對(duì)象來(lái)保存數(shù)據(jù)。它使用的同步工具集與使用JavaScript本身時(shí)已經(jīng)習(xí)慣的工具集相同。 對(duì)于LocalStorage的所有錯(cuò)誤(https://www.html5rocks.com/en/tutorials/offline/quota-research/),這個(gè)API的人機(jī)工程學(xué)在很大程度上解釋了它的持續(xù)流行。人們一直在使用LocalStorage,因?yàn)樗芎?jiǎn)單,而且工作正常。Promises aren"t a panacea
對(duì)于PouchDB,我們可以嘗試通過(guò)promises來(lái)減輕異步API的復(fù)雜性,這當(dāng)然有助于我們擺脫pyramid of doom。 然而,promisey代碼仍然很難閱讀,因?yàn)閜romisey基本上是語(yǔ)言原語(yǔ)(如try、catch和return)的bolt-on替換: var db = new PouchDB("mydb"); db.post({}).then(function (result) { // post a new doc return db.get(result.id); // fetch the doc }).then(function (doc) { console.log(doc); // log the doc }).catch(function (err) { console.log(err); // log any errors }); 作為JavaScript開發(fā)人員,我們現(xiàn)在有兩個(gè)并行系統(tǒng)——sync and async——我們必須直截了當(dāng)?shù)赜涀∵@兩個(gè)系統(tǒng)。當(dāng)我們的控制流變得更復(fù)雜時(shí),情況會(huì)變得更糟,我們需要使用promise.all()和promise.resolve()等API。或者我們只是選擇了眾多幫助程序庫(kù)中的一個(gè),并祈禱我們能夠理解文檔。 直到最近,這是我們所能期望的最好的。但所有這些都隨ES7而改變。Enter ES7
如果我告訴你,有了ES7,你可以把上面的代碼改寫成這樣: let db = new PouchDB("mydb"); try { let result = await db.post({}); let doc = await db.get(result.id); console.log(doc); } catch (err) { console.log(err); } 如果我告訴你,多虧了Babel.js和Regenerator這樣的工具,你現(xiàn)在可以將其發(fā)展到ES5并在瀏覽器中運(yùn)行它了? 女士們先生們,請(qǐng)大家鼓掌,直到博文結(jié)束。 首先,讓我們看看ES7是如何完成這一驚人的壯舉的。Async functions
ES7為我們提供了一種新的函數(shù),async函數(shù)。在async函數(shù)內(nèi)部,我們有一個(gè)新的關(guān)鍵字wait,用于“wait for”一個(gè)promise: async function myFunction() { let result = await somethingThatReturnsAPromise(); console.log(result); // cool, we have a result } 如果promise resolves,我們可以在下一行立即與之交互。如果它拒絕了,那么就會(huì)拋出一個(gè)錯(cuò)誤。所以,try/catch實(shí)際上再次有效! async function myFunction() { try { await somethingThatReturnsAPromise(); } catch (err) { console.log(err); // oh noes, we got an error } } 這允許我們編寫表面上看起來(lái)是同步的,但實(shí)際上是異步的代碼。API返回一個(gè)promise 而不是阻塞事件循環(huán)這一事實(shí)只是一個(gè)實(shí)現(xiàn)細(xì)節(jié)。 還記得你什么時(shí)候可以只使用"return"和"try/catch"嗎? 最好的一點(diǎn)是,我們今天可以把它和任何一個(gè)可以返回promises 的庫(kù)一起使用。PouchDB就是這樣一個(gè)庫(kù),所以讓我們用它來(lái)測(cè)試我們的理論。Managing errors and return values
首先,考慮一下pouchdb中的一個(gè)常見習(xí)慣用法:如果文檔存在,我們希望按_id獲取一個(gè)文檔,如果不存在,則返回一個(gè)新文檔。 有了promises,你就必須寫下這樣的東西: db.get("docid").catch(function (err) { if (err.name === "not_found") { return {}; // new doc } throw err; // some error other than 404 }).then(function (doc) { console.log(doc); }) 對(duì)于異步函數(shù),這將變成: let doc; try { doc = await db.get("docid"); } catch (err) { if (err.name === "not_found") { doc = {}; } else { throw err; // some error other than 404 } } console.log(doc); 可讀性更高!如果db.get()直接返回一個(gè)文檔而不是一個(gè)promise,那么這幾乎是我們編寫的代碼。唯一的區(qū)別是,當(dāng)我們調(diào)用任何promise-returning函數(shù)時(shí),必須添加wait關(guān)鍵字。Potential gotchas
我在玩這個(gè)的時(shí)候遇到了一些微妙的問(wèn)題,所以很高興能意識(shí)到它們。 首先,當(dāng)您等待某件事情時(shí),您需要在一個(gè)async函數(shù)中。因此,如果您的代碼嚴(yán)重依賴PouchDB,您可能會(huì)發(fā)現(xiàn)您編寫了許多async函數(shù),但很少有常規(guī)函數(shù)。 另一個(gè)更陰險(xiǎn)的問(wèn)題是,您必須小心地將代碼包裝在try/catch中,否則promise可能會(huì)被拒絕,在這種情況下,錯(cuò)誤會(huì)被默默地吞沒(méi)。(?。?我的建議是確保您的async函數(shù)完全被try/catch包圍,至少在頂層: async function createNewDoc() { let response = await db.post({}); // post a new doc return await db.get(response.id); // find by id } async function printDoc() { try { let doc = await createNewDoc(); console.log(doc); } catch (err) { console.log(err); } }Loops
當(dāng)涉及到迭代時(shí),Async 函數(shù)會(huì)變得非常令人印象深刻。例如,假設(shè)我們希望將一些文檔按順序插入到數(shù)據(jù)庫(kù)中。也就是說(shuō),我們希望這些promises一個(gè)接一個(gè)地執(zhí)行,而不是同時(shí)執(zhí)行。 使用標(biāo)準(zhǔn)的ES6承諾,我們必須滾動(dòng)自己的promise鏈: var promise = Promise.resolve(); var docs = [{}, {}, {}]; docs.forEach(function (doc) { promise = promise.then(function () { return db.post(doc); }); }); promise.then(function () { // now all our docs have been saved }); 這是可行的,但確實(shí)很難看。這也很容易出錯(cuò),因?yàn)槿绻恍⌒淖隽耍? docs.forEach(function (doc) { promise = promise.then(db.post(doc)); }); 然后promises實(shí)際上會(huì)同時(shí)執(zhí)行,這可能會(huì)導(dǎo)致意想不到的結(jié)果。 但是,使用ES7,我們可以使用常規(guī)for循環(huán): let docs = [{}, {}, {}]; for (let i = 0; i < docs.length; i++) { let doc = docs[i]; await db.post(doc); } 這個(gè)(非常簡(jiǎn)潔的)代碼與promise鏈的作用是一樣的!我們可以通過(guò)以下方式使其更短: let docs = [{}, {}, {}]; for (let doc of docs) { await db.post(doc); } 注意,這里不能使用foreach()循環(huán),如果你天真地寫: let docs = [{}, {}, {}]; // WARNING: this won"t work docs.forEach(function (doc) { await db.post(doc); }); 然后Babel.js將失敗,并出現(xiàn)一些不透明的錯(cuò)誤: Error : /../script.js: Unexpected token (38:23) > 38 | await db.post(doc); | ^ 這是因?yàn)樵谡:瘮?shù)中不能使用wait。您必須使用async函數(shù)。 但是,如果您嘗試使用async函數(shù),那么您將得到一個(gè)更微妙的錯(cuò)誤: let docs = [{}, {}, {}]; // WARNING: this won"t work docs.forEach(async function (doc, i) { await db.post(doc); console.log(i); }); console.log("main loop done"); 這將編譯,但問(wèn)題是這將打印出來(lái): main loop done 0 1 2 發(fā)生的是,主函數(shù)提前退出,因?yàn)閍wait實(shí)際上在子函數(shù)中。此外,這將同時(shí)執(zhí)行每一個(gè)promise,這不是我們的預(yù)期。 教訓(xùn)是:在async函數(shù)中有任何函數(shù)時(shí)要小心。wait只會(huì)暫停它的父函數(shù),所以檢查它是否在做你認(rèn)為它在做的事情。Concurrent loops
但是,如果我們確實(shí)希望同時(shí)執(zhí)行多個(gè)promises,那么使用ES7很容易實(shí)現(xiàn)這一點(diǎn)。 回想一下,有了ES6 promises,我們就有了promise.all()。讓我們使用它從promises數(shù)組中返回一個(gè)值數(shù)組: var docs = [{}, {}, {}]; return Promise.all(docs.map(function (doc) { return db.post(doc); })).then(function (results) { console.log(results); }); 在ES7中,我們可以這樣做,這是一種更簡(jiǎn)單的方法: let docs = [{}, {}, {}]; let promises = docs.map((doc) => db.post(doc)); let results = []; for (let promise of promises) { results.push(await promise); } console.log(results); 最重要的部分是1)創(chuàng)建promises數(shù)組,該數(shù)組立即調(diào)用所有的promises;2)我們?cè)谥骱瘮?shù)中等待這些promises。如果我們嘗試使用Array.prototype.map,那么它將無(wú)法工作: let docs = [{}, {}, {}]; let promises = docs.map((doc) => db.post(doc)); // WARNING: this doesn"t work let results = promises.map(async function(promise) { return await promise; }); // This will just be a list of promises :( console.log(results); 不起作用的原因是我們?cè)诘却雍瘮?shù)的內(nèi)部,而不是主函數(shù)。所以在我們真正等待完成之前,主函數(shù)就退出了。 如果您不介意使用promise.all,也可以使用它來(lái)整理代碼: let docs = [{}, {}, {}]; let promises = docs.map((doc) => db.post(doc)); let results = await Promise.all(promises); console.log(results); 如果我們使用數(shù)組壓縮,這看起來(lái)可能會(huì)更好。然而,規(guī)范還不是最終的,所以目前Regenerator不支持它。Caveats
ES7仍然非常前沿。Node.js 或 io.js都不支持Async函數(shù),您必須設(shè)置一些實(shí)驗(yàn)標(biāo)志,甚至讓babel考慮它。正式來(lái)說(shuō),async/await規(guī)范(https://github.com/tc39/ecmascript-asyncawait#status-of-this-proposal)仍處于“建議”階段。 另外,為了在ES5瀏覽器中工作,您還需要在您的開發(fā)代碼中包含Regenerator運(yùn)行時(shí)和ES6 shims。對(duì)我來(lái)說(shuō),這加起來(lái)大約60kb,縮小和gzip。對(duì)于許多開發(fā)人員來(lái)說(shuō),這實(shí)在是太多了。 然而,所有這些新工具都非常有趣,它們描繪了異步庫(kù)在陽(yáng)光明媚的ES7未來(lái)的美好圖景。 所以,如果你想自己玩,我已經(jīng)建立了一個(gè)小的演示庫(kù)(https://github.com/nolanlawson/async-functions-in-pouchdb)。要開始,只需檢查代碼,運(yùn)行npm安裝和npm運(yùn)行build,就可以了。關(guān)于ES7的更多信息,請(qǐng)看JafarHusain的演講。Conclusion
異步函數(shù)是ES7中的一個(gè)新概念。它們將我們丟失的returns和try/catches返回給我們,并獎(jiǎng)勵(lì)我們已經(jīng)從使用新的IDIOM編寫同步代碼中獲得的知識(shí),這些IDIOM看起來(lái)很像舊的IDIOM,但性能更高。 最重要的是,async函數(shù)使得像PouchDB這樣的API更容易使用。因此,希望這將減少用戶錯(cuò)誤和混淆,以及更優(yōu)雅和可讀的代碼。 誰(shuí)知道呢,也許人們最終會(huì)放棄LocalStorage,選擇更現(xiàn)代的客戶端數(shù)據(jù)庫(kù)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/105372.html
摘要:異步編程解決方案筆記最近讀了樸靈老師的深入淺出中異步編程一章,并參考了一些有趣的文章。另外回調(diào)函數(shù)中的也失去了意義,這會(huì)使我們的程序必須依賴于副作用。 JavaScript 異步編程解決方案筆記 最近讀了樸靈老師的《深入淺出NodeJS》中《異步編程》一章,并參考了一些有趣的文章。在此做個(gè)筆記,記錄并鞏固學(xué)到的知識(shí)。 JavaScript異步編程的兩個(gè)核心難點(diǎn) 異步I/O、事件驅(qū)動(dòng)使得...
摘要:一一開始是垃圾,但隨著時(shí)代的發(fā)展業(yè)務(wù)的進(jìn)步,變得越來(lái)越重要,但涉及之初就是用來(lái)打雜的,有缺陷如下簡(jiǎn)單翻譯下沒(méi)有模塊系統(tǒng)沒(méi)有標(biāo)準(zhǔn)庫(kù)沒(méi)有文件沒(méi)有系統(tǒng)沒(méi)有標(biāo)準(zhǔn)接口,用來(lái)做服務(wù)器或者數(shù)據(jù)庫(kù)沒(méi)有依賴包管理系統(tǒng)。 一 Commonjs 一開始js是垃圾,但隨著時(shí)代的發(fā)展、業(yè)務(wù)的進(jìn)步,js變得越來(lái)越重要,但js涉及之初就是用來(lái)打雜的,有缺陷如下: JavaScript has no module ...
摘要:換句話說(shuō),我們很好的對(duì)代碼的功能關(guān)注點(diǎn)進(jìn)行了分離通過(guò)將使用消費(fèi)值得地方函數(shù)中的邏輯和通過(guò)異步流程來(lái)獲取值迭代器的方法進(jìn)行了有效的分離。但是現(xiàn)在我們通過(guò)來(lái)管理代碼的異步流程部分,我們解決了回調(diào)函數(shù)所帶來(lái)的反轉(zhuǎn)控制等問(wèn)題。 本文翻譯自 Going Async With ES6 Generators 由于個(gè)人能力知識(shí)有限,翻譯過(guò)程中難免有紕漏和錯(cuò)誤,還望指正Issue ES6 Gener...
摘要:更好的異步編程上面的方法可以適用于那些比較簡(jiǎn)單的異步工作流程。小結(jié)的組合目前是最強(qiáng)大,也是最優(yōu)雅的異步流程管理編程方式。 訪問(wèn)原文地址 generators主要作用就是提供了一種,單線程的,很像同步方法的編程風(fēng)格,方便你把異步實(shí)現(xiàn)的那些細(xì)節(jié)藏在別處。這讓我們可以用一種很自然的方式書寫我們代碼中的流程和狀態(tài)邏輯,不再需要去遵循那些奇怪的異步編程風(fēng)格。 換句話說(shuō),通過(guò)將我們generato...
摘要:前端日?qǐng)?bào)精選入門指南入口,輸出,加載器和插件中數(shù)據(jù)類型轉(zhuǎn)換讓我印象深刻的面試題大話大前端時(shí)代一與的組件化庖丁解牛一發(fā)布中文第期手把手教你用管理狀態(tài)上個(gè)快速編程技巧眾成翻譯中執(zhí)行順序組件解耦之道眾成翻譯組件模型啟示錄有個(gè)梨作 2017-07-10 前端日?qǐng)?bào) 精選 Webpack入門指南: 入口,輸出,加載器和插件JavaScript中數(shù)據(jù)類型轉(zhuǎn)換讓我印象深刻的javascript面試題大...
閱讀 3163·2021-09-30 09:47
閱讀 2021·2021-09-22 16:04
閱讀 2288·2021-09-22 15:44
閱讀 2544·2021-08-25 09:38
閱讀 547·2019-08-26 13:23
閱讀 1233·2019-08-26 12:20
閱讀 2817·2019-08-26 11:59
閱讀 1085·2019-08-23 18:40