JavaScript 和 node 都是單線程非阻塞的運行方式,因此會出現一個異步回調的問題。promise就是爲了解決這類問題 前端常見的異步操做有:前端
setTimeout setInterval ajax
node
如今基本都使用async和awaitios
最初解決異步方式是經過callback回調去解決ajax
例1:npm
let callback = () => {
console.log('callback1');
}
setTimeout(callback, 1000)
console.log('begin');
// 打印順序
// begin
// callback1
複製代碼
例1是最典型的經過callback去解決異步問題, 可是有兩個問題數組
1.無止境的回調,不利於代碼維護以下,在ajax請求數據時尤爲常見promise
2.好幾個請求,很難同時獲得數據(能夠經過代碼作到,比較煩)bash
爲了解決上面兩個問題出現了promise,用法以下異步
// Promise 是一個類經過new獲得它的實例
// new Promise(executor) 傳入的函數就是executor
let promise = new Promise((resolve, reject) => {
// 傳入一個方法,這個方法裏面放的就是須要執行的異步程序
//這個方法有兩個參數 resolve表示成功 reject表示失敗 都是函數
// 若是調用resolve會走then的第一個方法,即成功方法
// 若是調用reject會走then的第二個方法,即失敗方法 ,若是有報錯也會走失敗方法
setTimeout(() => {
console.log('2')
resolve('resolveData')
// reject('err')
})
console.log('1')
});
// 在Promise的原型上有一個then方法,會在primiose的executor執行後根據不一樣狀況執行
promise.then((data) => {
console.log(data)
return data
}, (err) => {})
// 打印順序 1 => 2 => 'resolveData'
// then方法在Promise內部是返回一個新的Promise實例,因此能夠實現鏈式調用,而且將return出去的的值做爲下一個then相應方法的參數
promise.then((data) => {
console.log(data + '2')
return data
}, (err) => {}).then((data) => {
console.log(data)
}, () => {}) // 'resolveData' => 'resolveData2'
// 若是第一個then進入reject方法,可是在裏面並無報錯,那麼仍是會進入下一個resolve方法,一樣return出去的值也是第二個then相應方法的參數
複製代碼
promise 經常使用方法async
//catch 集體捕獲錯誤
promise.then(() => {}).then(() => {}).catch(() => {})
// 不須要再每個then裏面都加上失敗函數,能夠選擇在最後加上catch,若是失敗或報錯直接走到這
// Promise.all
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve({a: 1});
})
})
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve({b: 2});
})
})
let promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve({c: 2});
})
})
Promise.all([promise, promise1, promise2]).then((data) => {
console.log('data', data); // data [ { a: 1 }, { b: 2 }, { c: 2 } ]
}, (err) => {
console.log('err', err);
})
// 多個promise執行resolve後再走then第一個方法,有一個報錯或執行reject直接走then第二個方法
// Promise.race 只要有一個promise成功了 就算成功。若是第一個失敗了就失敗了
Promise.race([promise, promise1, promise2]).then((data) => {
console.log('data', data); // data { a: 1 }
}, (err) => {
console.log('err', err);
})
// Promise.resolve Promise.reject 分別返回一個成功 失敗的promise
複製代碼
Q庫是一個promise的庫,裏面有all defer等方法,最後被node收購了,如今的promise也有all方法可是沒有defer,很簡單後面咱們能夠手寫一個
generator 函數須要用*來作表示,函數內部配合yield去暫停而且返回一個對象 { value: 1, done: false }
value的值是yield後面跟着的數值,
done的值是一個布爾類型,表示函數有沒有執行完畢,false表示尚未執行完,能夠繼續經過it.next()調用;
function *mygenerator() {
let m = yield 1
console.log(m, '1'); // 22 '1'
let n = yield 2
console.log(n, '2'); // 33 '2'
}
let it = mygenerator(); // generator返回一個it值裏面有一個next方法能夠調用
console.log(it.next(11)); // { value: 1, done: false }
console.log(it.next(22)); // { value: 2, done: false }
console.log(it.next(33)); // { value: undefined, done: true }
複製代碼
co庫 npm install co 能夠自動的將generator進行迭代
let co = require('co');
let co = require('co');
function *mygenerator() {
let m = yield (() => { return {a: 1}})()
console.log(m, '1'); // { a: 1 } '1'
let n = yield (() => {return {b: 2}})()
console.log(n, '2'); // { b: 2 } '2'
return {m ,n}
}
co(mygenerator()).then((data) => {
console.log('data', data) // data { m: { a: 1 }, n: { b: 2 } }
})
// 在使用co庫時yield後面只能跟 function, promise, generator, array, or object 不然會報錯
複製代碼
async + await 就是 generator和co的語法糖 用法簡單
async function r() {
try{
let m = await (() => { return {a: 1}})();
let n = await (() => { return {a: 2}})();
return {m, n};
}catch(e){ // 若是出錯會catch
console.log('err',e)
}
}
r().then((data) => {
console.log('data', data);
})
複製代碼
function Promise(executor) { // executor是一個執行函數
let self = this;
self.status = 'pending';
self.value = undefined; // 默認成功的值
self.reason = undefined; // 默認失敗的緣由
self.onResolvedCallbacks = []; // 存放then成功的回調
self.onRejectedCallbacks = []; // 存放then失敗的回調
function resolve(value) { // 成功狀態
if (self.status === 'pending') {
self.status = 'resolved';
self.value = value;
self.onResolvedCallbacks.forEach(function (fn) {
fn();
});
}
}
function reject(reason) { // 失敗狀態
if (self.status === 'pending') {
self.status = 'rejected';
self.reason = reason;
self.onRejectedCallbacks.forEach(function (fn) {
fn();
})
}
}
try {
executor(resolve, reject)
} catch (e) { // 捕獲的時候發生異常,就直接失敗了
reject(e);
}
}
function resolvePromise(promise2, x, resolve, reject) {
// 有可能這裏返回的x是別人的promise
// 儘量容許其餘亂寫
if (promise2 === x) { //這裏應該報一個類型錯誤,有問題
return reject(new TypeError('循環引用了'))
}
// 看x是否是一個promise,promise應該是一個對象
let called; // 表示是否調用過成功或者失敗
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 多是promise {},看這個對象中是否有then方法,若是有then我就認爲他是promise了
try { // {then:1}
let then = x.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 {
resolve(x)
}
} catch (e) {
if (called) return
called = true;
reject(e);
}
} else { // 說明是一個普通值1
resolve(x); // 表示成功了
}
}
Promise.prototype.then = function (onFulfilled, onRjected) {
//成功和失敗默認不穿給一個函數
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
return value;
}
onRjected = typeof onRjected === 'function' ? onRjected : function (err) {
throw err;
}
let self = this;
let promise2; //返回的promise
if (self.status === 'resolved') {
promise2 = new Promise(function (resolve, reject) {
// 當成功或者失敗執行時有異常那麼返回的promise應該處於失敗狀態
// x多是一個promise 也有多是一個普通的值
setTimeout(function () {
try {
let x = onFulfilled(self.value);
// x多是別人promise,寫一個方法統一處理
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
})
}
if (self.status === 'rejected') {
promise2 = new Promise(function (resolve, reject) {
setTimeout(function () {
try {
let x = onRjected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
})
}
// 當調用then時可能沒成功 也沒失敗
if (self.status === 'pending') {
promise2 = new Promise(function (resolve, reject) {
// 此時沒有resolve 也沒有reject
self.onResolvedCallbacks.push(function () {
setTimeout(function () {
try {
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
})
});
self.onRejectedCallbacks.push(function () {
setTimeout(function () {
try {
let x = onRjected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
});
})
}
return promise2;
}
// 捕獲錯誤的方法
Promise.prototype.catch = function (callback) {
return this.then(null, callback)
}
// 解析所有方法
// let arr = [];
// arr[1] = 100;
// console.log(arr.length)
Promise.all = function (promises) {
//promises是一個promise的數組
return new Promise(function (resolve, reject) {
let arr = []; //arr是最終返回值的結果
let i = 0; // 表示成功了多少次
function processData(index, y) {
arr[index] = y;
if (++i === promises.length) {
resolve(arr);
}
}
for (let i = 0; i < promises.length; i++) {
promises[i].then(function (y) {
processData(i, y)
}, reject)
}
})
}
// 只要有一個promise成功了 就算成功。若是第一個失敗了就失敗了
Promise.race = function (promises) {
return new Promise(function (resolve, reject) {
for (var i = 0; i < promises.length; i++) {
promises[i].then(resolve,reject)
}
})
}
// 生成一個成功的promise
Promise.resolve = function(value){
return new Promise(function(resolve,reject){
resolve(value);
})
}
// 生成一個失敗的promise
Promise.reject = function(reason){
return new Promise(function(resolve,reject){
reject(reason);
})
}
Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise(function (resolve, reject) {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd
}
// mjs
module.exports = Promise;
複製代碼
有問題歡迎指正