異步

異步

所謂"異步",簡單說就是一個任務分紅兩段,先執行第一段,而後轉而執行其餘任務,等作好了準備,再回過頭執行第二段,好比,有一個任務是讀取文件進行處理,異步的執行過程就是下面這樣。

這種不連續的執行,就叫作異步。相應地,連續的執行,就叫作同步。html

高階函數

函數做爲一等公民,能夠做爲參數和返回值

能夠用於批量生成函數

let toString = Object.prototype.toString;
let isString = function (obj) {
  return toString.call(obj) == `[object String]`;
}
let isFunction = function (obj) {
  return toString.call(obj) == `[object Function]`;
}
let isType = function (type) {
  return function (obj) {
    return toString.call(obj) == `[object ${type}]`;
  }
}

能夠用於須要調用屢次才執行的函數

let after = function(times,task){
  return function(){
    if(times--==1){
      return task.apply(this,arguments);
    }
  }
}
let fn = after(3,function(){
  console.log(3);});
fn();

異步編程的語法目標,就是怎樣讓它更像同步編程,有如下幾種

  • 回調函數實現
  • 事件監聽
  • 發佈訂閱
  • Promise/A+ 和生成器函數
  • async/await

回調

所謂回調函數,就是把任務的第二段單獨寫在一個函數裏面,等到從新執行這個任務的時候,就直接調用這個函數
fs.readFile('某個文件', function (err, data) {
  if (err) throw err;
  console.log(data);
});

這是一個錯誤優先的回調函數(error-first callbacks),這也是Node.js自己的特色之一。編程

回調的問題

異常處理

try{
  //xxx
}catch(e){//TODO}
異步代碼時try catch再也不生效

let async = function(callback){
  try{
    setTimeout(function(){
      callback();
    },1000)
  }catch(e){
    console.log('捕獲錯誤',e);
  }
}

async(function(){
  console.log(t);
});

由於這個回調函數被存放了起來,直到下一個事件環的時候纔會取出,try只能捕獲當前循環內的異常,對callback異步無能爲力。瀏覽器

Node在處理異常有一個約定,將異常做爲回調的第一個實參傳回,若是爲空表示沒有出錯。app

async(function(err,callback){
  if(err){
    console.log(err);
  }
});
異步方法也要遵循兩個原則
  • 必須在異步以後調用傳入的回調函數
  • 若是出錯了要向回調函數傳入異常供調用者判斷
let async = function(callback){
try{
  setTimeout(function(){
    if(success)
      callback(null);
    else
      callback('錯誤');
  },1000)
}catch(e){
  console.log('捕獲錯誤',e);
}
}

回調地獄

異步多級依賴的狀況下嵌套很是深,代碼難以閱讀的維護
let fs = require('fs');
fs.readFile('template.txt','utf8',function(err,template){
fs.readFile('data.txt','utf8',function(err,data){
  console.log(template+' '+data);
})
})

異步流程解決方案

事件發佈/訂閱模型

訂閱事件實現了一個事件與多個回調函數的關聯
let fs = require('fs');
let EventEmitter = require('events');
let eve = new EventEmitter();
let html = {};
eve.on('ready',function(key,value){
  html[key] = value;
  if(Object.keys(html).length==2){
    console.log(html);
  }
});
function render(){
  fs.readFile('template.txt','utf8',function(err,template){
    eve.emit('ready','template',template);
  })
  fs.readFile('data.txt','utf8',function(err,data){
    eve.emit('ready','data',data);
  })
}
render();

哨兵變量

let fs = require('fs');

let after = function(times,callback){
  let result = {};
  return function(key,value){
    result[key] = value;
    if(Object.keys(result).length==times){
      callback(result);
    }
  }
}
let done = after(2,function(result){
  console.log(result);
});

function render(){
  fs.readFile('template.txt','utf8',function(err,template){
    done('template',template);
  })
  fs.readFile('data.txt','utf8',function(err,data){
    done('data',data);
  })
}
rende

Promise/Deferred模式

生成器Generators/ yield

當你在執行一個函數的時候,你能夠在某個點暫停函數的執行,而且作一些其餘工做,而後再返回這個函數繼續執行, 甚至是攜帶一些新的值,而後繼續執行。

上面描述的場景正是JavaScript生成器函數所致力於解決的問題。當咱們調用一個生成器函數的時候,它並不會當即執行, 而是須要咱們手動的去執行迭代操做(next方法)。也就是說,你調用生成器函數,它會返回給你一個迭代器。迭代器會遍歷每一箇中斷點。
next方法返回值的value屬性,是Generator函數向外輸出數據next方法還能夠接受參數,這是向 Generator 函數體內輸入數據異步

生成器的使用

function* foo () {
  var index = 0;
  while (index < 2) {
    yield index++; //暫停函數執行,並執行yield後的操做
  }
}
var bar =  foo(); // 返回的實際上是一個迭代器

console.log(bar.next());    // { value: 0, done: false }
console.log(bar.next());    // { value: 1, done: false }
console.log(bar.next());    // { value: undefined, done: true }
Co

co是一個爲Node.js和瀏覽器打造的基於生成器的流程控制工具,藉助於Promise,你可使用更加優雅的方式編寫非阻塞代碼。async

let fs = require('fs');
function readFile(filename) {
  return new Promise(function (resolve, reject) {
    fs.readFile(filename, function (err, data) {
      if (err)
        reject(err);
      else
        resolve(data);
    })
  })
}
function *read() {
  let template = yield readFile('./template.txt');
  let data = yield readFile('./data.txt');
  return template + '+' + data;
}
co(read).then(function (data) {
  console.log(data);
}, function (err) {
  console.log(err);
});
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, reason => reject(reason));
      }
    }();
  });
}

Async/ await

使用async關鍵字,你能夠輕鬆地達成以前使用生成器和co函數所作到的工做

Async的優勢異步編程

  • 內置執行器
  • 更好的語義
  • 更廣的適用性
let fs = require('fs');
function readFile(filename) {
  return new Promise(function (resolve, reject) {
    fs.readFile(filename, 'utf8', function (err, data) {
      if (err)
        reject(err);
      else
        resolve(data);
    })
  })
}

async function read() {
  let template = await readFile('./template.txt');
  let data = await readFile('./data.txt');
  return template + '+' + data;
}
let result = read();
result.then(data=>console.log(data));
async 函數的實現

async 函數的實現,就是將 Generator 函數和自動執行器,包裝在一個函數裏。函數

async function read() {
  let template = await readFile('./template.txt');
  let data = await readFile('./data.txt');
  return template + '+' + data;
}
// 等同於
function read(){
  return co(function*() {
    let template = yield readFile('./template.txt');
    let data = yield readFile('./data.txt');
    return template + '+' + data;
  });
}
async_function- generator
相關文章
相關標籤/搜索