手寫實現(xiàn)Promise

# 手寫實現(xiàn)Promise

## Promise異步編程

  異步編程簡介:無論在瀏覽器環(huán)境還是node環(huán)境都需要JavaScript的異步編程,如在瀏覽器環(huán)境中的定時器、事件、ajax等或是node環(huán)境中的文件讀取、事件等。伴隨著異步編程就有回調機制,異步編程免不了回調。

異步編程問題:產(chǎn)生回調地獄,難于維護和擴展。

       try、catch只能捕捉同步代碼中出現(xiàn)的異常。

       同步并發(fā)的異步操作存在一定的問題。

解決方案: ES6 Promise可以解決回調地獄,以及同步并發(fā)的異步問題。

  jQuery的Callbacks和Lodash的after都是解決回調問題的其他方法

### Promise使用

```js

//excutor function 同步執(zhí)行

let promise = new Promise((resolve,reject)=>{

? ? //異步操作

? ? setTimeout(()=>{

? ? ? ? Math.random()*100 > 60 ? resolve("ok"): reject("fail");

? ? },1000);

});

//Promise內(nèi)部狀態(tài),pending(等待),onFulFilled(成功),onReject(失敗)

//注冊回調,異步執(zhí)行

//宏任務(setTimeout)? ? 微任務

//微任務有優(yōu)先執(zhí)行權

//then 可以鏈式操作

//上一個then不拋出錯誤的話,下一個then會執(zhí)行成功函數(shù)

//返回值作為下一個then注冊函數(shù)的執(zhí)行參數(shù)

//如果返回值為Promise對象,則下一個then的執(zhí)行取決于該對象的執(zhí)行函數(shù)

promise.then((val)=>{//微任務

? ? console.log(val);

? ? return new Promise((resolve,reject)=>{

? ? ? ? reject("newPromise:fail");

? ? });

},(reason)=>{

? ? console.log(reason);

? ? return "fail then1:param";

}).then((val)=>{

? ? console.log("ok then2:",val);

},(reason)=>{

? ? console.log("fail then2:",reason);

});

```

#### then 注冊回調返回值

#### catch 異常捕獲

```js

let promise = new Promise((resolve,reject)=>{

? ? throw new Error("test error");

});

//失敗函數(shù)捕獲

promise.then(null,(reason)=>{

? ? console.log(reason);

});

//鏈式調用時如果有空then,則相當于不存在可忽視

//catch捕獲

//catch后面可以繼續(xù)鏈式調用

promise.then().catch((error)=>{

? ? console.log(error);

? ? }).then((val)=>{

? ? ? ? console.log(val,"after catch: ok");

? ? },(reason)=>{

? ? ? ? console.log(reason,"after ctach: fail");

? ? })

```

#### finally 最后處理函數(shù)

#### Promise.all 同步并發(fā)異步的結果

```js

let oPro = new Promise(()=>{});

//Promise.all參數(shù)為數(shù)組,數(shù)組元素必須為Promise對象,其會將

//多個Promise實例包裝成一個新的Promise實例。

//全部成功時數(shù)組內(nèi)元素的返回值組成數(shù)組,只要有失敗時返回最先被reject失敗

//狀態(tài)的值

Promise.all([oPro,oPro,oPro]).then((val)=>{

? ? console.log(val);//val為數(shù)組

});

```

#### Promise.race 誰先成功處理誰

```js

let oPro = new Promise(()=>{});

//Promise.race([p1,p2,p3]);里面的哪個結果獲得的快,就返回那個結果,

//不管結果本身成功或失敗。誰的狀態(tài)先發(fā)生改變就返回誰的狀態(tài)

Promise.race([oPro,oPro,oPro]).then((val)=>{

? ? console.log(val);

},(reason)=>{

? ? console.log(reason);

});

```

### Promise模擬實現(xiàn)

點擊查看 [Promise規(guī)范][promise-standard]

```js

//考慮兼容性,用ES5實現(xiàn)

function MyPromise(excutor) {

? ? this.status = "pending";

? ? this.resolveValue = null;

? ? this.rejectReason = null;

? ? this.resolveCallbackList = [];

? ? this.rejectCallbackList = [];

? ? try {

? ? ? ? excutor(this.resolve.bind(this), this.reject.bind(this));

? ? } catch (e) {

? ? ? ? this.reject(e);

? ? }

}

MyPromise.prototype = {

? ? resolve(val) {

? ? ? ? if (this.status === "pending") {

? ? ? ? ? ? this.status = "FulFilled";

? ? ? ? ? ? this.resolveValue = val;

? ? ? ? ? ? this.resolveCallbackList.forEach(function (cbFn) {

? ? ? ? ? ? ? ? cbFn();

? ? ? ? ? ? });

? ? ? ? }

? ? },

? ? reject(reason) {

? ? ? ? if (this.status === "pending") {

? ? ? ? ? ? this.status = "Rejected";

? ? ? ? ? ? this.rejectReason = reason;

? ? ? ? ? ? this.rejectCallbackList.forEach(function (cbFn) {

? ? ? ? ? ? ? ? cbFn();

? ? ? ? ? ? });

? ? ? ? }

? ? },

? ? then(onFulFilled, onRejected) {

? ? ? ? var self = this;

? ? ? ? self._dealNullThen(onFulFilled)._dealNullThen(onRejected);

? ? ? ? return new MyPromise(function (resolve, reject) {

? ? ? ? ? ? if (self.status === "FulFilled") {

? ? ? ? ? ? ? ? //模擬異步執(zhí)行,此為宏任務,底層代碼為微任務

? ? ? ? ? ? ? ? setTimeout(function () {

? ? ? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? ? ? var nextResolveValue = onFulFilled(self.resolveValue);

? ? ? ? ? ? ? ? ? ? ? ? self._dealReturnValPromse(nextResolveValue, resolve, reject);

? ? ? ? ? ? ? ? ? ? } catch (e) {

? ? ? ? ? ? ? ? ? ? ? ? reject(e);

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }, 0);

? ? ? ? ? ? } else if (self.status === "Rejected") {

? ? ? ? ? ? ? ? setTimeout(function () {

? ? ? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? ? ? var nextRejectValue = onRejected(self.rejectReason);

? ? ? ? ? ? ? ? ? ? ? ? self._dealReturnValPromse(nextRejectValue, resolve, reject, true);

? ? ? ? ? ? ? ? ? ? } catch (e) {

? ? ? ? ? ? ? ? ? ? ? ? reject(e);

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }, 0);

? ? ? ? ? ? } else if (self.status === "pending") {

? ? ? ? ? ? ? ? self.resolveCallbackList.push(function () {

? ? ? ? ? ? ? ? ? ? setTimeout(function () {

? ? ? ? ? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? ? ? ? ? var nextResolveValue = onFulFilled(self.resolveValue);

? ? ? ? ? ? ? ? ? ? ? ? ? ? self._dealReturnValPromse(nextResolveValue, resolve, reject);

? ? ? ? ? ? ? ? ? ? ? ? } catch (e) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? reject(e);

? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? }, 0);

? ? ? ? ? ? ? ? });

? ? ? ? ? ? ? ? self.rejectCallbackList.push(function () {

? ? ? ? ? ? ? ? ? ? setTimeout(function () {

? ? ? ? ? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? ? ? ? ? var nextRejectValue = onRejected(self.rejectReason);

? ? ? ? ? ? ? ? ? ? ? ? ? ? self._dealReturnValPromse(nextRejectValue, resolve, reject, true);

? ? ? ? ? ? ? ? ? ? ? ? } catch (e) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? reject(e);

? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? }, 0);

? ? ? ? ? ? ? ? });

? ? ? ? ? ? }

? ? ? ? });

? ? },

? ? _dealNullThen(fn) { //處理空then情況

? ? ? ? if (!fn) {

? ? ? ? ? ? fn = function (val) {

? ? ? ? ? ? ? ? return val;

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? return this;

? ? },

? ? _dealReturnValPromse(returnVal, resolve, reject, isRejected) {

? ? ? ? if (returnVal instanceof MyPromise) {

? ? ? ? ? ? //若返回值為MyPromise對象,則后面的執(zhí)行狀態(tài)由該對象來決定

? ? ? ? ? ? returnVal.then(function (val) {

? ? ? ? ? ? ? ? resolve(val);

? ? ? ? ? ? }, function (reason) {

? ? ? ? ? ? ? ? reject(reason);

? ? ? ? ? ? });

? ? ? ? } else {

? ? ? ? ? ? //如果返回值不為MyPromise對象,則執(zhí)行回調函數(shù)

? ? ? ? ? ? if (!isRejected) {

? ? ? ? ? ? ? ? resolve(returnVal);

? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? reject(returnVal);

? ? ? ? ? ? }

? ? ? ? }

? ? },

};

MyPromise.race = function (promiseArr) {

? ? return new MyPromise(function (resolve, reject) {

? ? ? ? promiseArr.forEach(function (ele) {

? ? ? ? ? ? ele.then(resolve, reject);

? ? ? ? });

? ? });

};

//全部成功才執(zhí)行成功回調,只要有一個失敗就執(zhí)行失敗回調

MyPromise.all = function (promiseArr) {

? ? return new MyPromise(function (resolve, reject) {

? ? ? ? var returnValueArr = [],

? ? ? ? ? ? count = 0;

? ? ? ? for (var i = 0, len = promiseArr.length; i < len; i++) {

? ? ? ? ? ? (function (i) {

? ? ? ? ? ? ? ? promiseArr[i].then(function (val) {

? ? ? ? ? ? ? ? ? ? returnValueArr[i] = val;

? ? ? ? ? ? ? ? ? ? if (++count == len) {

? ? ? ? ? ? ? ? ? ? ? ? resolve(returnValueArr);

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }, function (reason) {

? ? ? ? ? ? ? ? ? ? reject(reason);

? ? ? ? ? ? ? ? });

? ? ? ? ? ? }(i));

? ? ? ? }

? ? });

};

```

## ES6 Symbol

數(shù)據(jù)結構:第七種數(shù)據(jù)結構Symbol

特點:唯一,可作為對象的屬性,有靜態(tài)屬性Symbol.iterator

## ES6 Iterator

&emsp;&emsp;**迭代器目的**:標準化迭代操作。

&emsp;&emsp;**迭代模式**:提供一種方法可以順序獲得聚合對象中的各個元素,是一種最簡單也是最常見的設計模式。它可以讓用戶透過特定的接口巡訪集合中的每一個元素而不用了解底層的實現(xiàn)。

&emsp;&emsp;**迭代器簡介**:依照與迭代模式的思想而實現(xiàn),分內(nèi)部迭代器和外部迭代器。

&emsp;&emsp;&emsp;&emsp;**內(nèi)部迭代器**:本身是函數(shù),該函數(shù)內(nèi)部定義好迭代規(guī)則,完全接受整個迭代過程,外部只需要一次初始調用。

&emsp;&emsp;&emsp;&emsp;Array.prototype.forEach、jQuery.each內(nèi)部迭代器

&emsp;&emsp;&emsp;&emsp;**外部迭代器**:本身是函數(shù),執(zhí)行返回迭代對象,迭代下一個元素必須顯式調用,調用復雜度增加,但靈活性增強。

&emsp;&emsp;&emsp;&emsp;function outerIterator(){}外部迭代器

```js

//模擬寫自己外部迭代器

function OuterIterator(o){

? ? let curIndex=0;

? ? let next=()=>{

? ? ? ? return {

? ? ? ? ? ? value:o[curIndex],

? ? ? ? ? ? done:o.length == ++curIndex,

? ? ? ? }

? ? }

? ? return {

? ? ? ? next,

? ? }

}

let arr=[1,2,3];

let oIt=outerIterator(arr);

oIt.next();

oIt.next();

oIt.next();

```

### 部署Iterator

```js

let obj={

? ? 0:"a",

? ? 1:"b",

? ? 2:"c",

? ? length:3,

? ? //要能迭代,必須部署Iterator,符合ES6

? ? [Symbol.iterator]:function (){

? ? ? ? let curIndex=0;

? ? ? ? let next = () => {

? ? ? ? ? ? return {

? ? ? ? ? ? ? ? value: this[curIndex],

? ? ? ? ? ? ? ? done: this.length == curIndex++,

? ? ? ? ? ? }

? ? ? ? };

? ? ? ? return{

? ? ? ? ? ? next,

? ? ? ? }

? ? },

}

console.log([...obj]);

```

### Generator

Generator簡介:生成器,本身為函數(shù),執(zhí)行后返回迭代對象,函數(shù)內(nèi)部要配合yield使用,Generator函數(shù)分段執(zhí)行,遇到y(tǒng)ield即暫停。

特點:

&emsp;&emsp;function和函數(shù)名之間需要帶*

&emsp;&emsp;函數(shù)體內(nèi)yield表達式,產(chǎn)出不同的內(nèi)部狀態(tài)(值)

```js

//示例 Generator產(chǎn)生迭代對象

function *test(){

? ? let val1= yield "a";

? ? console.log(val1);//val1的值為第二次next中傳入的值

? ? yield "b";

? ? yield "c";

? ? return "d";

}

let oG=test();

oG.next();//{value:"a",done:false}

oG.next();//{value:"b",done:false}

oG.next();//{value:"c",done:false}

oG.next();//{value:"d",done:true}

```

改造前面的代碼

```js

let obj={

? ? 0:"a",

? ? 1:"b",

? ? 2:"c",

? ? length:3,

? ? //要能迭代,必須部署Iterator,符合ES6

? ? [Symbol.iterator]:function (){

? ? ? ? let curIndex=0;

? ? ? while(curIndex != this.length){

? ? ? ? ? yield this[curIndex++];

? ? ? };

? ? },

}

console.log([...obj]);

```

Generator函數(shù)使用

```js

function *read(path){

? ? let val1 = yield readFile(path);

? ? let val2 = yield readFile(val1);

? ? let val3 = yield readFile(val2);

? ? return val3;

}

let oG = read();

let {value, done} = oG.next();

value.then((val)=>{

? ? let {value, done} = oG.next();

? ? value.then((val)=>{

? ? ? ? let {value, done} = oG.next();

? ? ? ? value.then((val)=>{

? ? ? ? ? ? console.log(val);

? ? ? ? });

? ? });

});

//遞歸優(yōu)化

function Co(oIterator){

? ? return new Promise((res,rej)=>{

? ? ? ? let next = (data)=>{

? ? ? ? ? ? let {value, done} = oIterator.next(data);

? ? ? ? ? ? if(done){

? ? ? ? ? ? ? ? res(value);

? ? ? ? ? ? }else{

? ? ? ? ? ? ? ? value.then((val)=>{

? ? ? ? ? ? ? ? ? ? next(val);

? ? ? ? ? ? ? ? },rej);

? ? ? ? ? ? }

? ? ? ? };

? ? ? ? next();

? ? });

}

//使用

Co(read()).then((val)=>{

? ? console.log(val);

});

```

#### Promise化

```js

let fs = require("fs");

let path="./data.txt";

let format="utf-8";

//原始函數(shù)

function readFile(){

? ? return new Promise((res,rej)=>{

? ? ? ? fs.readFile(path,format,(err,data)=>{

? ? ? ? ? ? ? ? if(err){

? ? ? ? ? ? ? ? ? ? rej(err);

? ? ? ? ? ? ? ? }else{

? ? ? ? ? ? ? ? ? ? res(data);

? ? ? ? ? ? ? ? }

? ? ? ? ? ? });

? ? });

}

//對函數(shù)進行promise化? (npm i bluebird)

function promisify(fn){

? ? return (...arg)=>{

? ? ? ? return new Promise((res,rej)=>{

? ? ? ? ? ? fn(...arg,(err,data)=>{

? ? ? ? ? ? ? ? if(err){

? ? ? ? ? ? ? ? ? ? rej(err);

? ? ? ? ? ? ? ? }else{

? ? ? ? ? ? ? ? ? ? res(data);

? ? ? ? ? ? ? ? }

? ? ? ? ? ? });

? ? ? ? });

? ? };

}

let readFilePromisify=promisify(fs.readFile);

readFilePromisify(path,format).then((val)=>{

? ? console.log(val);

});

//進一步對對象內(nèi)異步方法進行promise化

function promisifyAll(){

? ? for(let key in obj){

? ? ? ? let fn=obj[key];

? ? ? ? if(typeof fn === "function"){

? ? ? ? ? ? obj[key + "Async"] = promisify(fn);

? ? ? ? }

? ? }

}

promisifyAll(fs);

fs.readFileAsync(path,format).then((val)=>{

? ? console.log(val);

});

```

### async & await

async簡介:async函數(shù),是Generator語法糖,通過babel編譯后可以看出它就是Generator+Promise+Co(遞歸)思想實現(xiàn)的,配合await使用。

目的:優(yōu)雅的解決異步操作問題。

```js

//解決回調地獄

//try catch

//同步并發(fā)的異步結果

async function read(path){

? ? try{

? ? ? ? let val1 = await readFile(path);

? ? ? ? let val2 = await readFile(val1);

? ? ? ? let val3 = await readFile(val2);

? ? }catch(e){

? ? ? ? console.log(e);//能夠捕獲異常

? ? }

? ? return val3;

}

read(path).then((val)=>{

? ? console.log(val);

});

//解決同步并發(fā)的異步問題

//Promise.all有局限性,一個異常其他也不能出結果

Promise.all([readFile(path1),readFile(path2),readFile(path3)])

.then((val)=>{

? ? console.log(val);

},(reason)=>{

? ? console.log(reason);

});

//使用async和await可以解決

```

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。