JavaScript異步發展史

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);
 }
 })
複製代碼

回調函數的問題

    1. 沒法捕獲錯誤 try catch
    1. 不能return
    1. 回調地獄
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

這種惡魔金字塔有如下問題

    1. 很是難看
    1. 很是難以維護
    1. 效率比較低,由於它們是串行的
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));
複製代碼

如今將Generator與promise綜合在一塊兒:

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);
});
複製代碼

隨着 Node 7的發佈,愈來愈多的人開始研究聽說是異步編程終級解決方案的 async/await

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

相關文章
相關標籤/搜索