異步的概念
一般同步的程序是請求文件-等待文件-處理文件,而異步則是在等待文件的階段可以處理其他任務,這便是異步
回調函數
Js通過回調函數實現異步編程,回調函數就是將處理文件這一階段單獨卸載一個函數里,等再次執行這個任務時便直接調用該函數
fs.readFile('/etc/passwd', function(err, data){
/*
這里便是回調函數
只有當讀取到/etc/passwd時才會執行
*/
if(err) throws err
console.log(data)
})
Promise
不過如果依次讀取多個文件時會出現多重嵌套,這便是callback hell
/*callback hell*/
fs.readFile(fileA, function (err, data) {
fs.readFile(fileB, function (err, data) {
// ...
});
});
Promise就是為了解決這個問題才出現的,它提供一個新的寫法
var readFile = require('fs-readfile-promise');
/*返回一個Promise版本的readFile函數*/
readFile(fileA)
.then(function(data){ /*Promise提供then方法加載回調函數*/
console.log(data.toString());
})
.then(function(){
return readFile(fileB);
})
.then(function(data){
console.log(data.toString());
})
.catch(function(err) {/*catch方法捕捉拋出錯誤*/
console.log(err);
});
協程(coroutine)
通過管理函數執行權來獲得異步編程能力
通過yield
命令,可以讓同步中的請求文件跟處理文件中斷開來并且通過這開辟的新的時間來處理其他任務,這便是協程
function asnycJob() {
// ...其他代碼
var f = yield readFile(fileA);/*執行到這里后執行權交給其他協程,暫停該進程*/
// ...其他代碼
}
Generator函數
協程在ES6中的實現,最大特點是可以交出函數的執行權
function* gen(x) { /*在函數名之前加星號以示區別*/
var y = yield x+2
return y
}
var g = gen(1);
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }
調用Generator函數執行該函數是不會返回結果而是返回指針對象
通過調用指針的.next()
方法會指向第一個遇到的yield語句,也就是分階段執行Generator函數
每次調用next方法都會返回當前階段信息
- value: yield語句后面表達式的值,表示當前階段的值
- done: 表示Generator函數是否執行完畢
簡單來說Generator函數就是一個封裝的異步任務的容器,在異步操作需要暫停的地方都用yield語句注明,再通過.next()
方法來分階段輸出
Generator函數的數據交換和錯誤處理
.next()
方法返回value屬性便是對外輸出的數據,同樣next方法也可以接受參數,賦值給value
function* gen(x){
var y = yield x + 2;
return y;
}
var g = gen(1);
g.next() // { value: 3, done: false }
g.next(2) // { value: 2, done: true }
使用指針對象g的throw方法拋出的錯誤可以被函數體被的try..catch
代碼捕獲
g.throw('warming!!!')
Generator用法
var fetch = require('node-fetch');
function* gen(){
var url = 'https://api.github.com/users/github';
var result = yield fetch(url); /*當需要的時候在fetch該api*/
console.log(result.bio);}/*.bio: 數據*/
使用該方法
var g = gen();
var result = g.next();
result.value.then(function(data){
return data.json();
}).then(function(data){
g.next(data);
});