摘要:傳統(tǒng)的異步方法回調(diào)函數(shù)事件監(jiān)聽發(fā)布訂閱之前寫過一篇關(guān)于的文章,里邊寫過關(guān)于異步的一些概念。內(nèi)部函數(shù)就是的回調(diào)函數(shù),函數(shù)首先把函數(shù)的指針指向函數(shù)的下一步方法,如果沒有,就把函數(shù)傳給函數(shù)屬性,否則直接退出。
Generator函數(shù)與異步編程
傳統(tǒng)的異步方法因為js是單線程語言,所以需要異步編程的存在,要不效率太低會卡死。
回調(diào)函數(shù)
事件監(jiān)聽
發(fā)布/訂閱
Promise
之前寫過一篇關(guān)于Promise的文章,里邊寫過關(guān)于異步的一些概念。這篇文章將會說一下Generator函數(shù)的異步應(yīng)用。
Generator函數(shù) 協(xié)程多個線程互相合作完成任務(wù),在傳統(tǒng)的編程語言中(比如java),當A線程在執(zhí)行,執(zhí)行一段時間之后暫停,由B線程繼續(xù)執(zhí)行,B線程執(zhí)行結(jié)束之后A線程再執(zhí)行,這個時候,A線程就被稱為協(xié)程,而這個協(xié)程A就是異步任務(wù)。
function* foo(){ ... //其他代碼 var f = readFile(); ... //其他代碼 }
上邊這個函數(shù),foo函數(shù)就是一個協(xié)程,通過yield命令實現(xiàn)協(xié)程的暫停,等到讀取文件的函數(shù)執(zhí)行完畢之后,再繼續(xù)執(zhí)行foo其他的操作。
協(xié)程的Generator函數(shù)實現(xiàn)Generator函數(shù)是協(xié)程在ES6的實現(xiàn),最大的特點是交出函數(shù)的執(zhí)行權(quán)(暫停函數(shù)執(zhí)行)
整個Generator函數(shù)就是一個封裝好了的異步任務(wù),而yield是函數(shù)暫停執(zhí)行的標志。
function* foo(){ let x = 1; let y = yield x + 2; return y } var f = foo() f.next(); // {value:3,done:false} f.next(); // {value:undefined,done:true} next方法的作用是分批端執(zhí)行Generator函數(shù)。異步函數(shù)的封裝
let fetch = require("node-fetch") function* asynsFun(){ let url = "...."; var f = yield fetch(url); console.log(f) }
當執(zhí)行完fetch之后把取回的數(shù)據(jù)賦值給f,然后再把f打印出來,這個看起來很像同步的寫法,但是實現(xiàn)起來卻是異步的。
是不是很簡單,如果用回掉函數(shù)或者Promise的寫法會很復(fù)雜的。
let a = asyncFun() a.next() a.value.then(function(data){ return data.json(); }).then(function(data){ g.next(data); });
這樣的寫法表示起來很簡潔,但是流程管理比較復(fù)雜。
Thunk函數(shù)thunk函數(shù)是自動執(zhí)行Generator函數(shù)的一種方法。
Thunk函數(shù)的核心理解就是傳名調(diào)用。
function f(x){ return x * 2 } f(x + 6) //等同于 var thunk = function(x) { return x + 5 } function f() { return thunk() * 2 }
理論上,x+6被thunk函數(shù)替代了,所有用到原來參數(shù)的地方,直接用thunk求值就行。
這就是Thunk函數(shù)的策略,是傳名求值的一種實現(xiàn)策略,用來替換某個表達式。
js中的Thunk函數(shù)在js中,函數(shù)的參數(shù)并不是傳名的而是傳值的,所以,thunk函數(shù)不是用來替換表達式的,而是用來替換多參數(shù)函數(shù)的。將其中一個參數(shù)替換成只接收一個回掉函數(shù)的單參數(shù)參數(shù)。
聽起來很拗口,看代碼。
// 正常的寫法 fs.readFile(filename,callback); // thunk函數(shù)的單參數(shù)版本 var thunk = function(filename) { return function(callback) { return fs.readFile(filename,callback); } } var readThunk = thunk(filename) readThunk(callback)
理論上,只要函數(shù)的一個參數(shù)是回調(diào)函數(shù),就可以改寫成Thunk函數(shù)。
Thunkify模塊一個轉(zhuǎn)換器,把函數(shù)轉(zhuǎn)成Thunk函數(shù)
安裝 npm install thunkify 使用方法: var thunkify = require("thunkify"); var fs = require("fs"); var read = thunkify(rs.readFile); read("package-json")(function(err,str) // ... )
thunkify接受一個回調(diào)方法、
Generator函數(shù)的流程管理之前說過,Generator函數(shù)的流程管理比較復(fù)雜,那么Thunk函數(shù)有什么用呢,正確答案是,他可以幫助Generator函數(shù)實現(xiàn)自動的流程管理。
function* gen(){ // ... } var g = gen(); var res = g.next(); while(!res.done){ console.log(res.value) res.next(); }
理論上,上面的代碼可以實現(xiàn)自動執(zhí)行,但是,不能適合異步。用Thunk可以解決這個問題。
var thunkify = require("thunkify"); var fs = require("fs"); var readFileThunk = thunkify(fs.readFile) var gen = function* (){ var r1 = readFileThunk("filename1") console.log(r1); var r2 = readFileThunk("filename2") console.log(r2); }Thunk函數(shù)的自動流程管理
Thunk函數(shù)的真正意義在于可以自動執(zhí)行Generator函數(shù),看下邊的例子。
function* g(){ // ... } function run(fn){ //Thunk函數(shù)接收一個Generator函數(shù) var gen = fn(); function next(err,data){ var result = gen.next(data); if(result.done) return; return result.value(next) } next(); } run(g)
解析一下這個代碼:
run方法其實就是一個Generator函數(shù)自動執(zhí)行器。內(nèi)部函數(shù)next就是Thunk的回調(diào)函數(shù),next函數(shù)首先把Generator函數(shù)的指針指向Generator函數(shù)的下一步方法(gen.next()),如果沒有,就把next函數(shù)傳給Thunk函數(shù)(result.value屬性),否則直接退出。
有了這個執(zhí)行器,執(zhí)行Generator函數(shù)就方便多了,不管內(nèi)部多少操作,直接把Generator函數(shù)傳給run函數(shù)即可,當然前提是每一個異步操作都是一個Thunk函數(shù),也就是yield后面的必須是Thunk函數(shù)。
function* g(){ var f1 = yield fs.readFileThunk("filename1") var f2 = yield fs.readFileThunk("filename2") ... } run(g)
Thunk 函數(shù)并不是 Generator 函數(shù)自動執(zhí)行的唯一方案。因為自動執(zhí)行的關(guān)鍵是,必須有一種機制,自動控制 Generator 函數(shù)的流程,接收和交還程序的執(zhí)行權(quán)。回調(diào)函數(shù)可以做到這一點,Promise 對象也可以做到這一點。
這篇文章寫得比較難懂,其實主要是為了下一篇文章做鋪墊。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/89035.html
摘要:更好的語義和分別表示異步和等待,比起和更容易理解。前邊聲明關(guān)鍵字,表示內(nèi)部有內(nèi)部操作,調(diào)用函數(shù)會返回一個對象。等價于其中函數(shù)就是自動執(zhí)行器。 async函數(shù) 定義 async函數(shù)其實就是之前說過的Generator的語法糖,用于實現(xiàn)異步操作。它是ES2017的新標準。 讀取兩個文件: const fs = require(fs) const readFile = function(f...
摘要:第二次同理,遇到了第二個函數(shù)會停下來,輸出的遍歷器對象值為,的值依然是。比如返回的遍歷器對象,都會有一個方法,這個方法掛在原型上。這三個函數(shù)共同的作用是讓函數(shù)恢復(fù)執(zhí)行。 Generator的語法 generator的英文意思是生成器 簡介 關(guān)于Generator函數(shù),我們可以理解成是一個狀態(tài)機,里面封裝了多種不同的狀態(tài)。 function* gener(){ yield hel...
摘要:的翻譯文檔由的維護很多人說,阮老師已經(jīng)有一本關(guān)于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細介紹promise的gitbook,看完再不會promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:返回值是一個對象,它的第一個屬性是后面表達式的值或者的值第二個屬性表示函數(shù)是否執(zhí)行完成。真正的業(yè)務(wù)邏輯確實是用同步的方式寫的。 開始前 我們從來沒有停止過對javascript語言異步調(diào)用方式的改造,我們一直都想用像java那樣同步的方式去寫異步,盡管Promise可以讓我們將異步回調(diào)添加到then方法中,但是這種調(diào)用方式仍然不那么優(yōu)雅,es6 中新增加了generator,我們可以通...
摘要:在這里看尤雨溪大神的這篇小短文,非常精簡扼要地介紹了當前常用的。根據(jù)尤雨溪大神的說法,的也只是的語法糖而已。對象有三種狀態(tài),,。對象通過和方法來規(guī)定異步結(jié)束之后的操作正確處理函數(shù)錯誤處理函數(shù)。方便進行后續(xù)的成功處理或者錯誤處理。 最近在寫一個自己的網(wǎng)站的時候(可以觀摩一下~Colors),在無意識中用callback寫了一段嵌套了5重回調(diào)函數(shù)的可怕的代碼。回過神來的時候被自己嚇了一跳,...
閱讀 2082·2021-11-02 14:48
閱讀 2760·2019-08-30 14:19
閱讀 2929·2019-08-30 13:19
閱讀 1297·2019-08-29 16:17
閱讀 3230·2019-08-26 14:05
閱讀 2987·2019-08-26 13:58
閱讀 3075·2019-08-23 18:10
閱讀 1105·2019-08-23 18:04