var d = new Date, count = 0, f, timer; timer = setInterval(f = function (){ if(new Date - d > 1000) { clearInterval(timer), console.log(count); } count++; }, 0);
try{ setTimeout(function(){ throw new Error("我不但願這個錯誤出現!") }, 1000); } catch(e){ console.log(e.message); }
function getData(callback) { $.ajax({ url: '', success: resp => { callback(resp); } }); } getData(resp => { // write your code here });
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 });
let myEvents = new MyEvent(); myEvents.addEvents({ once: () => { console.log('只會console一次'); myEvents.removeEvent('once'); }, infinity: () => { console.log('每次點擊,都會console'); } }); document.onclick = e => { myEvents.fireEvents(['once', 'infinity']); }
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'); } })
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設計模式》
vue
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對於循環引用的處理問題java
TODO: require引用?node
2.2.?commonJS中require是值的copy?,ES6中import是值的引用react
doSomething().then(function () { return doSomethingElse(); }).then(finalHandler); doSomething().then(function () { doSomethingElse(); }).then(finalHandler); doSomething().then(doSomethingElse()).then(finalHandler); doSomething().then(doSomethingElse).then(finalHandler);
// 不推薦 somePromise() .then(data => { anotherPromise() .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().then(data, anotherData); }) then((data, another) => { }) .catch(window.console.log.bind(window.console))
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); }) });
somePromise() .then(() => { return anotherPromise(); }) .then(() => { return lastPromise(); }) // 沒有業務錯誤需求,加上這句就方便調試 .catch(console.log.bind(console));
4.2.3.不推薦使用deferred(歷史包袱),兩種方式改正es6
4.2.3.1.使用第三方的庫包裝成promise,如angular的$q庫:github
$q.when(db.put(doc)).then(...)
new Promise(function (resolve, reject) { fs.readFile('myfile.txt', function (err, file) { if (err) { return reject(err); } resolve(file); }); }) .then(...)
somePromise() .then(() => { anotherPromise(); }) .then(data => { // data was undefined })
// 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 })
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的結果ajax
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); });
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); });
Promise.resolve('foo').then(Promise.resolve('bar')).then(function (result) { console.log(result); });
return
一個promise對象。return
一個同步值或者是undefinedthrow
一個錯誤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 { // 其餘的錯誤處理邏輯 } } });
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); });
let timeout = 3e3; Promise.race([new Promise((resolve, reject) => { $.ajax('url', resp => { console.log('ajax resp: ', resp); }); }), new Promise((resolve, reject) => { setTimeout(resolve, timeout); })]);
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);
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);
function f(param) { let a = yield 3 * param; }
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);
let f = m => m * 2; f(x + 5); 60年代就誕生 // 等同於 let thunk () => (x + 5); let f = thunk => (thunk() * 2);
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); } } } };
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()); };
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); }); });
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);
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.用戶態線程: 線程切換的時候,進程須要爲了管理而切換到內核態,處理狀態轉換(性能消耗嚴重)
3.1.5.關係: 子歷程是沒有使用yield的協程。Donald Ervin Knuth(wiki)/Donald Ervin Knuth(baidu): 子歷程是協程的一種特例
3.2.沒有異常處理的簡化版co函數
function co(gen){ let def = Promise.defer(); let iter = gen(); function resolve(data) { // 恢復迭代器並帶入promise的終值 step(iter.next(data)); } function step(it) { it.done ? // 迭代結束則解決co返回的promise def.resolve(it.value) : // 不然繼續用解決程序解決下一個讓步出來的promise it.value.then(resolve); } resolve(); return def.promise; }
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();
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是就不用了、仍是應該儘可能避免?