callback

callback

前言

setInterval: 另類的callback實(shí)現(xiàn)

  • setInterval同級(jí)別的另外一個(gè)函數(shù):setTimeout。
  • 設(shè)置n秒后,有一定時(shí)間延時(shí)的,2ms左右;
  • 最低時(shí)間為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中的錯(cuò)誤使用try,catch不可捕獲
try{
    setTimeout(function(){
        throw new Error("我不希望這個(gè)錯(cuò)誤出現(xiàn)!")
    }, 1000);
} catch(e){
    console.log(e.message);
}

callback: 常用的javascript回調(diào)

  • 通常作為參數(shù)進(jìn)行傳遞
function getData(callback) {
    $.ajax({
        url: '',
        success: resp => {
            callback(resp);
        }
    });
}
getData(resp => {
    // write your code here
});
  • 調(diào)用的時(shí)候,可以直接調(diào)用,還可以通過bind,call,apply指定當(dāng)前作用域
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
});

事件監(jiān)聽: 一般用作dom的事件綁定

let myEvents = new MyEvent();
myEvents.addEvents({
    once: () => {
        console.log('只會(huì)console一次');
        myEvents.removeEvent('once');
    },
    infinity: () => {
        console.log('每次點(diǎn)擊,都會(huì)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;

    // 元素節(jié)點(diǎn) 為1; 元素屬性 為2
    if (target.nodeType === 1) {
        console.log(`點(diǎn)擊類型:${e.type}`);
        $(target).fireEvent('console');
    }
})
事件流圖片

發(fā)布/訂閱: 消息通訊

  • 1.實(shí)現(xiàn)一個(gè)消息發(fā)布
let subPub = new SubPub();
subPub.subscribe('getName', name => {
    console.log('your name is: ', name);
});
subPub.publish('getName', 'Tom');

1.觀察者模式和發(fā)布/訂閱的區(qū)別:

1.1.Observer模式要求希望接收到主題通知者的觀察者必須訂閱內(nèi)容改變的事件

1.2.Subscribe/Publish模式使用了一個(gè)主題/事件通道,這個(gè)通道介于訂閱者和發(fā)布者之間。該事件系統(tǒng)允許代碼定義應(yīng)用程序的特定事件,該事件可以傳遞自定義參數(shù),自定義參數(shù)包含訂閱者所需要的值。其目的是避免訂閱者和發(fā)布者產(chǎn)生依賴關(guān)系。

from: 《Javascript設(shè)計(jì)模式》

  • 2.nodejs版本的消息發(fā)布、訂閱
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對(duì)于循環(huán)引用的處理問題

TODO: require引用?

  • 2.2.?commonJS中require是值的copy?,ES6中import是值的引用

  • 3.更高級(jí)的狀態(tài)管理:redux,vuex

promise: 回調(diào)的代碼組織的封裝

1.promise A+規(guī)范: wiki、plusA+翻譯

2.promise的流程

3.要點(diǎn)

  • 3.1.Promise 本質(zhì)是一個(gè)狀態(tài)機(jī)。每個(gè) promise 只能是 3 種狀態(tài)中的一種:pending、fulfilled 或 rejected。狀態(tài)轉(zhuǎn)變只能是 pending -> fulfilled 或者 pending -> rejected。狀態(tài)轉(zhuǎn)變不可逆。
  • 3.2.then 方法可以被同一個(gè) promise 調(diào)用多次。
  • 3.3.then 方法必須返回一個(gè) promise。

4.一些問題

  • 4.1.下面四個(gè)使用promise語句的不同點(diǎn)在哪里?
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
// 不推薦
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))
  • 4.2.2.forEach使用promise,應(yīng)該使用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);
  })
});
  • 4.2.3.忘記加catch
somePromise()
.then(() => {
  return anotherPromise();
})
.then(() => {
  return lastPromise();
})
// 沒有業(yè)務(wù)錯(cuò)誤需求,加上這句就方便調(diào)試
.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.不顯示調(diào)用return
somePromise()
.then(() => {
  anotherPromise();
})
.then(data => {
  // data was undefined
})
  • 4.3.進(jìn)階錯(cuò)誤
  • 4.3.1.不了解Promise.resolve()/Promise.reject();
  • 4.3.2.catch和then(null, reject => {})不完全相同: then中的rejectHandler不會(huì)捕獲resolveHandler中的錯(cuò)誤
// 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: 一個(gè)接一個(gè)執(zhí)行一系列的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.想要兩個(gè)promise的結(jié)果

  • 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);
});
Promise.resolve('foo').then(Promise.resolve('bar')).then(function (result) {
  console.log(result);
});

5.一些提議

  • 5.1.then方法內(nèi)部相關(guān):
  • 5.1.1.return一個(gè)promise對(duì)象。
  • 5.1.2.return一個(gè)同步值或者是undefined
  • 5.1.3.同步的throw一個(gè)錯(cuò)誤
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')) {
      // 已經(jīng)登出的處理邏輯
    } else {
      // 其他的錯(cuò)誤處理邏輯
    }
  }
});

6.一些Promise知識(shí)點(diǎn)

  • 6.1.Promise.all, Promise.race
  • 6.1.1.相同點(diǎn): Promise.race和Promise.all都能接收一個(gè)數(shù)組
  • 6.1.2.不同點(diǎn): Promise.race只要有一個(gè)reject或者resolve,就立即返回,Promise.all等待所有的resolve,reject,才會(huì)返回,如果有一個(gè)reject,那么all的結(jié)果也是reject的(所有的resolve,才會(huì)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網(wǎng)絡(luò)超時(shí)判斷使用
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返回一個(gè)已經(jīng)resolve的promise對(duì)象,reject同理

generator,yeild: 流程控制的新語法

1.generator的含義與定義: 異步操作的容器

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.函數(shù)可以暫停執(zhí)行和恢復(fù)執(zhí)行

  • 1.2.Generator 函數(shù)可以不用yield表達(dá)式,這時(shí)就變成了一個(gè)單純的暫緩執(zhí)行函數(shù)

function* f() {
    console.log('執(zhí)行了!')
}

var generator = f();

setTimeout(function () {
    generator.next()
}, 2000);

  • 1.3.函數(shù)體內(nèi)外的數(shù)據(jù)交換和?錯(cuò)誤處理機(jī)制?
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.yield表達(dá)式只能用在 Generator 函數(shù)里面
function f(param) {
    let a = yield 3 * param;
}

2.Thunk函數(shù)的含義與定義: 可以在回調(diào)函數(shù)里,將執(zhí)行權(quán)交還給 Generator 函數(shù),生產(chǎn)環(huán)境推薦thunkify

var gen = function* (){
  var f1 = yield readFile('fileA');
  var f2 = yield readFile('fileB');
  // ...
  var fn = yield readFile('fileN');
};

run(gen);
  • 2.thunk函數(shù)介紹: 誕生于上個(gè)60年代

  • 2.1.1.傳值調(diào)用

let f = (a, b) => b;

f(3 * x * x - 2 * x - 1, x);
  • 2.1.2.傳名調(diào)用
let f = m => m * 2;

f(x + 5);

60年代就誕生
// 等同于

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){
      // 檢查機(jī)制: 確?;卣{(diào)函數(shù)只運(yùn)行一次
      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結(jié)合:
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.手動(dòng)執(zhí)行:
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.結(jié)合:
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函數(shù)庫的含義與定義: Generator 函數(shù)的執(zhí)行器, yield后必須是thunk/promise函數(shù)

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.協(xié)程與事件循環(huán): 控制流的主動(dòng)讓出和恢復(fù)

  • 3.1.1.提出時(shí)間: 1963; 提出人: Melvin Conway

  • 3.1.2.歷程: 進(jìn)程->線程->用戶態(tài)線程->協(xié)程

  • 3.1.3.名詞釋義:

  • 3.1.3.1.進(jìn)程: 代碼,被代碼控制的資源(內(nèi)存,I/O,文件)兩大基本元素等組成的實(shí)體,兩大特性[掌控資源,可以被調(diào)度]

  • 3.1.3.2.線程: 程在進(jìn)程內(nèi)部,處理并發(fā)的邏輯,擁有獨(dú)立的棧,卻共享線程的資源

  • 3.1.3.3.用戶態(tài)線程: 線程切換的時(shí)候,進(jìn)程需要為了管理而切換到內(nèi)核態(tài),處理狀態(tài)轉(zhuǎn)換(性能消耗嚴(yán)重)

  • 3.1.4.沒火的原因: 命令式編程(自頂向下開發(fā),子歷程作為唯一控制結(jié)構(gòu))、函數(shù)式編程[意氣之爭]

  • 3.1.5.關(guān)系: 子歷程是沒有使用yield的協(xié)程。Donald Ervin Knuth(wiki)/Donald Ervin Knuth(baidu): 子歷程是協(xié)程的一種特例

  • 3.2.沒有異常處理的簡化版co函數(shù)

function co(gen){
    let def = Promise.defer();
    let iter = gen();

    function resolve(data) {
        // 恢復(fù)迭代器并帶入promise的終值
        step(iter.next(data));
    }

    function step(it) {
        it.done ?
            // 迭代結(jié)束則解決co返回的promise
            def.resolve(it.value) :
            // 否則繼續(xù)用解決程序解決下一個(gè)讓步出來的promise
            it.value.then(resolve);
    }

    resolve();
    return def.promise;
}
  • 3.3.使用co, yield后面放的必須是thunk/promise函數(shù)

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.同時(shí)觸發(fā):

// 寫法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);

// 寫法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;

最后的一些問題與思考

1.從異步操作上,async是最后演化的結(jié)果,callback是就不用了、還是應(yīng)該盡量避免?

參考資料

擴(kuò)展閱讀

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 弄懂js異步 講異步之前,我們必須掌握一個(gè)基礎(chǔ)知識(shí)-event-loop。 我們知道JavaScript的一大特點(diǎn)...
    DCbryant閱讀 2,875評(píng)論 0 5
  • Promise 對(duì)象 Promise 的含義 Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函...
    neromous閱讀 8,825評(píng)論 1 56
  • 原文地址:http://es6.ruanyifeng.com/#docs/promise Promise 的含義 ...
    AI云棧閱讀 974評(píng)論 0 7
  • 根據(jù)筆者的項(xiàng)目經(jīng)驗(yàn),本文講解了從函數(shù)回調(diào),到 es7 規(guī)范的異常處理方式。異常處理的優(yōu)雅性隨著規(guī)范的進(jìn)步越來越高,...
    黃子毅閱讀 8,680評(píng)論 7 37
  • 一、Javascript實(shí)現(xiàn)異步編程的過程以及原理 1、為什么要用Javascript異步編程 眾所周知,Java...
    Ebony_7c03閱讀 932評(píng)論 0 2

友情鏈接更多精彩內(nèi)容