JavaScript 的全部網絡操做,瀏覽器事件,都必須是異步執行。 正如咱們所知道的那樣,在JavaScript中,異步編程方式只能經過JavaScript語言中的一等公民函數才能完成:這種方式意味着咱們能夠將一個函數做爲另外一個函數的參數,在這個函數的內部能夠調用被傳遞進來的函數(即回調函數)。html
let fs = require('fs');
fs.readFile('./1.txt','utf8',function(err,data){
if(err){//若是err有值,就表示程序出錯了
console.log(err);
}else{//若是error爲空,就表示 成功了,沒有錯誤
console.log(data);
}
})
複製代碼
function read(filename){
fs.readFile(filename,'utf8',function(err,data){
throw Error('出錯了')
if(err){//若是err有值,就表示程序出錯了
console.log(err);
}else{//若是error爲空,就表示 成功了,沒有錯誤
console.log(data);
}
})
}
try{
read('./1.txt');
}catch(e){
console.log('err',e);
};
複製代碼
當你訪問服務器的時候,好比要請求一個HTML頁面,好比是用戶列表。服務器一方面會去讀取讀模板文件,多是ejs pug jade handlebar ,另一方面還要讀取數據(可能會放在文件裏,也能夠會放在數據裏),它們都很慢,因此都是異步的。node
fs.readFile('./template.txt', 'utf8', function (err, template) {
fs.readFile('./data.txt', 'utf8', function (err, data) {
console.log(template + ' ' + data);
})
})
複製代碼
1.經過事件發佈訂閱來實現 這是node核心模塊中的一個類,經過它能夠建立事件發射器的實例,裏面有兩個核心方法,一個叫on emit,on表示註冊監聽,emit表示發射事件ajax
let EventEmitter = require('events');
let eve = new EventEmitter();
//這個html對象是存放最終數據
let html = {};//template data
//監聽數據獲取成功事件,當事件發生以後調用回調函數
eve.on('ready',function(key,value){
html[key] = value;
if(Object.keys(html).length == 2){
console.log(html);
}
});
fs.readFile('./template.txt', 'utf8', function (err, template) {
//1事件名 2參數日後是傳遞給回調函數的參數
eve.emit('ready','template',template);
})
fs.readFile('./data.txt', 'utf8', function (err, data) {
eve.emit('ready','data',data);
})*/
//經過一個哨兵函數來處理
function done(key,value){
html[key] = value;
if(Object.keys(html).length == 2){
console.log(html);
}
}
複製代碼
ES6的不少特性都跟Generator扯上關係,並且實際用處比較廣, 包含了任何須要異步的模塊, 好比ajax, filesystem, 或者數組對象遍歷等均可以用到; Generator函數和普通的函數區別有兩個, 1:function和函數名之間有一個*號, 2:函數體內部使用了yield表達式;好比這樣:編程
/**
* 生成器是一個函數,能夠用來生成迭代器
* 生成器函數和普通函數不同,普通函數是一旦調用必定會執行完
* 可是生成器函數中間能夠暫停,能夠執行一會歇一會
*/
//生成器函數有一個特色,須要加個*
//生成器有若干個階段,如何劃分這些階段呢?
function *go(a){
console.log(1);
//此處的b用來供外界輸入進來的
//這一行實現輸入和輸出,本次的輸出放在yield後面,下次的輸入放在yield前面
let b = yield a;
console.log(2);
let c = yield b;
console.log(3);
return c;
}
//生成器函數和普通的函數不同,調用它的話函數並不會馬上執行
//它會返回今生成器的迭代器,迭代器是一個對象,每調用一次next就能夠返回一個值對象
let it = go("a值");
//next第一次執行不須要參數,傳參數沒有意義
let r1 = it.next();
//第一次調用next會返回一個對象,此對象有兩個屬性,一個是value就是yield後面那個值,一個是done表示是否迭代完成
console.log(r1);//{ value: 'a', done: false }
let r2 = it.next('B值');
console.log(r2);//{ value: 'B值', done: false }
let r3 = it.next('C值');
console.log(r3);//{ value: 'C值', done: true }
複製代碼
先回憶以前promise對異步的實現,以 bluebird爲例:數組
let Promise = require('bluebird');
let fs = require('fs');
function promisifyAll(obj) {
for (let key in obj) {
if (obj.hasOwnProperty(key) && typeof obj[key] == 'function') {
obj[key+'Async'] = Promise.promisify(obj[key]);
}
}
}
//它會遍歷對象上面的全部方法,而後對每一個方法添加一個新的方法 Async
promisifyAll(fs);
fs.readFileAsync('./1.txt', 'utf8').then(data => console.log(data));
複製代碼
let fs = require('fs');
function readFile(filename) {
return new Promise(function (resolve, reject) {
fs.readFile(filename, 'utf8', function (err, data) {
err ? reject(err) : resolve(data);
});
})
}
function *read() {
console.log('開始');
let a = yield readFile('1.txt');
console.log(a);
let b = yield readFile('2.txt');
console.log(b);
let c = yield readFile('3.txt');
console.log(c);
return c;
}
function co(gen) {
let it = gen();//咱們要讓咱們的生成器持續執行
return new Promise(function (resolve, reject) {
!function next(lastVal) {
let {value,done} = it.next(lastVal);
if(done){
resolve(value);
}else{
value.then(next,reject);
}
}()
});
}
co(read).then(function (data) {
console.log(data);
});
複製代碼
let Promise = require('bluebird');
let readFile = Promise.promisify(require('fs').readFile);
async function read() {
//await後面必須跟一個promise,
let a = await readFile('./1.txt','utf8');
console.log(a);
let b = await readFile('./2.txt','utf8');
console.log(b);
let c = await readFile('./3.txt','utf8');
console.log(c);
return 'ok';
}
read().then(data => {
console.log(data);
});
複製代碼
async await是語法糖,內部仍是用generator+promise實現promise