摘要:環境中產生異步操作的函數分為兩大類計時函數和函數。如果要在應用中定義復雜的異步操作,就要使用者兩類異步函數作為基本的構造快。在本例子中同步事件循環不包含內部的外部的異步事件循環內部的。其弊端是操作強耦合維護代價高。
JavaScript環境中產生異步操作的函數分為兩大類:計時函數和I/O函數。如果要在應用中定義復雜的異步操作,就要使用者兩類異步函數作為基本的構造快。本文沒有對某個知識點細致展開,僅供思路參考。1. 計時函數
先看一個經典的例子:
for(var i = 0;i < 5; i++){ setTimeout(function(){ console.log(i); },5000); } console.log(i);
結果輸出什么?立馬輸出一個5,5秒鐘過后連續輸出5個5(嗚嗚嗚嗚嗚~),搞懂為什么這樣輸出5,需要知道3件事:
這里只有一個i變量,作用域由var定義,不管i怎么變,都指向同一個內存區域;
循環結束后 i=== 5;
JavaScript事件處理器在線程空閑之前不會執行。
來,再來和我背一遍口訣:先同步后異步最后回調。在本例子中:
同步事件:for循環(不包含內部的setTimeout);外部的console.log(i);
異步事件:for循環內部的setTimeout。
先執行for循環,遇到setTimeout壓入延遲事件隊列,一邊循環一邊壓入隊列;for循環結束執行外部的console.log(i),此時i=5,故立即輸出5,此時同步事件執行完畢,接下來開始執行異步事件setTimeout,5個setTimeout事件等待5秒同時在等待,所以5秒結束連續輸出5個5。
再看一個例子:
var start = new Date; setTimeout(function(){ var end = new Date; console.log("time using:" + (end - start) + "ms"); },5000); while(new Date - start < 1000){};
猜猜結果是什么?
調換一下時間:
var start = new Date; setTimeout(function(){ var end = new Date; console.log("time using:" + (end - start) + "ms"); },2000); while(new Date - start < 5000){};
這里唯一想說的是:像setTimeout或是setInterval并非是精確計時的, setTimeout與setInterval在不同瀏覽器下的差異。
2. I/O函數這里的I/O是一個廣義的概念,包括讀寫文件,GET或POST請求,異步讀取值值函數等等。一個常見的讀文件的操作:fs.js
var fs = require("fs"); fs.readFile("data.txt",function(err,data){ if(err){ return console.log(err); } console.log(data.toString()) });
data.txt的內容:
hello world hello async
node fs.js:
沒毛病!設想一個場景,在另外一個函數,假設名字叫panfen(),里面有一堆代碼,其中需要用到從data.txt文件讀取的數據,常見的做法是把fs.readFile操作寫在panfen()里面,處理data的操作都寫在fs.readFile的callback里面,如果callback里面還要寫數據呢?繼續執行fs.writeFile在它的callback里面執行其他操作...
var fs = require("fs"); fs.readFile("data.txt",function(err,data){ if(err){ return console.log("failed to read data!"); }else{ fs.writeFile("./data_copy.txt", data, function(){ if(err){ ... }else{ ... } }); } });
這就是回調金字塔。其弊端是操作強耦合、維護代價高。如何做到理想的異步呢?
靈機一動這樣寫:
var fs = require("fs"); var data = fs.readFile("data.txt",function(err,data){ return data ? data : err; }); fs.writeFile("./data_copy.txt", data, function(){ ... });
然而,根據先同步后異步最后回調的法則,fs.writeFile里面使用到的data,肯定是undefined
3.Promisevar fs = require("fs") function myReadFile(filepath) { return new Promise(function (resolve, reject) { fs.readFile(filepath, function (err, data) { data ? resolve(data) : reject(err); }); }); } function myWriteFile(filepath,data) { return new Promise(function (resolve, reject) { fs.writeFile(filepath,data,function (err) { err ? reject(err) : resolve(); }); }); } function test() { myReadFile("data.txt").then(function(data){ myWriteFile("./data1.txt",data) }); } test();
Promise的特點在于可以用then方法向下傳遞值。
4. Generator 4.1 function*關于生成器函數,這里不作具體介紹,簡單看一例子:
function* GenFunc(){ yield [1,2]; yield* [3,4]; yield "56"; yield* "78" } var gen = GenFunc(); console.log(gen.next()); console.log(gen.next()); console.log(gen.next()); console.log(gen.next()); console.log(gen.next()); console.log(gen.next());
yield和yield*的區別:
yield只返回右值;
yield*將函數委托給另一個生成器,或可迭代的對象(字符串、數組、arguments、Map、Set)
注: yield關鍵字只能出現在生成器函數里面!!!否則會報錯:Unexpected strict mode reserved word
4.2 coco是基于Generator的一個庫:
var fs = require("fs"); var co = require("co"); function myReadFile(filepath){ return function(cb){ fs.readFile(filepath,cb); }; } function myWriteFile(filepath,data){ return function(cb){ fs.writeFile(filepath,data,cb); }; } co(function* GenFunc(){ var data = yield myReadFile("data.txt"); yield myWriteFile("data1.txt",data); }).catch(function(err){ console.log(err); });
看起來是不是神清氣爽?
4.3 KoaKoa是基于Generator和co的web框架。
var koa = require("koa"); var app = koa(); app.use(function* (next){ console.log(1); yield next; console.log(3); }); app.use(function* (){ console.log(2); this.body = "hello Koa!"; }); app.listen(8080);
啟動程序,流浪器輸入localhost:8080,看到頁面有hello Koa!,控制臺輸入1 2 3
5.async/awit隨著 Node 7 的發布,越來越多的人開始研究async/await,據說這是異步編程終級解決方案的 。個人覺得沒有最好,只有更好。用這種方式改寫:
"use strict" var fs = require("fs") function myReadFile(filepath) { return new Promise(function (resolve, reject) { fs.readFile(filepath, function (err, data) { data ? resolve(data) : reject(err); }); }); } function myWriteFile(filepath,data) { return new Promise(function (resolve, reject) { fs.writeFile(filepath,data,function (err) { err ? reject(err) : resolve(); }); }); } async function test() { const data = await myReadFile("data.txt"); await myWriteFile("./data1.txt",data); } test();
當然,這個例子還不足以體現async/awit的優勢。
6. 總結JavaScript的異步操作可能是區別其他語言比較大的一點,也是一個難點,不過也是很有趣的。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/82601.html
摘要:前言這個系列的上一篇文章編寫自己的代碼庫常用實例的實現與封裝總結了個常見的操作。前序修改以及寫法優化此處修改之前提交函數已經發現的,基于這個系列上篇文章的提供的函數。 1.前言 這個系列的上一篇文章(編寫自己的代碼庫(javascript常用實例的實現與封裝))總結了34個常見的操作。但是在開發中,常見的實例又何止這么多個,經過這些日子的探索,以及他人的意見,現在得追加一些操作實例了。...
摘要:前言在異步編程之一中實現了一個異步函數調用鏈,它是一個順序調用鏈,很類似責任鏈模式,但現實往往不是平鋪直敘的,更多的其實是峰回路轉,本文將繼續討論更多的用法。 前言 在《ES6 異步編程之一:Generator》中實現了一個異步函數調用鏈,它是一個順序調用鏈,很類似責任鏈模式,但現實往往不是平鋪直敘的,更多的其實是峰回路轉,本文將繼續討論更多Generator的用法。 作為函數的Gen...
摘要:定時器例子之前通過調用定時器,需要傳一個回調,然后所有的代碼邏輯都包在里面。這里定時器會阻塞在這一行,直到一秒后才會執行下面的一行。 之前介紹過quasar,如果你希望在vert.x項目里使用coroutine的話,建議使用vertx-sync。本篇將介紹vertx-sync。 showImg(/img/bVzIsu); 本來打算另起一篇,寫其他方面的東西,但是最近比較忙,就先寫一篇實...
摘要:本來以為是無參調用時返回了運算的幺元,后來細琢磨,好像沒有什么關系,對于運算集合上的二元運算,如果滿足,則是運算的幺元。乘法運算的幺元是,因為。但是我們定義函數或者函數,如果不傳遞參數時,返回幺元的話也是不合理的。 昨天心血來潮寫了一篇文章:為什么Math.min() 比 Math.max() 大? 為什么很多人會有這種疑惑,是因為犯了想當然的錯誤——望文生義。Math.min() 作...
閱讀 2322·2021-11-23 09:51
閱讀 3755·2021-11-11 10:57
閱讀 1404·2021-10-09 09:43
閱讀 2492·2021-09-29 09:35
閱讀 2023·2019-08-30 15:54
閱讀 1793·2019-08-30 15:44
閱讀 3187·2019-08-30 13:20
閱讀 1697·2019-08-30 11:19