JavaScript非同步程式設計–Generator函式、async、await
JavaScript 非同步程式設計–Generator函式
Generator(生成器)是ES6標準引入的新的資料型別,其最大的特點就是可以交出函式的執行的控制權,即:通過yield關鍵字標明需要暫停的語句,執行時遇到yield語句則返回該語句執行結果,等到呼叫next函式時(也就是說可以通過控制呼叫next函式的時機達到控制generator執行的目的)重新回到暫停的地方往下執行,直至generator執行結束。
基本結構
以下是一個典型的generator函式的示例,以”*”標明為generator。
function* gen(x){
var y = yield x + 2;
console.log(y); // undefine
var yy = yield x + 3;
console.log(yy); // 6
return y; // 沒啥用
}
var g = gen(1);
var r1 = g.next();
console.log(r1); // { value: 3, done: false }
var r2 = g.next();
console.log(r2); // { value: 4, done: false }
var r3 = g.next(6);
console.log(r3); // { value: undefined, done: true }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
上述程式碼中,呼叫gen函式,會返回一個內部指標(即遍歷器)g,這是Generator函式和一般函式不同的地方,呼叫它不會返回結果,而是一個指標物件。呼叫指標g的next方法,會移動內部指標,指向第一個遇到的yield語句,上例就是執行到x+2為止。換言之,next方法的作用是分階段執行Generator函式。每次呼叫next方法,會返回一個物件{value: any, done: boolean},表示當前階段的資訊,其中value屬性是yield語句後面表示式的值;done屬性是一個布林值,表示Generator函式是否執行完畢,即是否還有下一個階段。next方法輸入引數即為yield語句的值,因此生成器gen中y為第二次呼叫next的輸入引數”undefine”,yy為第三次呼叫next的輸入引數6。
總結:
- generator返回遍歷器,可遍歷所有yield
- yield將生成器內部程式碼分割成n段,通過呼叫next方法一段一段執行
- next方法返回的value屬性向外輸出資料,next方法通過實參向生成器內部輸入資料
思考
如果yield標記的語句是個非同步執行的函式func,然後在func回撥中呼叫next,則實現了等待func非同步執行的效果—–“func要做的事做完了,才會往下走”,這樣就避免了多重回撥巢狀(callback hell,回撥地獄, 如下所示)
func1(function (res) { // do something func2(function (res2) { // do something func3(function (res3) { // do something }) }) })
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
Thunk函式
什麼是thunk函式?詳見Thunk 函式的含義和用法
簡單理解:thunk函式利用閉包可以快取狀態的特性,先傳引數,再執行函式,這樣就將函式呼叫過程分成了兩步。以下thunkify函式可將普通非同步函式轉化為thunk函式。
function thunkify(fn) {
assert(`function` == typeof fn, `function required`);
return function () {
// arguments為非同步函式的引數(不包含回撥函式引數)
var args = new Array(arguments.length);
var ctx = this;
for (var i = 0; i < args.length; ++i) {
args[i] = arguments[i];
}
// done為非同步函式的回撥函式(callback)
return function (done) {
var called;
args.push(function () {
if (called) return;
called = true;
done.apply(null, arguments);
});
try {
// 到這裡,非同步函式才真正被呼叫
fn.apply(ctx, args);
} catch (err) {
done(err);
}
}
}
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
Generator執行控制
thunk函式有什麼用呢?其一個典型應用就是用於控制generator的執行,見如下示例是為了實現多個檔案的順序讀取,實現了同步寫法,避免回撥巢狀。
const fs = require(`fs`);
const readFileThunk = thunkify(fs.readFile);
var generator = function* () {
for (var i = 0; i < arguments.length; i++) {
console.log(`file: %s`, arguments[i]);
// yield 返回thunkify最內部的 function (done){} 函式,此處傳入了readFile函式引數,但並沒有執行
var r1 = yield readFileThunk(arguments[i], `utf8`);
console.log(`r1: %s`, r1);
}
}
function rungenerator(generator) {
//檔名稱
var args = [];
for (var i = 1; i < arguments.length; i++) {
args.push(arguments[i]);
}
//生成generator例項
var gen = generator.apply(null, args);
function done(err, data) {
//執行跳到 generator中去
var result = gen.next(data);
if (result.done) { return; }
// 此處才是真正的呼叫readFile函式開始讀取檔案內容,done作為回撥, 檔案讀取完成後,執行gen.next(),
// 告訴generator繼續執行,並通過yield返回下一個thunk函式,開始讀取下一個檔案,從而達到順序執行的效果
result.value(done);
}
next();
}
rungenerator(generator, `123.txt`, `1234.txt`, `he.txt`)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
上述程式碼中,rungenerator是一個執行generator的函式,具有通用性,封裝下就成了co庫—-generator函式自動執行的解決方案。
var fs = require(`fs`);
var co = require(`co`);
var thunkify = require(`thunkify`);
var readFile = thunkify(fs.readFile);
co(function*(){
var files=[`./text1.txt`, `./text2.txt`, `./text3.txt`];
var p1 = yield readFile(files[0]);
console.log(files[0] + ` ->` + p1);
var p2 = yield readFile(files[1]);
console.log(files[1] + ` ->` + p2);
var p3 = yield readFile(files[2]);
console.log(files[2] + ` ->` + p3);
return `done`;
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
看起來舒服多了。。。
async和await
async和await是ES7中的新語法,實際上是generator函式的語法糖
Nodejs最新版已經支援了,瀏覽器不支援的,可以用Babel轉下。
var fs = require(`fs`);
var readFile = function (fileName){
return new Promise(function (resolve, reject){
fs.readFile(fileName, function(error, data){
if (error){
reject(error);
}
else {
resolve(data);
}
});
});
};
var asyncReadFile = async function (){
var f1 = await readFile(`./text1.txt`);
var f2 = await readFile(`./text2.txt`);
console.log(f1.toString());
console.log(f2.toString());
};
asyncReadFile();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
致謝
主要學習了nullcc的部落格《深入解析Javascript非同步程式設計》、無腦的部落格《node的 thunkify模組說明》、阮一峰老師的部落格《Thunk 函式的含義和用法》,由衷地感謝以上作者!!!!!
原文釋出時間:2018-6-19
原文作者:陽光七十米
本文來源csdn部落格如需轉載請緊急聯絡作者
相關文章
- generator函式與async/await函式AI
- JS非同步程式設計 (2) – Promise、Generator、async/awaitJS非同步程式設計PromiseAI
- JavaScript非同步程式設計:Generator與AsyncJavaScript非同步程式設計
- JavaScript非同步程式設計史:回撥函式到Promise到Async/AwaitJavaScript非同步程式設計函式PromiseAI
- 前端-JavaScript非同步程式設計async函式前端JavaScript非同步程式設計函式
- 深入解析JavaScript非同步程式設計:Generator與AsyncJavaScript非同步程式設計
- async、await和generator函式內部原理AI函式
- JavaScript深入淺出非同步程式設計三、async、awaitJavaScript非同步程式設計AI
- 非同步程式設計解決方案全集—promise、generator+co、async+await非同步程式設計PromiseAI
- 非同步程式設計新方式async/await非同步程式設計AI
- Flutter非同步程式設計-async和awaitFlutter非同步程式設計AI
- JS非同步程式設計之async&awaitJS非同步程式設計AI
- 【理解ES7async/await並實現】手把手進行ES6非同步程式設計:Generator + Promise = Async/AwaitAI非同步程式設計Promise
- 非同步操作系列之Generator函式與Async函式非同步函式
- JS非同步程式設計——深入理解async/awaitJS非同步程式設計AI
- C# 非同步程式設計Task(三) async、awaitC#非同步程式設計AI
- [譯] JavaScript - Generator-Yield/Next 和 Async-AwaitJavaScriptAI
- Generator與Promise的完美結合 -- async await函式誕生記PromiseAI函式
- vue中非同步函式async和await的用法Vue非同步函式AI
- .net 溫故知新:【5】非同步程式設計 async await非同步程式設計AI
- JavaScript async和await 非同步操作JavaScriptAI非同步
- JS 非同步發展流程(回撥函式=>Async/await)JS非同步函式AI
- 非同步函式async await在wpf都做了什麼?非同步函式AI
- Promise與async/await與GeneratorPromiseAI
- 004 Rust 非同步程式設計,async await 的詳細用法Rust非同步程式設計AI
- 002 Rust 非同步程式設計,async await 簡單介紹Rust非同步程式設計AI
- ASP.Net中的async+await非同步程式設計ASP.NETAI非同步程式設計
- 非同步程式設計 101:Python async await發展簡史非同步程式設計PythonAI
- 非同步程式設計之Async,Await和ConfigureAwait的關係非同步程式設計AI
- JavaScript Generator 函式JavaScript函式
- ES6-Generator 函式 和 async 函式函式
- Flutter非同步程式設計-sync*和async*生成器函式Flutter非同步程式設計函式
- Promise, Generator, async/await的漸進理解PromiseAI
- JavaScript async await 使用JavaScriptAI
- JavaScript Promises, async/awaitJavaScriptPromiseAI
- C#非同步程式設計由淺入深(二)Async/Await的作用.C#非同步程式設計AI
- Generator函式非同步應用函式非同步
- 非同步程式設計的終極解決方案 async/await:用同步的方式去寫非同步程式碼非同步程式設計AI