Promise 是一種非同步流程的控制手段
- Promise 只有一個引數 excutor(執行器),預設 new 時候就會呼叫執行器
- promise 可以解決非同步問題,then 方法是非同步的,執行器 excutor 是同步的。
console.log(0);
new Promise((resolve, reject) => {
console.log(1);
resolve(4);
}).then(data => {
console.log(data);
})
console.log(2);
// 0 1 2 4
複製程式碼
-
三個狀態
- pending 等待
- resole 成功
- reject 失敗
- 只有狀態是等待的時候,才可以改變狀態,如果 promsie 成功了就不能失敗,相反失敗了也不能成功
- 解決回撥地獄
promise 支援鏈式呼叫,鏈式呼叫返回的並不是 this 而是一個新的 promise
舉個例子: 本次的結果是下次請求的資料。
回撥方式
let fs = require('fs');
fs.readFile('1.text', 'utf8', function(error, data) {
if(error) {
console.log('1.error', error);
}
console.log('1.data', data);
// 讀取2.text
fs.readFile(data, 'utf8', function(error, data) {
if(error) {
console.log('2.error', error);
}
console.log('2.data', data);
// .... 繼續巢狀
});
});
複製程式碼
promise 版本
let fs = require('fs');
function read(url) {
return new Promise((resolve, reject) => {
fs.readFile(url, 'utf8', function(error, data) {
if (error) {
reject(error);
}
resolve(data);
})
});
}
read('1.text').then((data) => {
console.log(data);
return read(data); // 在次 扔出一個新的 promise 例項
}, (error) => {
console.log(error);
}).then((data) => {
console.log('2.data', data);
}, (error) => {
console.log('2.error', error);
});
複製程式碼
- promise 可以支援多個併發請求
Promise.all() 併發處理,有一個失敗就失敗
Promise.all([read('1.text'), read('2.text')]).then(data => {
console.log(data); // [ '2.text', '我是根據1.text來的' ] 和請求一一對應
});
複製程式碼
Promise.race() 賽跑,只取回來最快的
Promise.race([read('1.text'), read('2.text')]).then(data => {
console.log(data); // 誰先回來用誰的
});
複製程式碼
- 語法糖
返回一個成功的 promise
Promise.resolve('123').then(data => {
console.log(data);
});
複製程式碼
返回一個失敗的 promise
Promise.reject('錯誤了').then(null, error => {
console.log(error);
})
複製程式碼
- 流程圖
自己實現 myPromise
- 如何驗證是否符合 Promise A+ 規範
<!--安裝測試包-->
npm install promises-aplus-tests -g
<!--測試檔案-->
promises-aplus-tests 檔名
複製程式碼
- 呼叫
let myPromise = require('./promise');
let p = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve('哈哈');
}, 1000);
});
// 返回值是 promise 將 promise 的返回值傳遞到下一個then中(2.then)
// 返回普通值 直接把值傳遞到下一個then (2.then)
// promise then鏈式呼叫 返回的是一個新的promise
p.then((data) => {
console.log('1.then')
console.log('data', data);
return new Promise((resolve, reject) => {
resolve(new Promise((resolve, reject) => {
resolve('我是普通值~');
}));
})
}).then((data) => {
console.log('2.then');
console.log('2.then:data', data);
}, (error) => {
console.log('2.then:error', error);
});
複製程式碼
- 內部實現
// promise then鏈式呼叫 返回的是一個新的promise
function resolvePromise(promise2, x, resolve, reject) {
// 此處的 resolve、reject 是 promise2 中的
// 判斷 x 值是個啥
if (promise2 === x) {
return reject(new TypeError('大哥自己等待自己,遊戲結束~'));
}
// x 是函式或者物件
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
let called; // 防止成功後在呼叫失敗
try {
let then = x.then; // 取 x 的 then 方法
if (typeof then === 'function') {
// 如果 then 是函式就認為它是 promise
then.call(x, y => {
if (called) {
return;
}
called = true;
// y 可能還是一個 Promsie
// 遞迴呼叫
resolvePromise(promise2, y, resolve, reject);
}, error => {
if (called) {
return;
}
called = true;
reject(error);
});
} else {
resolve(x);
}
} catch(e) {
if (called) {
return;
}
called = true;
reject(e);
}
} else { // x 是普通值
// 呼叫 promise2 中的 resolve 扔出x
resolve(x);
}
}
class Promise {
// 建構函式
constructor(executor) {
// 預設狀態
this.status = 'pending';
// 將執行器中 resolve(value)、rejecet(reason) 的值存一下
this.value = undefined;
this.reason = undefined;
// 儲存是要解決執行器裡面 非同步呼叫 resolve() 、 reject();
// 儲存成功回撥
this.onResolvedCallBacks = [];
// 儲存失敗回撥
this.onRejectedCallBacks = [];
// 呼叫 resolve 後不能再呼叫 reject
// 狀態只有在 pending 的時候允許改變
let resolve = (value) => {
if (this.status === 'pending') {
// 更新 status
this.status = 'resolved';
this.value = value;
// 讓所有成功的函式執行
this.onResolvedCallBacks.forEach(fn => fn());
}
}
let reject = (reason) => {
if (this.status === 'pending') {
// 更新 status
this.status = 'rejected';
this.reason = reason;
// 讓所有失敗的函式執行
this.onRejectedCallBacks.forEach(fn => fn());
}
}
// 將函式傳遞出去 並做異常判斷
try {
// 呼叫執行器
executor(resolve, reject);
} catch(e) {
reject(e);
}
}
// then 方法
then(onFulFilled, onRejected) {
// then 可以預設不穿引數,值的穿透
onFulFilled = typeof onFulFilled === 'function' ? onFulFilled : y => y;
onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error };
// promise then鏈式呼叫 返回的是一個新的 promise 例項
let promise2;
if (this.status === 'resolved') {
// Promise new 的時候執行器立馬執行
promise2 = new Promise((resolve, reject) => {
// 新增setTimeout then 方法是非同步呼叫
setTimeout(() => {
// excutor 執行的時候會有非同步的情況,需要給每個 then 方法新增try catch
try {
// 傳遞 value
// x 是第一個 then 呼叫後的返回值
let x = onFulFilled(this.value);
// x 是否為 promise 如果是 promise 取其結果傳遞給promise2
// x 是普通值 直接專遞給 promise2
// promise2 是鏈式呼叫中第2個 then 的 Promise 物件
resolvePromise(promise2, x, resolve, reject);
} catch(e) {
reject(e);
}
}, 0);
});
}
if (this.status === 'rejected') {
promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
// 傳遞失敗原因
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
// 當前既沒有完成,也沒有失敗
if (this.status === 'pending') {
promise2 = new Promise((resolve, reject) => {
this.onResolvedCallBacks.push(() => {
setTimeout(() => {
try {
let x = onFulFilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallBacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
});
}
return promise2;
}
// catch 方法
catch(onRejected) {
// 將當前例項的 then 扔出去
return this.then(null, onRejected);
}
}
// Promise 類上的方法
Promise.resolve = function (val) {
return new Promise((resolve, reject) => resolve(val));
}
Promise.reject = function (val) {
return new Promise((resolve, reject) => reject(val));
}
// 有一個失敗就失敗了。
Promise.all = function(promises) {
return new Promise((resolve, reject) => {
let arr = [];
let i = 0; // 監控是否全部成功。
function processData(index, data) {
arr[index] = data;
i++;
if (i === promises.length) {
resolve(arr);
}
}
for (let i = 0; i < promises.length; i++) {
promises[i].then(data => {
processData(i, data);
}, reject);
}
});
};
// 誰快就用誰的
Promise.race = function(promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(resolve, reject);
}
});
}
// promise 語法糖
// 外界呼叫可以不用巢狀了。
Promise.deferred = Promise.defer = function() {
let dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
module.exports = Promise;
複製程式碼