Promise的好處
Promsie 可以解決的問題
- 把你從回調地獄中解救出來
- 讓你優雅的捕獲錯誤
- 爲你分擔異步併發的難題
// 此處使用node舉例,不會不要緊,先混個臉熟。再見就不陌生了呀
let fs = require('fs');
// 異步讀取文件
fs.readFile('./name', function (err, data){
if(err){}
fs.readFile(data, function (err, address){
if(err){}
fs.readFile(address, function (err, product){
// 1)深陷在回調地域中不能抽身
if(err){
// 2)捕獲錯誤。OMG,我哪裏錯了?!?!告訴我,肯定改~
}
});
});
});
// 3) 你的名字、你的地址。都告訴我,驚喜纔會送到你面前呀~
fs.readFile('./name', function (err, data){});
fs.readFile('./address', function (err, data){});
Promise 使用的例子
狀態變化
- Promise 有3種狀態
- pending 等待態
- fulfilled 成功態
- rejected 失敗態
【等待態 -> 成功態】 or 【等待態 -> 失敗態】二選一,你來定。
- Promsie 是個類,接收一個函數參數 executor 執行器,一上來就執行了。這裏是同步的。
- 每一個Promsie的實例上都有一個then方法。是基於回調實現的。
console.log('一封情書');
let p = new Promise((resolve, reject)=>{
console.log('executor請說出你的選擇:');
resolve('你中意我~(*^▽^*)');
reject('你發了好人卡(╥﹏╥)o');
});
p.then((value)=>{
console.log('成功態', value);
}, (reason) => {
console.log('失敗態', reason);
});
console.log('紙短情長');
鏈式調用
console.log('-----一封情書-----');
let p = new Promise((resolve, reject) => {
console.log('executor 請說出你的選擇:');
resolve('你中意我~(*^▽^*)');
reject('你發了好人卡(╥﹏╥)o');
});
p.then((value) => {
console.log('成功態---', value);
}, (reason) => {
console.log('失敗態---', reason);
}).then((value) => {
console.log('---愛你一萬年~(*^▽^*)');
}, (reason) => {
console.log('---傷心總是難免的o(╥﹏╥)o');
});
console.log('~~~紙短情長~~~');
異步請求
- name.txt
zhaoxiajingjing
- 3.js
let fs = require('fs');
let p = new Promise((resolve, reject)=>{
fs.readFile('./name.txt', 'utf8', function (err, data){
if(err){
return reject(err);
}
resolve(data);
});
});
p.then((value)=>{
console.log('成功了', value);
}, (reason)=>{
console.log('失敗了', reason);
});
複雜的使用 Promise
下面的例子輸出什麼呢?
let fs = require('fs');
function read(filePath) {
return new Promise((resolve, reject) => {
fs.readFile(filePath, 'utf8',function(err, data){
if(err) {
return reject(err);
}
resolve(data);
});
});
}
read('./name.txt')
// then-1
.then(function (data) {
console.log('data①', data);
return new Promise((resolve, reject) => {
reject('錯誤了');
});
})
// then-2
.then((data) => {
console.log('data②', data);
}, err => {
console.log('err②', err);
})
// then-3
.then((data) => {
console.log('data③', data);
}, (err) => {
console.log('err③', err);
});
OK,提煉一下重點:
- 有三個狀態。怎麼變化的?
- executor。怎麼執行的?
- then方法,成功態和失敗態的回調。
- then方法的鏈式調用。
- Promise 處理異步。
實現以上內容
Promise 基本實現
- new Promise 會傳一個函數作爲參數。這個函數有兩個參數:resolve 成功 reject 失敗,都是用於改變狀態的。都在實例上,一個 Promise 一生只改變一次狀態
- 每個實例上都有一個 then 方法,異步的。對於成功態和失敗態的回調:onFulfilled onRejected
const PENDING = 'pending';
const SUCCESS = 'fulfilled';
const FAIL = 'rejected';
class Promise{
constructor(execuotr){
const status = PENDING; // 我等着你給答案~
this.value;
this.reason;
let resolve = (value)=>{
if(status === PENDING) {
this.status = SUCCESS; // 你中意我~
this.value = value;
}
};
let reject = (reason)=>{
if(status === PENDING) {
this.status = FAIL; // 你發了好人卡
this.reason = reason;
}
};
// 是同步的哦~
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
then(onFulfilled, onRejected){
if(this.status === SUCCESS) {
onFulfilled(this.value); // 愛你一萬年~
}
if(this.status === FAIL) {
onRejected(this.reason); // 傷心總是難免的
}
}
}
Pomise 解決異步問題
Promise 是個容器,裏面可以放一些異步的請求,請求成功了走成功態,請求失敗了走失敗態。當然,你要反過來走也可以噠~
const PENDING = 'pending';
const SUCCESS = 'fulfilled';
const FAIL = 'rejected';
class Promise {
constructor(executor){
this.status = PENDING;
this.value;
this.reason;
// 用來存儲 訂閱的內容的
this.onSuccessCallbacks = [];
this.onFailCallbacks = [];
let resolve = (value)=>{
if(this.status === PENDING) {
this.status = SUCCESS;
this.value = value;
this.onSuccessCallbacks.forEach(fn => fn());
}
};
let reject = (reason)=>{
if(this.status === PENDING) {
this.status = FAIL;
this.reason = reason;
this.onFailCallbacks.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
then(onFulfilled, onRejected){
if(this.status === SUCCESS){
onFulfilled(this.value);
}
if(this.status === FAIL){
onRejected(this.reason);
}
// 當Promise裏面有異步請求控制狀態改變時,會先走到then方法裏面
if(this.status === PENDING) {
this.onSuccessCallbacks.push(()=>{
onFulfilled(this.value);
});
this.onFailCallbacks.push(()=>{
onRejected(this.reason);
});
}
}
}
Promise 裏面有異步請求時候,會先走到 then方法裏面了。此時,需要把成功態回調和失敗態回調先存儲起來,等到異步請求回來以後變更了狀態,再觸發執行。
Promise 的鏈式
then 方法 返回一個新的 Promise
Promise 一生只能改變一次狀態。那麼,Promise 的鏈式調用then方法,說明每次都會返回一個新的Promise。
const SUCCESS = 'fulfilled';
const FAIL = 'rejected';
const PENDING = 'pending';
class Promise {
constructor(executor) {
// ... executor 代碼
}
then(onFulfilled, onRejected) {
let promise2;
promise2 = new Promise((resolve, reject)=>{
if (this.status === SUCCESS) {
try { // 用 try catch 捕獲同步的報錯
// 成功態的回調的返回值 x
// 【問題】 如果 x 是一個promsie,那麼需要取得x執行後的結果
let x = onFulfilled(this.value);
resolve(x);
} catch (e) {
reject(e);
}
}
if (this.status === FAIL) {
try {
let x = onRejected(this.reason);
resolve(x);
} catch (e) {
reject(e);
}
}
if (this.status === PENDING) {
this.onSuccessCallbacks.push(()=>{
try {
let x = onFulfilled(this.value);
resolve(x);
} catch (e) {
reject(e);
}
});
this.onFailCallbacks.push(()=>{
try {
let x = onRejected(this.reason);
resolve(x);
} catch (e) {
reject(e);
}
});
}
});
return promise2;
}
}
解析 x
如何判斷 x 是Promsie,還是一個普通值?【參考規範 https://promisesaplus.com "Promsie A+" 2.2.7】
const SUCCESS = 'fulfilled';
const FAIL = 'rejected';
const PENDING = 'pending';
function resolvePromise(promise2, x, resolve, reject) {
// 死循環了,容錯
if(promise2 === x) {
return reject('TypeError: Chaining cycle detected for promise~~~~');
}
// 判斷 x 類型
if(typeof x === 'function' || (typeof x === 'object' && x != null)) {
// 這個纔有可能是 promsie
try {
let then = x.then;
if(typeof then === 'function') {
// 此時,認爲就是個promise
// 如果promsie是成功的,那麼結果向下傳遞,如果失敗了就傳到下一個失敗裏面去
then.call(x, y=>{
resolvePromise(promise2, y, resolve, reject);
}, r => {
reject(r);
});
} else {
resolve(x);
}
} catch (e) {
reject(e);
}
} else {
// x 肯定不是一個promsie
resolve(x);
}
}
class Promise {
constructor(executor) {
// ... executor 代碼
}
then(onFulfilled, onRejected) {
let promise2;
promise2 = new Promise((resolve, reject) => {
if (this.status === SUCCESS) {
// 用定時器模擬此時promise2已經能獲取到了
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
// 其他情況同理,先以一個爲例說明
});
return promise2;
}
}
嚴謹度
Promsie 給你的承諾,一句情話:一個 Promise 一生只改變一次狀態
const SUCCESS = 'fulfilled';
const FAIL = 'rejected';
const PENDING = 'pending';
function resolvePromise(promise2, x, resolve, reject) {
// 死循環了,容錯
if(promise2 === x) {
return reject('TypeError: Chaining cycle detected for promise~~~~');
}
let called;
if(typeof x === 'function' || (typeof x === 'object' && x != null)) {
try {
let then = x.then;
if(typeof then === 'function') {
then.call(x, y=>{
if(called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
}, r => {
if(called) return;
called = true;
reject(r);
});
} else {
resolve(x);
}
} catch (e) {
if(called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
class Promise {
constructor(executor) {
// .... executor 的代碼
}
then(onFulfilled, onRejected) {
let promise2;
promise2 = new Promise((resolve, reject) => {
if (this.status === SUCCESS) {
// 用定時器模擬此時promise2已經能獲取到了
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
// 其他情況同理,先以一個爲例說明
});
return promsie2;
}
}
值的穿透
let p = new Promise((resolve, reject)=>{
resolve(1000);
});
p.then().then().then(data => {
console.log(data);
});
值可以傳過去
const SUCCESS = 'fulfilled';
const FAIL = 'rejected';
const PENDING = 'pending';
function resolvePromise(promise2, x, resolve, reject) {
// ... 判斷 x 的值
}
class Promise {
constructor(executor) {
// ... executor 代碼
}
then(onFulfilled, onRejected) {
// 值穿透
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err};
// ... promsie2 的判斷
}
}
測試
- 測試這個庫是否符合我們的promise A+ 規範
-
promises-aplus-tests
用來測試當前的庫是否符合規範 npm i promises-aplus-tests -g
promises-aplus-tests 文件名
Promise A+ 規範
Promsie 是一個構造函數,是個類。默認高版本瀏覽器,node都自帶了。不用考慮兼容性,放心大膽的使用吧!如果真不兼容,那就用es6-promsie包自己是一套吧~
Promsie A+
容錯
上面的內容,還需要一部分容錯。就是當executor 裏面的有一個promsie的時候,執行的結果。
let Promise = require('./promise.js');
let p = new Promise((resolve, reject)=>{
resolve(new Promise((resolve, reject)=>{
reject(404);
}));
});
p.then(value => console.log(1, value), reason => console.log(2, reason));
// 輸出:
// 2 404
在同步執行時,resolve 的value是一個Promsie,那麼需要等它的結果。
const SUCCESS = 'fulfilled';
const FAIL = 'rejected';
const PENDING = 'pending';
function resolvePromise(promise2, x, resolve, reject) {
// ... 校驗x
}
class Promise {
constructor(executor) {
// ... code
let resolve = (value) => {
if (value instanceof Promise) {
return value.then(resolve, reject);
}
if (this.status === PENDING) {
this.status = SUCCESS;
this.value = value;
this.onSuccessCallbacks.forEach(fn => fn());
}
};
// ... code
}
then(onFulfilled, onRejected) {
// ... then 方法
}
}
module.exports = Promise;
交流
在這裏,一起成長~