JS Thunk 函數(shù)的含義和用法實例總結(jié)
本文實例講述了JS Thunk 函數(shù)的含義和用法。分享給大家供大家參考,具體如下:
前面我們已經(jīng)學習過了Generator 函數(shù)的優(yōu)勢和使用場景。
這篇文章我們繼續(xù)學習阮老師的第二篇文章,Thunk 函數(shù)的含義和用法
說實話,在這之前是沒聽過這個詞的,但其實如果你對犀牛書里的不完全函數(shù)有認真看過的話
理解起來也不是很費勁。
首先什么是 thunk 函數(shù)?
很多場景下我們都會陷入一個問題,就是函數(shù)參數(shù)的求值時間。
是函數(shù)調(diào)用時即求值還是在函數(shù)內(nèi)使用時才求值?
var x = 1;
function f(m){
return m * 2;
}
f(x + 5)
//我們把在調(diào)用時就計算的方式稱為傳值調(diào)用,等同于:
f(6)
//我們把在函數(shù)內(nèi)部使用時才求值的方式稱為傳名調(diào)用,等同于:
return (x + 5) * 2;
兩種方式各有利弊,傳值調(diào)用比較簡單,但是如果計算后的結(jié)果沒有在程序中使用的話,損失就有點大。
因此有很多場景都傾向于傳名調(diào)用。
但是像 C,java 的編譯方式都是固定的,如何基于現(xiàn)有基礎改變程序的執(zhí)行方式。
比較常見的是將想要傳名調(diào)用的參數(shù)放到一個臨時函數(shù)之中,把臨時函數(shù)當做參數(shù),只在使用的時候執(zhí)行。
這個包裝參數(shù)的臨時函數(shù)就叫 Thunk 函數(shù)。我們試一下用 Thunk 函數(shù)改寫一下上面的例子:
function f(m){
return m * 2;
}
f(x + 5);
// 等同于
var thunk = function () {
return x + 5;
};
function f(thunk){
return thunk() * 2;
}
其實這里我倒覺得可以翻翻犀牛書里的不完全函數(shù),跟 Thunk 函數(shù)一個道理,
通過 return 一個 function 來實現(xiàn)傳名調(diào)用。
老師也順便介紹了用在生產(chǎn)環(huán)境的 Thunkify 模塊
我們看一下源碼,還是有一些好玩的地方的。
function thunkify(fn){
//全局返回一個臨時函數(shù)
return function(){
var args = new Array(arguments.length);
var ctx = this;
for(var i = 0; i < args.length; ++i) {
args[i] = arguments[i];
}
//上面一段將參數(shù)copy到args
return function(done){
var called;
args.push(function(){
if (called) return; //對callback重新包裝,控制callback只執(zhí)行一次
called = true;
done.apply(null, arguments);
});
try {
fn.apply(ctx, args);
} catch (err) {
done(err);
}
}
}
};
執(zhí)行結(jié)果:
function f(a, b, callback){
var sum = a + b;
callback(sum);
callback(sum);
}
var ft = thunkify(f);
ft(1, 2)(console.log);
// 3
這個地方的理解,方法f在執(zhí)行時的參數(shù)并不是 1,2,console.log
console.log 參數(shù)在 thunkify 內(nèi)部被重新包裝,成了:
function(){
if (called) return; //對callback重新包裝,控制callback只執(zhí)行一次
called = true;
console.log.apply(null, arguments);
}
了解了 Thunk 函數(shù)之后,我們要停下來想一想,還是那句話,它的出現(xiàn)要解決什么問題?
是不是一定要使用 Thunk 函數(shù)?Thunk 用在什么場景下?
從前面的內(nèi)容來看,其實并沒有什么用,可能概念比較新穎,但是使用起來好像并沒有太多提高。
但是沒用的話我們也不會寫這么一篇文章。
自從有了 Generator 函數(shù),Thunk 函數(shù)現(xiàn)在可以用于 Generator 函數(shù)的自動流程管理。
看一下例子:
var fs = require('fs');
var thunkify = require('thunkify');
var readFile = thunkify(fs.readFile);
var gen = function* (){
var r1 = yield readFile('/etc/fstab');
console.log(r1.toString());
var r2 = yield readFile('/etc/shells');
console.log(r2.toString());
};
這個例子中,我們使用 yield 將執(zhí)行權(quán)交給下一個協(xié)程,那么就需要有一種方法把執(zhí)行權(quán)在交還給當前函數(shù)
這種方法就是 Thunk 函數(shù),因為它可以重新包裝回調(diào)函數(shù),我們可以自己寫包裝函數(shù),將執(zhí)行權(quán)交還給 Generator 函數(shù)。
為了對比,我們先看一下如果手動執(zhí)行上面的代碼會是什么樣的:
var g = gen(); //開始執(zhí)行協(xié)程
var r1 = g.next(); //讀取第一個文件
r1.value(function(err, data){ //讀取完成執(zhí)行回調(diào)
if (err) throw err;
var r2 = g.next(data); //讀取第二個文件
r2.value(function(err, data){ //讀取完成執(zhí)行回調(diào)
if (err) throw err;
g.next(data); //結(jié)束協(xié)程
});
});
不難發(fā)現(xiàn),上面的代碼其實就是將同一個回調(diào)函數(shù)傳入 value 屬性(next 執(zhí)行返回 value 和 done )
我在看的時候就在想,這個value是屬性啊,為什么可以執(zhí)行?還傳遞參數(shù)?
慢慢理一理:
value屬性是yield的返回值,gen中的yield返回的是一個 Thunk 函數(shù),不是固定值。
所以可以執(zhí)行value,看前面例子里的這句:ft(1, 2)(console.log);
value就等同于ft(1, 2)的返回值
傳遞的function回調(diào)等同于(console.log);
這么是不是就理解了?
Thunk 函數(shù)真正的威力,在于可以自動執(zhí)行 Generator 函數(shù)。下面就是一個基于 Thunk 函數(shù)的 Generator 執(zhí)行器:
function run(fn) {
var gen = fn(); //自動開始協(xié)程
//對next進行包裝,形成 Thunk 函數(shù),遍歷調(diào)用
function next(err, data) {
var result = gen.next(data);
if (result.done) return;
result.value(next);
}
next();
/* 參考bootstrap的寫法改寫一下
!function next(err, data) {
var result = gen.next(data);
if (result.done) return;
result.value(next);
}();
*/
}
run(gen);
上面的寫法很簡單吧,這么改寫之后就不需要你手動的去控制執(zhí)行next的時機了
只需要執(zhí)行run函數(shù)就行。但是要保證每一個yield后面都是一個 Thunk 函數(shù),否則的話就不能自動執(zhí)行了。
這一章的學習總結(jié)就結(jié)束了,我們學會了如何使用 Thunk 函數(shù)實現(xiàn)自動執(zhí)行,但 Thunk 函數(shù)并不是 Generator 函數(shù)自動執(zhí)行的唯一方案。
因為自動執(zhí)行的關(guān)鍵是,必須有一種機制,自動控制 Generator 函數(shù)的流程,接收和交還程序的執(zhí)行權(quán)?;卣{(diào)函數(shù)可以做到這一點,Promise 對象也可以做到這一點。
下一篇文章我們?nèi)タ匆幌禄趐romise實現(xiàn)的自動執(zhí)行器:co
感興趣的朋友可以使用在線HTML/CSS/JavaScript代碼運行工具:http://tools.jb51.net/code/HtmlJsRun測試上述代碼運行效果。
更多關(guān)于JavaScript相關(guān)內(nèi)容可查看本站專題:《JavaScript常用函數(shù)技巧匯總》、《javascript面向?qū)ο笕腴T教程》、《JavaScript錯誤與調(diào)試技巧總結(jié)》、《JavaScript數(shù)據(jù)結(jié)構(gòu)與算法技巧總結(jié)》及《JavaScript數(shù)學運算用法總結(jié)》
希望本文所述對大家JavaScript程序設計有所幫助。
相關(guān)文章
JavaScript蒙板(model)功能的簡單實現(xiàn)代碼
本文給大家介紹JavaScript蒙板(model)功能的簡單實現(xiàn)代碼,創(chuàng)建一個蒙板, 設置蒙板的堆疊順序保證能將其它元素蓋住,感興趣的朋友可以參考下實現(xiàn)代碼2016-08-08
bootstrap fileinput插件實現(xiàn)預覽上傳照片功能
這篇文章主要介紹了bootstrap fileinput插件實現(xiàn)預覽上傳照片功能,代碼簡單易懂,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2018-01-01
基于Leaflet的Webgis經(jīng)緯網(wǎng)格功能實現(xiàn)
本文將介紹一款Leaflet的經(jīng)緯網(wǎng)插件,基于這款經(jīng)緯網(wǎng)插件,詳細介紹如何實現(xiàn)經(jīng)緯網(wǎng)功能,本文通過實例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧2023-12-12
js和jquery實現(xiàn)tab狀態(tài)欄切換效果
這篇文章主要為大家詳細介紹了js和jquery實現(xiàn)tab狀態(tài)欄切換效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-08-08
JavaScript中獲得CheckBox狀態(tài)的方法小結(jié)
在 JavaScript 中,獲取復選框(CheckBox)的狀態(tài)(選中或未選中)可以通過以下幾種方式實現(xiàn),以下是具體方法及示例,并通過代碼示例介紹的非常詳細,需要的朋友可以參考下2025-03-03
JavaScript實現(xiàn)鼠標經(jīng)過表格某行時此行變色
這篇文章主要為大家詳細介紹了JavaScript實現(xiàn)鼠標經(jīng)過表格某行時此行變色,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-11-11
js中關(guān)于new Object時傳參的一些細節(jié)分析
這里討論給Object傳參時,其內(nèi)部的處理。參考:ECMA262 V5 15.2.2.12011-03-03

