摘要:環(huán)境中產(chǎn)生異步操作的函數(shù)分為兩大類計時函數(shù)和函數(shù)。如果要在應(yīng)用中定義復(fù)雜的異步操作,就要使用者兩類異步函數(shù)作為基本的構(gòu)造快。在本例子中同步事件循環(huán)不包含內(nèi)部的外部的異步事件循環(huán)內(nèi)部的。其弊端是操作強(qiáng)耦合維護(hù)代價高。
JavaScript環(huán)境中產(chǎn)生異步操作的函數(shù)分為兩大類:計時函數(shù)和I/O函數(shù)。如果要在應(yīng)用中定義復(fù)雜的異步操作,就要使用者兩類異步函數(shù)作為基本的構(gòu)造快。本文沒有對某個知識點(diǎn)細(xì)致展開,僅供思路參考。1. 計時函數(shù)
先看一個經(jīng)典的例子:
for(var i = 0;i < 5; i++){ setTimeout(function(){ console.log(i); },5000); } console.log(i);
結(jié)果輸出什么?立馬輸出一個5,5秒鐘過后連續(xù)輸出5個5(嗚嗚嗚嗚嗚~),搞懂為什么這樣輸出5,需要知道3件事:
這里只有一個i變量,作用域由var定義,不管i怎么變,都指向同一個內(nèi)存區(qū)域;
循環(huán)結(jié)束后 i=== 5;
JavaScript事件處理器在線程空閑之前不會執(zhí)行。
來,再來和我背一遍口訣:先同步后異步最后回調(diào)。在本例子中:
同步事件:for循環(huán)(不包含內(nèi)部的setTimeout);外部的console.log(i);
異步事件:for循環(huán)內(nèi)部的setTimeout。
先執(zhí)行for循環(huán),遇到setTimeout壓入延遲事件隊列,一邊循環(huán)一邊壓入隊列;for循環(huán)結(jié)束執(zhí)行外部的console.log(i),此時i=5,故立即輸出5,此時同步事件執(zhí)行完畢,接下來開始執(zhí)行異步事件setTimeout,5個setTimeout事件等待5秒同時在等待,所以5秒結(jié)束連續(xù)輸出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){};
猜猜結(jié)果是什么?
調(diào)換一下時間:
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函數(shù)這里的I/O是一個廣義的概念,包括讀寫文件,GET或POST請求,異步讀取值值函數(shù)等等。一個常見的讀文件的操作: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的內(nèi)容:
hello world hello async
node fs.js:
沒毛病!設(shè)想一個場景,在另外一個函數(shù),假設(shè)名字叫panfen(),里面有一堆代碼,其中需要用到從data.txt文件讀取的數(shù)據(jù),常見的做法是把fs.readFile操作寫在panfen()里面,處理data的操作都寫在fs.readFile的callback里面,如果callback里面還要寫數(shù)據(jù)呢?繼續(xù)執(zhí)行fs.writeFile在它的callback里面執(zhí)行其他操作...
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{ ... } }); } });
這就是回調(diào)金字塔。其弊端是操作強(qiáng)耦合、維護(hù)代價高。如何做到理想的異步呢?
靈機(jī)一動這樣寫:
var fs = require("fs"); var data = fs.readFile("data.txt",function(err,data){ return data ? data : err; }); fs.writeFile("./data_copy.txt", data, function(){ ... });
然而,根據(jù)先同步后異步最后回調(diào)的法則,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的特點(diǎn)在于可以用then方法向下傳遞值。
4. Generator 4.1 function*關(guān)于生成器函數(shù),這里不作具體介紹,簡單看一例子:
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*的區(qū)別:
yield只返回右值;
yield*將函數(shù)委托給另一個生成器,或可迭代的對象(字符串、數(shù)組、arguments、Map、Set)
注: yield關(guān)鍵字只能出現(xiàn)在生成器函數(shù)里面!!!否則會報錯: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 的發(fā)布,越來越多的人開始研究async/await,據(jù)說這是異步編程終級解決方案的 。個人覺得沒有最好,只有更好。用這種方式改寫:
"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();
當(dāng)然,這個例子還不足以體現(xiàn)async/awit的優(yōu)勢。
6. 總結(jié)JavaScript的異步操作可能是區(qū)別其他語言比較大的一點(diǎn),也是一個難點(diǎn),不過也是很有趣的。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/82601.html
摘要:前言這個系列的上一篇文章編寫自己的代碼庫常用實(shí)例的實(shí)現(xiàn)與封裝總結(jié)了個常見的操作。前序修改以及寫法優(yōu)化此處修改之前提交函數(shù)已經(jīng)發(fā)現(xiàn)的,基于這個系列上篇文章的提供的函數(shù)。 1.前言 這個系列的上一篇文章(編寫自己的代碼庫(javascript常用實(shí)例的實(shí)現(xiàn)與封裝))總結(jié)了34個常見的操作。但是在開發(fā)中,常見的實(shí)例又何止這么多個,經(jīng)過這些日子的探索,以及他人的意見,現(xiàn)在得追加一些操作實(shí)例了。...
摘要:前言在異步編程之一中實(shí)現(xiàn)了一個異步函數(shù)調(diào)用鏈,它是一個順序調(diào)用鏈,很類似責(zé)任鏈模式,但現(xiàn)實(shí)往往不是平鋪直敘的,更多的其實(shí)是峰回路轉(zhuǎn),本文將繼續(xù)討論更多的用法。 前言 在《ES6 異步編程之一:Generator》中實(shí)現(xiàn)了一個異步函數(shù)調(diào)用鏈,它是一個順序調(diào)用鏈,很類似責(zé)任鏈模式,但現(xiàn)實(shí)往往不是平鋪直敘的,更多的其實(shí)是峰回路轉(zhuǎn),本文將繼續(xù)討論更多Generator的用法。 作為函數(shù)的Gen...
摘要:定時器例子之前通過調(diào)用定時器,需要傳一個回調(diào),然后所有的代碼邏輯都包在里面。這里定時器會阻塞在這一行,直到一秒后才會執(zhí)行下面的一行。 之前介紹過quasar,如果你希望在vert.x項目里使用coroutine的話,建議使用vertx-sync。本篇將介紹vertx-sync。 showImg(/img/bVzIsu); 本來打算另起一篇,寫其他方面的東西,但是最近比較忙,就先寫一篇實(shí)...
摘要:本來以為是無參調(diào)用時返回了運(yùn)算的幺元,后來細(xì)琢磨,好像沒有什么關(guān)系,對于運(yùn)算集合上的二元運(yùn)算,如果滿足,則是運(yùn)算的幺元。乘法運(yùn)算的幺元是,因為。但是我們定義函數(shù)或者函數(shù),如果不傳遞參數(shù)時,返回幺元的話也是不合理的。 昨天心血來潮寫了一篇文章:為什么Math.min() 比 Math.max() 大? 為什么很多人會有這種疑惑,是因為犯了想當(dāng)然的錯誤——望文生義。Math.min() 作...
閱讀 2311·2021-11-23 09:51
閱讀 3748·2021-11-11 10:57
閱讀 1391·2021-10-09 09:43
閱讀 2481·2021-09-29 09:35
閱讀 2013·2019-08-30 15:54
閱讀 1788·2019-08-30 15:44
閱讀 3179·2019-08-30 13:20
閱讀 1687·2019-08-30 11:19