所謂"異步" 簡單說就是一個任務分紅兩段, 先執行第一段, 而後轉而執行其餘任務, 等作好了準備, 再回過頭來執行第二段.html
函數既能夠做爲參數,也能夠做爲返回值jquery
高階函數的英文名叫 Higher-Order Function,熟悉 React 的朋友應該知道高階組件 Higher-Order Component。沒錯,React 的高階組件本質上就是高階函數。編程
那麼,什麼是高階函數呢?promise
高階函數源自於函數式編程,是函數式編程的基本技術。異步
那麼,JS做爲一門「一切皆爲對象」的語言,是如何擁有函數式編程的能力呢?async
是由於在JS中函數是一等公民,即函數能夠被賦值給變量,被變量引用,這便使得函數能夠做爲參數,在其餘函數間相互傳遞:函數式編程
/** * 數值轉換 * @param {Number} val 要被處理的數值 * @param {Function} fn 處理輸入的val * @return {Number || String} */
const toConvert = function(val, fn) {
return fn(val);
};
const addUnitW = function(val) {
return val + 'W';
};
toConvert(123.1, Math.ceil); // 124
toConvert(123.1, addUnitW); // "123.1W"
複製代碼
// 判斷一個變量的類型
function isType(type) {
return function (param) {
return Object.prototype.toString.call(param) == `[object ${type}]`
}
}
let isString = isType('String')
let isArray = isType('Array')
console.log(isString('nan')); // true
console.log(isArray({})); // false
複製代碼
假設有這樣一個需求, 有個函數須要連續調用三次才能夠執行, 你能夠提早思考一下怎麼寫異步編程
function eat() {
console.log("吃完了");
}
function after(time, fn) {
let count = 0
return function () {
if(count++ === time) {
fn()
}
}
}
let newEat = after(3, eat)
newEat()
newEat()
newEat()
複製代碼
所謂回調函數, 就是把任務的第二段單獨寫在一個函數裏面, 等到從新執行這個任務的時候, 就直接調用這個函數函數
fs.readFile('某個文件', function(err, data) {
if(err) throw err
console.log(data)
})
複製代碼
回調函數的問題:佈局
// 利用發佈訂閱的模式, 讀取html模板和數據, 當二者都存在時輸出
const Event = require('events')
const fs = require('fs')
let eve = new Event()
let html = {}
eve.on('ready', function (key, value) {
html[key] = value
// 當兩個結果都有了
if(Object.keys(html).length == 2) {
console.log(html);
}
}) // 註冊
fs.readFile('./template.txt','utf8', function (err, template) {
eve.emit('ready', 'data', template)
})
fs.readFile('./data.txt', 'utf8', function (err, data) {
eve.emit('ready', 'data', data)
})
/********************************************/
// 以上這樣方式簡寫的形式能夠用一個"哨兵函數來監聽"
const fs = require('fs')
/* let html = {} // 哨兵函數 function done (key, value) { html[key] = value if(Object.keys(html).length == 2) { console.log(html); } } **/
// 給哨兵函數封裝一下
/**********************************************/
function reader (length, fn) {
let html = {}
return function (key, value) {
html[key] = value
if(Object.keys(html).length == length) {
fn(html)
}
}
}
let done = reader(2, function (html) {
console.log(html)
})
/**********************************************/
fs.readFile('./data.txt','utf8', function (err, template) {
done('template', template)
})
fs.readFile('./demo.txt', 'utf8', function (err, data) {
done('data', data)
})
複製代碼
生成器是一個函數, 能夠用來生成迭代器
Generator 函數是 ES6 提供的一種異步編程解決方案,整個 Generator 函數就是一個封裝的異步任務,或者說是異步任務的容器。異步操做須要暫停的地方,都用 yield 語句註明。
function* gen() {
let a = yield 111;
console.log(a);
let b = yield 222;
console.log(b);
let c = yield 333;
console.log(c);
let d = yield 444;
console.log(d);
}
let t = gen();
//next方法能夠帶一個參數,該參數就會被看成上一個yield表達式的返回值
t.next(1); //第一次調用next函數時,傳遞的參數無效
t.next(2); //a輸出2;
t.next(3); //b輸出3;
t.next(4); //c輸出4;
t.next(5); //d輸出5;
複製代碼
爲了讓你們更好的理解上面代碼是如何執行的,我畫了一張圖,分別對應每一次的next方法調用:
手寫promise
Promise.all的原理
function gen(times, fn) {
return function (i, data) {
if(i+1 == times) {
fn(data)
}
}
}
Promise.all = function (promises) {
return new Promise(function (resolve, reject) {
let done = gen(promises.length, resolve)
for (let i = 0; i < promises.length; i++) {
promises[i].then(function (data) {
done(i, data)
}, reject)
}
})
}
Promise.race的原理:
Promise.race = function (promises) {
return new Promise(function (resolve, reject) {
for (let i = 0; i < promises.length; i++) {
promises[i].then(resolve, reject)
}
})
}
複製代碼
q
(早期angular項目中用過) bluebird
這兩個庫
let fs = require('fs');
let bluebird = require('bluebird'); // bluebird中的一個方法, 可讓異步轉化爲promise
let read = bluebird.promisify(fs.readFile);
read('1.txt','utf-8').then(function(data){
console.log(data);
})
***********實現原理*************
function promisify(fn) {
return function (...args) {
return new Promise(function (resolve, reject) {
fn(...args, function (err, data) {
if (err) reject(err);
resolve(data);
})
})
}
}
複製代碼
function *read () {
console.log('開始');
let a = yield readFile('1.txt');
console.log(a)
let b = yield readFile('2.txt');
console.log(=b)
let c = yield readFile('3.txt');
console.log(c)
return c;
}
function co(it){
return new Promise((resolve,reject)=>{
function next(data){
let { value,done } = it.next(data)
if(!done){
value.then((data)=>{
next(data)
},reject)
}else{
resolve(value)
}
}
next()
})
}
複製代碼
// 完整代碼 也順便帶你們理順一下
function Promise(executor) {
let self = this;
self.value = undefined; // 成功的值
self.reason = undefined; // 失敗的值
self.status = 'pending'; // 目前promise的狀態pending
self.onResolvedCallbacks = []; // 可能new Promise的時候會存在異步操做,把成功和失敗的回調保存起來
self.onRejectedCallbacks = [];
function resolve(value) { // 把狀態更改成成功
if (self.status === 'pending') { // 只有在pending的狀態才能轉爲成功態
self.value = value;
self.status = 'resolved';
self.onResolvedCallbacks.forEach(fn => fn()); // 把new Promise時異步操做,存在的成功回調保存起來
}
}
function reject(reason) { // 把狀態更改成失敗
if (self.status === 'pending') { // 只有在pending的狀態才能轉爲失敗態
self.reason = reason;
self.status = 'rejected';
self.onRejectedCallbacks.forEach(fn => fn()); // 把new Promise時異步操做,存在的失敗回調保存起來
}
}
try {
// 在new Promise的時候,當即執行的函數,稱爲執行器
executor(resolve, reject);
} catch (e) { // 若是執行executor拋出錯誤,則會走失敗reject
reject(e);
}
}
// 這個函數爲核心,全部的promise都遵循這個規範
// 主要是處理then中返回的值x和promise2的關係
function resolvePromise(promise2,x,resolve,reject){
// 當promise2和then返回的值x爲同一個對象時,變成了本身等本身,會陷入死循環
if(promise2 === x){
return reject(new TypeError('Chaining cycle'));
}
let called;
// x多是一個promise也多是一個普通值
if(x!==null && (typeof x=== 'object' || typeof x === 'function')){
try{
let then = x.then;
if(typeof then === 'function'){
then.call(x,y=>{
if(called) return;
called = true;
resolvePromise(promise2,y,resolve,reject);
},err=>{
if(called) return;
called = true;
reject(err);
});
}else{
resolve(x);
}
}catch(e){
if(called) return;
called = true;
reject(e);
}
}else{
resolve(x);
}
}
// then調用的時候,都是屬於異步,是一個微任務
// 微任務會比宏任務先執行
// onFulfilled爲成功的回調,onRejected爲失敗的回調
Promise.prototype.then = function (onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function'?onFulfilled:val=>val;
onRejected = typeof onRejected === 'function'?onRejected: err=>{throw err}
let self = this;
let promise2;
// 上面講了,promise和jquery的區別,promise不能單純返回自身,
// 而是每次都是返回一個新的promise,才能夠實現鏈式調用,
// 由於同一個promise的pending resolve reject只能更改一次
promise2 = new Promise((resolve, reject) => {
if (self.status === 'resolved') {
// 爲何要加setTimeout?
// 首先是promiseA+規範要求的
// 其次是你們寫的代碼,有的是同步,有的是異步
// 因此爲了更加統一,就使用爲setTimeout變爲異步了,保持一致性
setTimeout(()=>{
try { // 上面executor雖然使用try catch捕捉錯誤
// 可是在異步中,不必定可以捕捉,因此在這裏
// 用try catch捕捉
let x = onFulfilled(self.value);
// 在then中,返回值多是一個promise,因此
// 須要resolvePromise對返回值進行判斷
resolvePromise(promise2,x,resolve,reject);
} catch (e) {
reject(e);
}
},0)
}
if (self.status === 'rejected') {
setTimeout(()=>{
try {
let x = onRejected(self.reason);
resolvePromise(promise2,x,resolve,reject);
} catch (e) {
reject(e);
}
},0)
}
if (self.status === 'pending') {
self.onResolvedCallbacks.push(() => {
setTimeout(()=>{
try {
let x = onFulfilled(self.value);
resolvePromise(promise2,x,resolve,reject);
} catch (e) {
reject(e);
}
},0)
});
self.onRejectedCallbacks.push(() => {
setTimeout(()=>{
try {
let x = onRejected(self.reason);
resolvePromise(promise2,x,resolve,reject);
} catch (e) {
reject(e);
}
},0)
});
}
});
return promise2
}
Promise.defer = Promise.deferred = function(){
let dfd = {};
dfd.promise = new Promise((resolve,reject)=>{
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
}
module.exports = Promise;
複製代碼
async await解決了異步問題:
可讓代碼像同步
可使用try catch
可使用promise
若是let r1 = await 後面等待的是promise,那麼會把promise的結果賦值給前面的r1,若是let r1 = await 後面等待的是普通值,那麼就會把這個普通值賦值給前面的r1
設爲 Flex 佈局之後,子元素的 float 、 clear 和 vertical-align 屬性將失效
async函數的實現, 就是將Generator函數和自動執行器,包裝到一個函數中
async function read() {
let template = await readFile('./template.txt');
let data = await readFile('data.txt');
return template + '+' + data
}
//等同於
function read () {
return co(function *() {
let template = yield readFile('template.txt');
let data = yield readFile('data.txt');
return template + '+' + data
})
}
複製代碼