异步
異步
所謂"異步",簡單說就是一個任務分成兩段,先執行第一段,然后轉而執行其他任務,等做好了準備,再回過頭執行第二段,比如,有一個任務是讀取文件進行處理,異步的執行過程就是下面這樣。這種不連續的執行,就叫做異步。相應地,連續的執行,就叫做同步。
高階函數
函數作為一等公民,可以作為參數和返回值可以用于批量生成函數
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在處理異常有一個約定,將異常作為回調的第一個實參傳回,如果為空表示沒有出錯。
async(function(err,callback){if(err){console.log(err);} }); 異步方法也要遵循兩個原則- 必須在異步之后調用傳入的回調函數
- 如果出錯了要向回調函數傳入異常供調用者判斷
回調地獄
異步多級依賴的情況下嵌套非常深,代碼難以閱讀的維護 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);}) } rendePromise/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 } Coco是一個為Node.js和瀏覽器打造的基于生成器的流程控制工具,借助于Promise,你可以使用更加優雅的方式編寫非阻塞代碼。
let fs = require('fs'); function readFile(filename) {return new Promise(function (resolve, reject) {fs.readFile(filename, function (err, data) {if (err)reject(err);elseresolve(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的優點
- 內置執行器
- 更好的語義
- 更廣的適用性
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總結
- 上一篇: Python-数学篇之计算方法的目录:
- 下一篇: PyQt5——布局管理