ECMAScript 6入門(阮一峯)javascript
setInterval同級別的另一個函數:setTimeout。html
設置n秒後,有必定時間延時的,2ms左右;vue
最低時間爲4ms,參考傳送門java
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不可捕獲node
try{ setTimeout(function(){ throw new Error("我不但願這個錯誤出現!") }, 1000); } catch(e){ console.log(e.message); }
一般做爲參數進行傳遞react
function getData(callback) { $.ajax({ url: '', success: resp => { callback(resp); } }); } getData(resp => { // write your code here });
調用的時候,能夠直接調用,還能夠經過bind,call,apply指定當前做用域git
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 });
1.js自定義事件監聽:es6
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自定義事件github
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'); } })
1.實現一個消息發佈
let subPub = new SubPub(); subPub.subscribe('getName', name => { console.log('your name is: ', name); }); subPub.publish('getName', 'Tom');
1.觀察者模式和發佈/訂閱的區別:<br/>
1.1.Observer模式要求但願接收到主題通知者的觀察者必須訂閱內容改變的事件<br/>
1.2.Subscribe/Publish模式使用了一個主題/事件通道,這個通道介於訂閱者和發佈者之間。該事件系統容許代碼定義應用程序的特定事件,該事件能夠傳遞自定義參數,自定義參數包含訂閱者所須要的值。其目的是避免訂閱者和發佈者產生依賴關係。<br/>
from: 《Javascript設計模式》<br/>
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引用?
3.1.Promise 本質是一個狀態機。每一個 promise 只能是 3 種狀態中的一種:pending、fulfilled 或 rejected。狀態轉變只能是 pending -> fulfilled 或者 pending -> rejected。狀態轉變不可逆。
3.2.then 方法能夠被同一個 promise 調用屢次。
3.3.then 方法必須返回一個 promise。
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); });
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.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); })]);
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; }
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);
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函數
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是就不用了、仍是應該儘可能避免?