摘要:前言入門阮一峰另類的實現同級別的另外一個函數。該事件系統允許代碼定義應用程序的特定事件,該事件可以傳遞自定義參數,自定義參數包含訂閱者所需要的值。其目的是避免訂閱者和發布者產生依賴關系。狀態轉變不可逆。方法必須返回一個。
callback 前言
ECMAScript 6入門(阮一峰)
setInterval: 另類的callback實現setInterval同級別的另外一個函數:setTimeout。
設置n秒后,有一定時間延時的,2ms左右;
最低時間為4ms,參考傳送門
var d = new Date, count = 0, f, timer; timer = setInterval(f = function (){ if(new Date - d > 1000) { clearInterval(timer), console.log(count); } count++; }, 0);
setTimeout中的錯誤使用try,catch不可捕獲
try{ setTimeout(function(){ throw new Error("我不希望這個錯誤出現!") }, 1000); } catch(e){ console.log(e.message); }callback: 常用的javascript回調
通常作為參數進行傳遞
function getData(callback) { $.ajax({ url: "", success: resp => { callback(resp); } }); } getData(resp => { // write your code here });
調用的時候,可以直接調用,還可以通過bind,call,apply指定當前作用域
function getData(callback) { $.ajax({ url: "", success: resp => { callback(resp.data); callback.bind(null)(resp.data); callback.call(null, resp.data); callback.apply(null, resp.data); } }); } getData((...resp) => { // write your code here });事件監聽: 一般用作dom的事件綁定
1.js自定義事件監聽:
let myEvents = new MyEvent(); myEvents.addEvents({ once: () => { console.log("只會console一次"); myEvents.removeEvent("once"); }, infinity: () => { console.log("每次點擊,都會console"); } }); document.onclick = e => { myEvents.fireEvents(["once", "infinity"]); }
2.DOM自定義事件
let elImage = document.getElementById("image"); $(elImage).addEvent("click", e => { e = e || window.event; let target = e.target || e.srcElement; // 元素節點 為1; 元素屬性 為2 if (target.nodeType === 1) { console.log(`點擊類型:${e.type}`); $(target).fireEvent("console"); } })
2.1.nodeType:
2.2.DOM事件流:
發布/訂閱: 消息通訊1.實現一個消息發布
let subPub = new SubPub(); subPub.subscribe("getName", name => { console.log("your name is: ", name); }); subPub.publish("getName", "Tom");
1.觀察者模式和發布/訂閱的區別:
1.1.Observer模式要求希望接收到主題通知者的觀察者必須訂閱內容改變的事件
1.2.Subscribe/Publish模式使用了一個主題/事件通道,這個通道介于訂閱者和發布者之間。該事件系統允許代碼定義應用程序的特定事件,該事件可以傳遞自定義參數,自定義參數包含訂閱者所需要的值。其目的是避免訂閱者和發布者產生依賴關系。
from: 《Javascript設計模式》
2.nodejs版本的消息發布、訂閱
const EventEmitter = require("events"); class MyEmitter extends EventEmitter {} const myEmitter = new MyEmitter(); myEmitter.on("event", (a, b) => { console.log(a, b, this); }); myEmitter.emit("event", "a", "b");
2.1.ES6中import對于循環引用的處理問題
TODO: require引用?
2.2.?commonJS中require是值的copy?,ES6中import是值的引用
3.更高級的狀態管理:redux,vuex
promise: 回調的代碼組織的封裝 1.promise A+規范: wiki、plus、A+翻譯 2.promise的流程 3.要點3.1.Promise 本質是一個狀態機。每個 promise 只能是 3 種狀態中的一種:pending、fulfilled 或 rejected。狀態轉變只能是 pending -> fulfilled 或者 pending -> rejected。狀態轉變不可逆。
3.2.then 方法可以被同一個 promise 調用多次。
3.3.then 方法必須返回一個 promise。
4.一些問題4.1.下面四個使用promise語句的不同點在哪里?
doSomething().then(function () { return doSomethingElse(); }).then(finalHandler); doSomething().then(function () { doSomethingElse(); }).then(finalHandler); doSomething().then(doSomethingElse()).then(finalHandler); doSomething().then(doSomethingElse).then(finalHandler);
4.2.新手問題:
4.2.1.callback方式使用promise
// anotherPromise依賴somePromise // 不推薦 somePromise() .then(data => { anotherPromise(data.id) .then(anotherData => { // write your code here }) .catch(window.console.log.bind(window.console)) }) .catch(window.console.log.bind(window.console)) // 推薦 somePromise() .then(data => { return anotherPromise(data.id).then(data, anotherData); }) then((data, another) => { }) .catch(window.console.log.bind(window.console))
4.2.2.forEach使用promise,應該使用Promise.all
// 不推薦 let promises = [new Promise(resolve => { let dataA = { name: "dataA" }; resolve(dataA); }), new Promise(resolve => { let dataB = { name: "dataB" }; resolve(dataB); })]; let keys = ["dataA", "dataB"] let dataAll = {}; promises.forEach((promise, index) => { promise .then(data => { dataAll[keys[index]] = data; }) .catch(e => { console.log("error: ", e); }) }); // 推薦 Promise .all(promises) .then(data => { // [dataA, dataB] })
4.2.3.忘記加catch
somePromise() .then(() => { return anotherPromise(); }) .then(() => { return lastPromise(); }) // 沒有業務錯誤需求,加上這句就方便調試 .catch(console.log.bind(console));
4.2.3.不推薦使用deferred(歷史包袱),兩種方式改正
4.2.3.1.使用第三方的庫包裝成promise,如angular的$q庫:
$q.when(db.put(doc)).then(...)
4.2.3.2.使用promise:
new Promise(function (resolve, reject) { fs.readFile("myfile.txt", function (err, file) { if (err) { return reject(err); } resolve(file); }); }) .then(...)
4.2.4.不顯示調用return
somePromise() .then(() => { anotherPromise(); }) .then(data => { // data was undefined })
4.3.進階錯誤
4.3.1.不了解Promise.resolve()/Promise.reject();
4.3.2.catch和then(null, reject => {})不完全相同: then中的rejectHandler不會捕獲resolveHandler中的錯誤
// 1.then reject somePromise().then(resolve => { throw new Error("error"); }, reject => { // catch nothing }) // 2.catch: this type was recomended somePromise() .then(resolve => { throw new Error("error"); }) .catch(e => { // catch the error }) // 3.the same as below: somePromise() .then(resolve => { throw new Error("error"); }) .then(null, e => { // catch the error })
4.3.3.promise vs promise factories: 一個接一個執行一系列的promise
function executeSequentially(promiseFactories) { var result = Promise.resolve(); promiseFactories.forEach(function (promiseFactory) { result = result.then(promiseFactory); }); return result; } // 使用promise工廠 function myPromiseFactory() { return somethingThatCreatesAPromise(); } // 示例: let promiseFactories = []; promiseFactories.push(myPromiseFactory); executeSequentially(promiseFactories);
4.3.4.想要兩個promise的結果
4.3.4.1.原始代碼
let getUserAndAccount = user => { return new Promise((resolve, reject) => { getUserAccountById(user.id) .then(userAccount => { resolve(user, userAccount); }) .catch(reject); }) } getUserByName("nolan") .then(getUserAndAccount) .then(function (user, userAccount) { console.log("user and userAccount: ", user, userAccount); }) .cath(e => { console.log("error: ", e); });
4.3.4.2.簡化后代碼
let getUserAndAccount = user => getUserAccountById(user.id) .then(userAccount => Promise.resolve(user, userAccount)) getUserByName("nolan") .then(getUserAndAccount) .then(function (user, userAccount) { console.log("user and userAccount: ", user, userAccount); }) .cath(e => { console.log("error: ", e); });
4.3.5.值穿透
Promise.resolve("foo").then(Promise.resolve("bar")).then(function (result) { console.log(result); });
4.3.6.不能cancel?,issue70, proposal-cancelable-promises
5.一些提議5.1.then方法內部相關:
5.1.1.return一個promise對象。
5.1.2.return一個同步值或者是undefined
5.1.3.同步的throw一個錯誤
getUserByName("nolan").then(function (user) { if (user.isLoggedOut()) { throw new Error("user logged out!"); // throwing a synchronous error! } return inMemoryCache[user.id] || getUserAccountById(user.id); // returning a synchronous value or a promise! }).then(function (userAccount) { // I got a user account! }).catch(function (err) { // Boo, I got an error! if (err) { let message = err.message; if (~message.indexOf("logged")) { // 已經登出的處理邏輯 } else { // 其他的錯誤處理邏輯 } } });6.一些Promise知識點
6.1.Promise.all, Promise.race
6.1.1.相同點: Promise.race和Promise.all都能接收一個數組
6.1.2.不同點: Promise.race只要有一個reject或者resolve,就立即返回,Promise.all等待所有的resolve,reject,才會返回,如果有一個reject,那么all的結果也是reject的(所有的resolve,才會resolve)
Promise.all([new Promise((resolve, reject) => { setTimeout(() => { console.log("first"); }, 1000); }), Promise.reject(123), new Promise((resolve, reject) => { console.log("second"); resolve(); })]) .then(data => { console.log("I am all data: ", data); }) .catch(e => { console.log("error", e); });
6.1.3.使用場景: Promise.race可以在ajax網絡超時判斷使用
let timeout = 3e3; Promise.race([new Promise((resolve, reject) => { $.ajax("url", resp => { console.log("ajax resp: ", resp); }); }), new Promise((resolve, reject) => { setTimeout(resolve, timeout); })]);
generator,yeild: 流程控制的新語法 1.generator的含義與定義: 異步操作的容器6.2.Promise.resolve返回一個已經resolve的promise對象,reject同理
function* gen(){ let url = "https://api.github.com/users/github"; let result = yield fetch(url); console.log("result: ", result.bio); } let genUser = () => { let g = gen(); let result = g.next(); result.value.then(data => { let json = data.json(); return json; }).then(data => { g.next(data); }); }
1.1.函數可以暫停執行和恢復執行
1.2.Generator 函數可以不用yield表達式,這時就變成了一個單純的暫緩執行函數
function* f() { console.log("執行了!") } var generator = f(); setTimeout(function () { generator.next() }, 2000);
1.3.函數體內外的數據交換和
function* gen(x){ var y = yield x + 2; console.log("gen(): ", y, x); return y; } var g = gen(1); var value = g.next(); console.log("value: ", value); var value2 = g.next(12); console.log("value2: ", value2);
1.4.錯誤處理機制
function* gen(x){ var y; try{ y = yield x + 2; console.log("gen(): ", y, x); }catch(e){ console.log(e); } return y; } var g = gen(1); var value = g.next(); console.log("value: ", value); var value2 = g.throw(new Error("error"));
1.5.yield表達式只能用在 Generator 函數里面
function f(param) { let a = yield 3 * param; }2.Thunk函數的含義與定義: 可以在回調函數里,將執行權交還給 Generator 函數,生產環境推薦thunkify
var gen = function* (){ var f1 = yield readFile("fileA"); var f2 = yield readFile("fileB"); // ... var fn = yield readFile("fileN"); }; run(gen);
2.thunk函數介紹: 誕生于上個60年代
2.1.1.傳值調用
let f = (a, b) => b; f(3 * x * x - 2 * x - 1, x);
2.1.2.傳名調用
let f = m => m * 2; f(x + 5); // 等同于 let thunk () => (x + 5); let f = thunk => (thunk() * 2);
2.1.3.thunkify源碼:
function thunkify(fn){ return function(){ let args = Array.prototype.slice.call(arguments); let ctx = this; return function(done){ // 檢查機制: 確保回調函數只運行一次 let called; args.push(function(){ if (called) return; called = true; done.apply(null, arguments); }); try { fn.apply(ctx, args); } catch (err) { done(err); } } } };
2.1.4.thunk與generator結合:
let fs = require("fs"); let thunkify = require("thunkify"); let readFile = thunkify(fs.readFile); let gen = function* (){ let r1 = yield readFile("/etc/fstab"); console.log(r1.toString()); let r2 = yield readFile("/etc/shells"); console.log(r2.toString()); };
2.1.5.手動執行:
let g = gen(); let r1 = g.next(); r1.value(function(err, data){ if (err) throw err; let r2 = g.next(data); r2.value(function(err, data){ if (err) throw err; g.next(data); }); });
2.1.6.簡化封裝:
function run(fn) { let gen = fn(); function next(err, data) { let result = gen.next(data); if (result.done) return; result.value(next); } next(); } run(gen);3.co函數庫的含義與定義: Generator 函數的執行器, yield后必須是thunk/promise函數
var gen = function* (){ var f1 = yield readFile("/etc/fstab"); var f2 = yield readFile("/etc/shells"); console.log(f1.toString()); console.log(f2.toString()); }; var co = require("co"); co(gen);
3.1.協程與事件循環: 控制流的主動讓出和恢復
3.1.1.提出時間: 1963; 提出人: Melvin Conway
3.1.2.歷程: 進程->線程->用戶態線程->協程
3.1.3.名詞釋義:
3.1.3.1.進程: 代碼,被代碼控制的資源(內存,I/O,文件)兩大基本元素等組成的實體,兩大特性[掌控資源,可以被調度]
3.1.3.2.線程: 程在進程內部,處理并發的邏輯,擁有獨立的棧,卻共享線程的資源
3.1.3.3.用戶態線程: 線程切換的時候,進程需要為了管理而切換到內核態,處理狀態轉換(性能消耗嚴重)
3.1.4.沒火的原因: 命令式編程(自頂向下開發,子歷程作為唯一控制結構)、函數式編程[意氣之爭]
3.1.5.關系: 子歷程是沒有使用yield的協程。Donald Ervin Knuth(wiki)/Donald Ervin Knuth(baike): 子歷程是協程的一種特例
3.2.使用co, yield后面放的必須是thunk/promise函數
async,await: generator的語法糖 async的含義與定義let getData = () => { return new Promise((resolve, reject) => { $.ajax({ url: "json/test.json", method: "GET", success: function (resp) { // data = resp.data; resolve(resp); }, error: function (error) { reject(error); } }); }); } async function initView(){ try { let resp = await getData(); console.log(resp); } catch (e) { console.error(e); } } initView();async的一些問題
1.同時觸發:
// 寫法一 let [foo, bar] = await Promise.all([getFoo(), getBar()]); // 寫法二 let fooPromise = getFoo(); let barPromise = getBar(); let foo = await fooPromise; let bar = await barPromise;最后的一些問題與思考
參考資料1.從異步操作上,async是最后演化的結果,callback是就不用了、還是應該盡量避免?
Node.js最新技術棧之Promise篇
Promise實現原理
詳解ES6 中的Promise與異步編程
深入Promise
你可能不知道的Promise
談談使用promise時候的一些反模式(EFE)
Promise Demo Implement
Promise Demo Implement for Question
JavaScript Promise迷你書(中文版)
mdn Promise
JavaScript Promises ... In Wicked Detail
JavaScript異步編程原理
深入掌握ECMAScript 6 異步編程系列(阮一峰)
漫談js自定義事件、DOM/偽DOM自定義事件(張鑫旭)
js原生創建模擬事件和自定義事件
JS觀察者模式
NodeJS Event
NodeJS EventEmitter
JS發布/訂閱簡單實現
擴展閱讀JS函數式編程指南
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/83679.html
摘要:的支持的方法有幾個主要的,和,比如官方有一個例子這兩個作為函數調用的生成從基本可以看出,函數生成了一個對象,這個對象的方法是添加回調函數,而方法則是執行回調函數。 歡迎來我的專欄查看系列文章。 講真,Sizzle 的源碼真的太壓抑了,以至于寫 Sizzle 文章的這段時間里都非常的痛苦,剛開始覺得它還挺有意思的,越到后面越覺得代碼很難讀懂,煩。 寒假也過完了,在家里待了兩周的時間,感覺...
摘要:源碼參考主要內容的迭代設計中主要的代碼片段,翻譯一部分加上自己的理解,同時指出的一些特性。先貼完整代碼安全性和穩定性保證和在未來他們被調用的時候,應該是和注冊時的順序是保持一致的。這將顯著降低異步編程中流程控制出錯可能性。 源碼參考https://github.com/kriskowal/...主要內容:promise的迭代設計中主要的代碼片段,翻譯一部分加上自己的理解,同時指出pro...
前言 我在學習瀏覽器和NodeJS的Event Loop時看了大量的文章,那些文章都寫的很好,但是往往是每篇文章有那么幾個關鍵的點,很多篇文章湊在一起綜合來看,才可以對這些概念有較為深入的理解。 于是,我在看了大量文章之后,想要寫這么一篇博客,不采用官方的描述,結合自己的理解以及示例代碼,用最通俗的語言表達出來。希望大家可以通過這篇文章,了解到Event Loop到底是一種什么機制,瀏覽器和Nod...
摘要:接受個參數,包括事件的名稱,回調函數和回調函數執行的上下文環境。保留回調函數在數組中取出對應的以及中的函數。當然,你同樣可以在綁定的回調函數執行前手動通過將其移除。 Backbone源碼解讀 Backbone在流行的前端框架中是最輕量級的一個,全部代碼實現一共只有1831行1。從前端的入門再到Titanium,我雖然幾次和Backbone打交道但是卻對它的結構知之甚少,也促成了我想讀...
摘要:序言最近閑暇無事閱讀了一下的源碼對整體的結構有了初步認識與大家分享不知道為什么右邊的目錄一直出不來非常不舒服不如移步到吧是的核心模塊也是個調度模塊各種異步事件都是由他調度的所以必須弄清他的執行邏輯源碼分析而的核心部分則是這個循環內部的邏輯貼 序言 最近閑暇無事,閱讀了一下tornado的源碼,對整體的結構有了初步認識,與大家分享 不知道為什么右邊的目錄一直出不來,非常不舒服. 不如移...
閱讀 2022·2023-04-25 23:30
閱讀 1452·2021-11-24 10:18
閱讀 3070·2021-10-09 09:54
閱讀 2017·2021-10-08 10:05
閱讀 3431·2021-09-23 11:21
閱讀 3161·2019-08-30 15:52
閱讀 1560·2019-08-30 13:05
閱讀 1056·2019-08-30 13:02