關(guān)于本文
Tips: 本文參考標(biāo)準(zhǔn)規(guī)范為 Promises/A+
[鏈接:https://promisesaplus.com/]
Promise 來源
起源:Promise最初被提出是在 E語言中拣凹, 它是
基于并列/并行處理
設(shè)計(jì)的一種編程語言涧偷。核心:解決
回調(diào)地獄
的問題-
原JS異步處理方式盯滚,存在如下問題:
非Promise實(shí)現(xiàn)異步處理方式悴了,如下:
let fs = require('fs'); fs.readFile('./2.promise/1.txt','utf8',function(err,data){ fs.readFile(data,'utf8',function(err,data){ console.log(data); }); });
總結(jié)存在問題(雖然都能解決,但是可拓展性太差):
try/catch問題
- 無法在同一時(shí)刻
合并兩次異步的結(jié)果
,異步方案不支持return
-
Promise的處理方式
- 理念:Promise是把類似的異步處理對象和處理規(guī)則進(jìn)行規(guī)范化唐础, 并按照采用統(tǒng)一的接口來編寫勾栗,而采取規(guī)定方法之外的寫法都會出錯(cuò)狰晚。
var promise = getAsyncPromise("fileA.txt"); promise.then(function(result){ // 獲取文件內(nèi)容成功時(shí)的處理 ... }).catch(function(error){ // 獲取文件內(nèi)容失敗時(shí)的處理 ... });
Promise 狀態(tài)流
用new Promise 實(shí)例化的promise對象有以下三個(gè)狀態(tài)。
Pending
(es6, has-resolution)
狼纬,
既不是resolve也不是reject的狀態(tài)羹呵。 promise對象剛被創(chuàng)建后的初始化狀態(tài)
等resolved
(es6, has-rejection)
,
resolve(成功)時(shí)疗琉,此時(shí)會調(diào)用 onFulfilled
rejected
(es6冈欢,unresolved)
,
reject(失敗)時(shí)盈简。此時(shí)會調(diào)用 onRejected
Promise API
三個(gè)類型凑耻,內(nèi)含部分源碼實(shí)現(xiàn)
- Constructor
- 狀態(tài)機(jī)
status
- 處理函數(shù)
reject, resolve
- 執(zhí)行函數(shù)
executor
- 狀態(tài)機(jī)
- Instance Method
-
Resolve callback
(onFulfilled會被調(diào)用)
function resolve(value) { // 成功狀態(tài) if (self.status === 'pending') { self.status = 'resolved'; self.value = value; // 執(zhí)行成功的回調(diào) self.onResolvedCallbacks.forEach(function (fn) { fn(); }); } }
-
Reject callback
(onRejected 會被調(diào)用)
function reject(reason) { if (self.status === 'pending') { self.status = 'rejected'; self.reason = reason; // 執(zhí)行是失敗的回調(diào) self.onRejectedCallbacks.forEach(function (fn) { fn(); }); } }
- promise.then 成功和失敗時(shí)都可以使用。 另外在只想對異常進(jìn)行處理時(shí)可以采用
promise.then(undefined, onRejected)
這種方式柠贤,只指定reject時(shí)的回調(diào)函數(shù)即可香浩。 - 不過這種情況下
promise.catch(onRejected)
應(yīng)該是個(gè)更好的選擇。
- promise.then 成功和失敗時(shí)都可以使用。 另外在只想對異常進(jìn)行處理時(shí)可以采用
-
Static Method
(靜態(tài)方法)
-
Promise.all
原理:hold住所有的隊(duì)列中的實(shí)例臼勉,等待所有實(shí)例執(zhí)行完畢后邻吭,執(zhí)行resolve 一起返回
Promise.all = function(promises) { //promises是一個(gè)promise的數(shù)組 return new Promise(function (resolve, reject) { //arr是最終返回值的結(jié)果 let 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.catch
原理:利用的是promise實(shí)例的鏈?zhǔn)秸{(diào)用中,如果return 一個(gè)error flag(例如: null, throw new Error等)宴霸,則直接走到 Reject callback 中
// 捕獲錯(cuò)誤的方法 Promise.prototype.catch = function (callback) { return this.then(null, callback) }
-
Promise.race
釋義:只要有一個(gè)promise成功了 就算成功囱晴。如果第一個(gè)失敗了就失敗了
Promise.race = function (promises) { return new Promise(function (resolve, reject) { for (var i = 0; i < promises.length; i++) { promises[i].then(resolve,reject) } }) }
-
Promise.resolve
釋義:生成一個(gè)成功的promise
Promise.resolve = function(value){ return new Promise(function(resolve,reject){ resolve(value); }) }
-
Promise.reject
釋義:生成一個(gè)失敗的promise
Promise.reject = function(reason){ return new Promise(function(resolve,reject){ reject(reason); }) }
-
-
Promise 核心方法實(shí)現(xiàn)
- 核心特性
-
多次then的實(shí)現(xiàn)
- 原理:promise實(shí)例可以多次then膏蚓,當(dāng)成功后會將then中的成功方法按順序執(zhí)行,我們可以先
將then中的成功的回調(diào)和失敗的回調(diào)存到數(shù)組內(nèi)畸写,當(dāng)成功時(shí)調(diào)用成功的數(shù)組即可
- 原理:promise實(shí)例可以多次then膏蚓,當(dāng)成功后會將then中的成功方法按順序執(zhí)行,我們可以先
-
鏈?zhǔn)秸{(diào)用
- 原理:promise實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用靠的是
返回一個(gè)新的promise
- 原理:promise實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用靠的是
-
兼容所有返回結(jié)果
-
有值就進(jìn)入下一個(gè)的then中
如果then中無論是成功的回調(diào)還是失敗的回調(diào)只要返回了結(jié)果就會走下一個(gè)then中的成功驮瞧,如果有錯(cuò)誤走下一個(gè)then的失敗
-
普通值
,例如string類型
如果第一個(gè)promise返回一個(gè)普通值剧董,
會進(jìn)到下一次then的成功的回調(diào)
-
Error 值
,例如throw new Error()
如果第一個(gè)promise返回一個(gè)Error值破停,
會進(jìn)到下一次then的失敗的回調(diào)
-
promise
如果第一個(gè)promise返回了一個(gè)promise翅楼,需要
等待返回的promise執(zhí)行后的結(jié)果傳遞給下一次then中
-
-
resolvePromise 處理重復(fù)返回promise的方法
-
校驗(yàn)循環(huán)引用
resolvePromise 返回的結(jié)果和promise是同一個(gè)那么永遠(yuǎn)不會成功和失敗,導(dǎo)致循環(huán)引用
// 預(yù)防循環(huán)引用 if (promise2 === x) { //這里應(yīng)該報(bào)一個(gè)類型錯(cuò)誤真慢,有問題 return reject(new TypeError('循環(huán)引用')) }
-
-
* `校驗(yàn)value是不是一個(gè)promise`
如果x是對象毅臊,并且x的then方法是函數(shù),我們就認(rèn)為他是一個(gè)promise
```
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
....
}
```
* `校驗(yàn)promise 是否ok`
有些人寫的promise可能會既調(diào)用成功 又調(diào)用失敗黑界,如果兩個(gè)都調(diào)用先調(diào)用誰另一個(gè)就忽略掉
* promise中`值的穿透`
* 原理:校驗(yàn)方法是不是又返回值管嬉,如果沒有,則`內(nèi)置回調(diào)方法朗鸠,默認(rèn)把值往下傳遞`
```
//成功和失敗默認(rèn)不穿給一個(gè)函數(shù)
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
return value;
}
onRjected = typeof onRjected === 'function' ? onRjected : function (err) {
throw err;
}
```
* promise規(guī)范中要求蚯撩,`所有的onFufiled和onRjected都需要異步執(zhí)行,setTimeout`
```
setTimeout(function () {
try {
let x = onFulfilled(self.value);
// x可能是別人promise,寫一個(gè)方法統(tǒng)一處理
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
```
`如下開始展示源碼實(shí)現(xiàn)`
-
構(gòu)造函數(shù) Promise 的實(shí)現(xiàn)
function Promise(executor) { // executor是一個(gè)執(zhí)行函數(shù) let self = this; self.status = 'pending'; // 狀態(tài)機(jī)烛占,默認(rèn)值為pending胎挎,初始化的值 self.value = undefined; // 默認(rèn)成功的值 self.reason = undefined; // 默認(rèn)失敗的原因 self.onResolvedCallbacks = []; // 存放then成功的回調(diào) self.onRejectedCallbacks = []; // 存放then失敗的回調(diào) // 成功時(shí)的處理函數(shù) function resolve(value) { // 成功狀態(tài) if (self.status === 'pending') { self.status = 'resolved'; self.value = value; self.onResolvedCallbacks.forEach(function (fn) { fn(); }); } } // 失敗時(shí)的處理函數(shù) 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) { // 捕獲的時(shí)候發(fā)生異常,就直接失敗了 reject(e); } }
-
then方法實(shí)現(xiàn)
- 步驟1處理了值穿透的問題,case如下:
promise.then().then().then(function() { ... });
結(jié)構(gòu)代碼:
Promise.prototype.then = function (onFulfilled, onRjected) { // 步驟1: // 成功和失敗默認(rèn)不穿給一個(gè)函數(shù) onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) { return value; } onRjected = typeof onRjected === 'function' ? onRjected : function (err) { throw err; } let self = this; let promise2; // 步驟2: // 成功的處理 if (self.status === 'resolved') { promise2 = new Promise(function (resolve, reject) { // 當(dāng)成功或者失敗執(zhí)行時(shí)有異常那么返回的promise應(yīng)該處于失敗狀態(tài) // x可能是一個(gè)promise 也有可能是一個(gè)普通的值 setTimeout(function () { try { let x = onFulfilled(self.value); // x可能是別人promise忆家,寫一個(gè)方法統(tǒng)一處理 resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }) }) } // 步驟3: // 錯(cuò)誤的處理 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); } }) }) } // 步驟4: // 等待異步執(zhí)行的過程當(dāng)犹菇,即調(diào)用then時(shí)可能沒成功 也沒失敗 // 此時(shí)還需要同步的回調(diào)函數(shù)push到隊(duì)列中,方便后面的回調(diào)使用 if (self.status === 'pending') { promise2 = new Promise(function (resolve, reject) { // 此時(shí)沒有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; }
- 步驟1處理了值穿透的問題,case如下:
-
處理函數(shù)芽卿,resolvePromise方法實(shí)現(xiàn)
function resolvePromise(promise2, x, resolve, reject) { // 校驗(yàn):循環(huán)引用 if (promise2 === x) { return reject(new TypeError('循環(huán)引用了')) } // 校驗(yàn):表示是否調(diào)用過成功或者失敗 let called; // 校驗(yàn):看x是不是一個(gè)promise揭芍,promise應(yīng)該是一個(gè)對象 if (x !== null && (typeof x === 'object' || typeof x === 'function')) { // 可能是promise {},看這個(gè)對象中是否有then方法,如果有then我就認(rèn)為他是promise了 try { let then = x.then; if (typeof then === 'function') { // 成功 then.call(x, function (y) { if (called) return called = true; // y可能還是一個(gè)promise卸例,在去解析直到返回的是一個(gè)普通值 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 { // 說明是一個(gè)普通值1 resolve(x); } }
Promise 語法糖
原理:語法糖負(fù)責(zé)幫你簡化了邏輯称杨,代碼結(jié)構(gòu)如下
Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise(function (resolve, reject) {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd
}
類似Q.js用法如圖:
let fs = require('fs');
let Q = require('q');
function readFile(url) {
let defer = Q.defer();
require('fs').readFile(url, 'utf8', function (err, data) {
if (err) defer.reject(err);
defer.resolve(data);
});
return defer.promise
}