所謂"異步",簡單說就是一個任務分紅兩段,先執行第一段,而後轉而執行其餘任務,等作好了準備,再回過頭執行第二段,好比,有一個任務是讀取文件進行處理,異步的執行過程就是下面這樣。這種不連續的執行,就叫作異步。相應地,連續的執行,就叫作同步。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();
所謂回調函數,就是把任務的第二段單獨寫在一個函數裏面,等到從新執行這個任務的時候,就直接調用這個函數
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
當你在執行一個函數的時候,你能夠在某個點暫停函數的執行,而且作一些其餘工做,而後再返回這個函數繼續執行, 甚至是攜帶一些新的值,而後繼續執行。
上面描述的場景正是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 }
Coco是一個爲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關鍵字,你能夠輕鬆地達成以前使用生成器和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