JS早期實現異步都是採用回調方式callback來實現異步任務的。包括事件回調,setTimeout,setInterval, Ajax等,採起這樣的方式寫代碼,功能實現沒有問題,可是不夠優雅清晰,容易出現回調地獄。jquery
new Promise(function(resovle, reject){
})
複製代碼
從上代碼能夠看出構造函數接受一個函數。github
const PENDING = "pending"; //等待
const FULFILLED = "fulfilled"; //已完成
const REJECTED = "rejected"; // 已拒絕
function Promise(executor) {
let self = this;
self.status = PENDING;
self.value;
self.reason;
function resolve(value) {
if (self.status === PENDING) {
self.status = FULFILLED;
self.value = value;
}
}
function reject(reason) {
if (self.status === PENDING) {
self.status = REJECTED;
self.reason = reason;
}
}
try { // 規範提到,執行器拋異常會reject
executor(resolve, reject);
} catch(e) {
reject(e)
}
}
複製代碼
Promise.prototype.then = function (onFulfilled, onRjected) {
let self = this;
/** * onFulfilled 和 onRejected 都是可選參數。 * 若是 onFulfilled 不是函數,其必須被忽略 * 若是 onRejected 不是函數,其必須被忽略 */
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(value) {
return value;
};
onRjected = typeof onRjected === 'function' ? onRjected : function(reason) {
throw reason;
}
if (self.status === FULFILLED) {
onFulfilled(self.value);
}
if (self.status === REJECTED) {
onRjected(self.reason);
}
}
複製代碼
promise實例能夠屢次then,當成功後會將then中的成功方法按順序執行,能夠先將then中的成功的回調和失敗的回調存到數組內,當成功時調用成功或失敗的數組。npm
// 添加then的回調隊列
self.onResolvedCallbacks = [];
self.onRejectedCallbacks = [];
複製代碼
// 成功時候調用隊列裏的handler
self.onResolvedCallbacks.forEach(function (fn, index, array) {
fn()
});
複製代碼
// 失敗時候調用隊列裏的handler
self.onRejectedCallbacks.forEach(function (fn, index, array) {
fn()
})
複製代碼
當鏈式調用then()時候,jquery能實現鏈式調用靠的就是返回this,可是promise不能返回this,promise實現鏈式調用靠的是返回一個新的promise。 並且then()方法能夠什麼都不穿,實現穿透調用下一個then()...數組
Promise.prototype.then = function (onFulfilled, onRjected) {
let self = this;
let promise2 = null; //返回的promise
// 咱們的代碼能夠在then中什麼都不傳 promise中值的穿透
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
return value;
};
onRjected = typeof onRjected === 'function' ? onRjected : function (reason) {
throw reason;
}
if (self.status === FULFILLED) {
promise2 = new Promise(function (onFulfilled2, onRjected2) {
let result = onFulfilled(self.value);
onFulfilled2(result);
});
}
if (self.status === REJECTED) {
promise2 = new Promise(function (onFulfilled2, onRjected2) {
let reason = onRjected(self.reason);
onRjected2(reason);
});
}
if (self.status === PENDING) {
promise2 = new Promise(function (resolve, reject) {
self.onResolvedCallbacks.push(function () {
let value = onFulfilled(self.value);
resolve(value);
});
self.onRejectedCallbacks.push(function () {
let reason = onRjected(self.reason);
reject(reason);
})
});
}
return promise2;
}
複製代碼
若是then中不管是成功的回調仍是失敗的回調,只要返回告終果(return xxx)就會走下一個then中的成功,若是有錯誤(throw new Error)就走下一個then的失敗。promise
若是第一個promise返回一個普通值,會進到下一次then的成功的回調,若是第一個promise返回了一個promise,須要等待返回的promise執行後的結果傳遞給下一次then中。併發
function resolvePromise(promise2, x, resolve, reject) {
// 有可能這裏返回的x是別人的promise
// 儘量容許其餘亂寫
// 返回的結果和promise是同一個那麼永遠不會成功和失敗
if (promise2 === x) {
throw new Error('不能反悔本身')
}
let called;
// 判斷Promise類型
if (typeof x !== 'object' && (typeof x === 'object' || typeof x === 'function')) {
try {
let then = x.then;
// promise 能夠是一個函數。函數上有一個then方法。
if (typeof then === 'function') {
then.call(x, function (y) {
if (called) return;
called = true;
// y可能仍是一個promise,在去解析直到返回的是一個普通值
resolvePromise(promise2, y, resolve, reject);
}, function (err) {
if (called) return;
called = true;
reject(err);
});
} else {
// 不是函數,又是一個promise
resolve(x)
}
} catch (err) {
if (called) return
called = true;
reject(err);
}
} else {
//通常類型返回
//3.若是then中不管是成功的回調仍是失敗的回調只要返回告終果就會走下一個then中的成功,
resolve(x);
}
}
複製代碼
Promise.prototype.then = function (onFulfilled, onRjected) {
let self = this;
let promise2 = null;
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
return value;
};
onRjected = typeof onRjected === 'function' ? onRjected : function (reason) {
throw reason;
}
if (self.status === FULFILLED) {
promise2 = new Promise(function (onFulfilled2, onRjected2) {
setTimeout(function () { //9.promise規範中要求,全部的onFufiled和onRjected都須要異步執行,setTimeout
try {
let result = onFulfilled(self.value);
resolvePromise(promise2, result, onFulfilled, onRjected);
} catch (err) {
onRjected2(err);//若是有錯誤走下一個then的失敗
}
});
});
}
if (self.status === REJECTED) {
promise2 = new Promise(function (onFulfilled2, onRjected2) {
setTimeout(function () { //9.promise規範中要求,全部的onFufiled和onRjected都須要異步執行,setTimeout
try {
let reason = onRjected(self.reason);
resolvePromise(promise2, reason, onFulfilled, onRjected);
} catch (err) {
onRjected2(err);//若是有錯誤走下一個then的失敗
}
});
});
}
if (self.status === PENDING) {
promise2 = new Promise(function (resolve, reject) {
self.onResolvedCallbacks.push(function () {
setTimeout(function () { //9.promise規範中要求,全部的onFufiled和onRjected都須要異步執行,setTimeout
try {
let value = onFulfilled(self.value);
resolvePromise(promise2, value, resolve, reject);
} catch (err) {
reject(err); //若是有錯誤走下一個then的失敗
}
});
});
self.onRejectedCallbacks.push(function () {
setTimeout(function () { //9.promise規範中要求,全部的onFufiled和onRjected都須要異步執行,setTimeout
try {
let reason = onRjected(self.reason);
resolvePromise(promise2, reason, resolve, reject);
} catch (err) {
reject(err);//若是有錯誤走下一個then的失敗
}
});
})
});
}
return promise2;
}
複製代碼
實現catch, all, race, resolve, reject, defer這些事例方法。框架
// 捕獲錯誤的方法
// 至關於不要then的第一個成功回調
Promise.prototype.catch = function (callback) {
return this.then(null, callback)
};
Promise.prototype.all = function (promises) {
return new Promise(function (resolve, reject) {
let returns = [];
let index = 0;
function processData(i, data) {
returns[i] = data;
if (++index === promises.length) {
resolve(returns)
}
}
for (let i = 0; i <= promises.length; i++) {
promises[i].then(function (data) {
processData(i, data)
}, reject);
}
})
};
Promise.prototype.race = function (promises) {
return new Promise(function (resolve, reject) {
promises.forEach(function (value) {
value.then(resolve, reject);
})
})
};
Promise.resolve = function (value) {
return new Promise(function (resolve, reject) {
resolve(value)
});
};
Promise.reject = function (reason) {
return new Promise(function (resolve, reject) {
reject(reason)
})
};
Promise.defer = Promise.deferred = function () {
let dfd = {};
return new Promise(function (resolve, reject) {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
};
複製代碼
Genernator函數要用* 來比標識,yield(暫停 產出),它會將函數分割出好多個部分,調用一次next就會繼續向下執行,返回結果是一個迭代器,迭代器有一個next方法,yield後面跟着的是value的值,yield等號前面的是咱們當前調用next傳進來的值,第一次next傳值是無效的。異步
function* read() {
console.log('init');
let a = yield '第一波湯圓';
console.log(a);
let b = yield '第二波湯圓'
console.log(b);
return b;
}
let it = read();
console.log(it.next('給我湯圓'));
// init
// {value:'第一波湯圓',done:false}
console.log(it.next('我還要湯圓'));
// 我還要湯圓
// {value:'第二波湯圓',done:false}
console.log(it.next('夠了不要了'));
// 夠了不要了
// {value:‘第二波湯圓’,done:true}
console.log(it.next('還有麼?'));
// {value:undefined,done:true}
複製代碼
能夠看到,當調用read()時候,程序僅僅返回一個迭代器。函數內代碼並無運行。async
提一次調用next('給我湯圓'),後函數運行了,以下內容:
console.log('init');
yield '第一波湯圓';
// 因此答應出來
// init
// 而且返回{value:'第一波湯圓',done:false}
// 注意,第一次傳入的'給我湯圓' 函數並無接收處理。
複製代碼
第二次調用console.log(it.next('我還要湯圓')),程序運行了以下內容:
let a = '我還要湯圓';
console.log(a);
yield '第二波湯圓'
// 我還要湯圓
// {value:'第二波湯圓',done:false}
// 將傳入參宿賦值給a,並打印出來,而後返回yield後的'第二波湯圓'
複製代碼
第三次調用console.log(it.next('夠了不要了')),程序運行了以下內容:
let b = '夠了不要了'
console.log(b);
return b;
// 夠了不要了
// {value:‘夠了不要了’, done:true}
// 將傳入參宿賦值給b,並打印出來,而後返回b的'夠了不要了',這是迭代結束,返回的done就變爲true。
複製代碼
繼續調用console.log(it.next('還有麼?')),程序沒有迭代內容了,因此直接返回
// {value:undefined, done:true}
複製代碼
let bluebird = require('bluebird'); // bluebird見相關函數庫介紹
let fs = require('fs');
let read = bluebird.promisify(fs.readFile);
function* gRead() {
let content1 = yield read('./1.txt', 'utf8');
// content1 存放的是content2的文件地址
let content2 = yield read(content1, 'utf8');
return content2;
}
// 接下來我要一次一次的調用next才能獲得我要的content2的內容,麻煩
複製代碼
因而與co庫
// co庫 npm install co 能夠自動的將generator進行迭代
let co = require('co');
co(gRead()).then(function (data) {
console.log(data)
})
// 這樣就能夠一次拿到結果
複製代碼
但是co庫是在麼實現的呢?讓咱們來模擬一個co庫
function co(it) {
// 必須返回一個promise
return new Promise(function (resolve, reject) {
function next(d) { // 第二次遞歸進來就有值了。
let { value, done } = it.next(d);
// value 是一個promise,第一次調用傳遞值無用。
if (!done) {
// 若是沒有遍歷結束
value.then(function (data) {
// content2的promise運行,並傳入讀取結果。
next(data)
}, reject)
} else {
resolve(value);
}
}
next();
});
}
複製代碼
能夠看出co庫就是遞歸調動了迭代器裏的每個next(),直到done===true。
async-await能夠理解爲co + generator的語法糖。用async 來修飾函數,aysnc須要配await,await只能promise。
async function aRead(){
try{
let content1 = await read('./1.txt','utf8');
let content2 = await read(content1,'utf8');
return content2;
}catch(e){ // 若是出錯會catch
console.log('err',e)
}
}
複製代碼
函數調用和promise同樣,以下:
// async函數返回的是promise,
aRead().then(function (data) {
console.log('print', data);
}, function (err) {
console.log('error', err);
});
複製代碼
/ blueBird
// npm install bluebird
let fs = require('fs');
let bluebird = require('bluebird');
function promisify(fn) { // promise化 將回調函數在內部進行處理
return function (...args) {
return new Promise(function (resolve, reject) {
fn(...args, function (err, data) {
if (err) reject(err);
resolve(data);
})
})
}
}
function promisifyAll(obj) {
Object.keys(obj).forEach(key => { // es5將對象轉化成數組的方法
if (typeof obj[key] === 'function') {
obj[key + 'Async'] = promisify(obj[key])
}
})
}
promisifyAll(fs); // 將全部的方法所有增長一個promise化
fs.readFileAsync('./1.txt', 'utf8').then(function (data) {
console.log(data);
});
複製代碼