国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

讓你從頭到尾把promise整的明明白白

silenceboy / 494人閱讀

摘要:它的作用是為實例添加狀態改變時的回調函數。里面有兩個回調函數,前者返回的回調函數,后者是可選值??梢钥闯墒堑膭e名專門用來指定錯誤發生時的回調函數。

最近一直私下在看Android項目,前端這一塊沒怎么仔細研究。昨天在寫重構公司前端項目的時候,我發現一旦有異步的任務,腦海里面條件反射一般的出現promise的字樣。重構的多了 心就就在納悶:既然promise這么好用,我能不能自己手寫一個promise呢?我思索了半天,按照自己的想法模擬了出來,但是和一位大佬交流的時候,他說我的寫法并沒有遵循Promise/A+規范。當時我心里是懵逼的,加班的時候就一直想寫這個問題。

一、 promise是什么? 1.1、 為什么要用promise?

我們都知道,在javascript里面所有的代碼都是單線程執行的。由于這種“單線程”的弊端,所有javascript重的網絡請求、瀏覽器事件等都只能用異步來執行。傳統的異步實現的方式都是用回調函數來實現的,例如:

request.onreadystatechange = function () {
    if (request.readyState === 4) {
        if (request.status === RESULT_OK) {
            return success(req.responseText);
        } 
        return fail(req.status);
    }
}

這樣雖然比較直觀,但是并不符合程序員的審美習慣,代碼的復用性也很低。按照現在主流的代碼風格來說,一份優秀的代碼模版就應該是鏈表形式的。

// Android寫習慣了,一般封裝網絡都是okhttp 。不用理我 = =
okhttp.request()
	  .onSuccesul()
	  .onFail();

于是promise應運而生。

1.2、 promise介紹

promise的中文釋意為:承諾。我覺得這個翻譯很能凸顯這個對象的含義。

promise里面有三個狀態:

pending:進行中

fulfilled: 已成功

rejected:已結束

為什么說,承諾這個解釋很能凸顯promise對象的特性呢?我們來為您談談promise里面的兩個特點:

1、 對象不能被任何手段來更改。怎么解釋呢?只要是你下了承諾,就不能受任何外界環境的干擾,只受到一步操作的結果來決定。 2、 一旦狀態改變,就不會再變,并且任何時候都可以得到這個結果。

從上面的特性里面,是不是更加加深了對承諾的理解呢!

二、 promise簡單探究。

前面說了這么多,都是傻把式,下面我們就用代碼體驗一把promise的快感吧 。

new Promise((resolve,reject)=>{
	let randomNumber = Math.random()
	console.log(randomNumber)
	if(randomNumber>0.5){
		resolve("success!!!")
	}else{
		reject("fail!!!")
	}
}).then(res=>{
	console.log(res);
},error=>{
	console.log(error);
})

從上面的代碼我們可以比較清楚的發現,Promise接收兩個參數:resolve和reject。resolve函數的作用是,將Promise對象的狀態從“進行中”變為“成功”( pending => resolved),在異步操作成功時調用,并將異步操作的結果,作為參數傳遞出去;reject函數的作用是,將Promise對象的狀態從“進行中”變為“失敗”( pending=>rejected),在異步操作失敗時調用,并將異步操作報出的錯誤,作為參數傳遞出去。

并且在Promise對象實例生成后,可以用then來分別指定完成后的resolve和reject。并且then里面的reject是可選項。

對于promise,我覺得主要有三個點需要給讀者講一下,來看下面一段代碼:

let promise =  new Promise((resolve,reject)=>{
	let randomNumber = Math.random()
	console.log(randomNumber)
	if(randomNumber>0.5){
		resolve("success!!!")
	}else{
		reject("fail!!!")
	}
})

我們發現多帶帶運行這一段代碼的話,console依然被打印出來了。這說明promise是創建的時候就被運行的。 再來看一段代碼:

let promise =  new Promise((resolve,reject)=>{
	let randomNumber = Math.random()
	if(randomNumber>0.5){
		resolve("success!!!")

	}else{
		reject("fail!!!")
	}
	console.log(randomNumber)
}).then(res=>{
	console.log(res);
},error=>{
	console.log(error)
})

效果如下:

由上面的現象我們可以輕易的看出來:resolve和reject后的代碼一般都還會運行,如果你想避免這種情況,你可以結合return來使用,例如:

return resolve("success!!!");
return reject("fail!!!");
三、 promise重要屬性探究 2.1、 then

Promise 實例具有then方法,也就是說,then方法是定義在原型對象Promise.prototype上的。它的作用是為 Promise 實例添加狀態改變時的回調函數。then里面有兩個回調函數,前者返回resolve的回調函數,后者是可選值。并且then返回返回一個新的promise,這樣就能一直使用鏈式結構了。

2.2、 catch

catch可以看成是then(null/undefined, reject)的別名 專門用來指定錯誤發生時的回調函數。那么為什么要設計這個屬性呢?主要有兩個方面,首先來看下面一段代碼:

// 普通then
.then(res=>{
	console.log(res);
},error=>{
	console.log(error)
})

//catch
.then(res=>{
	console.log(res);
})
.catch(error=>{
	console.log(error)
})

明顯第二個相對前一個會更加優雅一點。

我們再來看一段代碼:

//  普通then
.then(res=>{
	throw Error("have exception")
	console.log(res);
},error=>{
	console.log(error)
})
// catch
.then(res=>{
	throw Error("have exception")
}).catch(error=>{
	console.log(error)
}) 

運行上面的代碼我們很清楚的發現catch能捕獲到then的異常,但是then的reject回調里面并不能捕獲到resolve的異常。這在一定程度上保證了代碼的正常運行順序。

2.3、 finally

finally是es2018才引入的屬性,不管 Promise 對象最后狀態如何,都會執行的操作。其本質也是then方法的特性。

promise
.finally(() => {
  // ...
});

// 等同于
promise
.then(
  result => {
    // ...
    return result;
  },
  error => {
    // ...
    throw error;
  }
);
2.4、 all

Promise.all方法用于將多個 Promise 實例,包裝成一個新的 Promise 實例。其基礎的語法是:

const p = Promise.all([p1, p2, p3]);

Promise.all方法接受一個數組作為參數,p1、p2、p3都是 Promise 實例,如果不是,就會先調用下面講到的Promise.resolve方法,將參數轉為 Promise 實例,再進一步處理。 關于p的狀態,主要由接收的promise數組決定的 (1)只有p1、p2、p3的狀態都變成fulfilled,p的狀態才會變成fulfilled,此時p1、p2、p3的返回值組成一個數組,傳遞給p的回調函數。 (2)只要p1、p2、p3之中有一個被rejected,p的狀態就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。

這里值得我們注意的一點是,如果p1,p2,p3中有錯誤 并且有自己的catch方法的時候。會調用其自己的catch,當沒有catch方法的時候才會拋給p的catch來處理

2.5、 race

Promise.race方法同樣是將多個 Promise 實例,包裝成一個新的 Promise 實例。其基礎的語法是:

const p = Promise.race([p1, p2, p3]);

和all方法不同的是: (1)只有p1、p2、p3的狀態都變成rejected,p才會變成rejected (2) 如果p1、p2、p3中有一個狀態變成fulfilled,p的狀態就會變成fulfilled 并將首次變成fulfilled的返回值返回。

四、 初次手寫的promise

前面大概都將所謂的promise的用法給描述了一遍,大概寫一兩個例子就能感受出promise的奧秘。那么如果讓你自己來寫一個promise,你應該如何來寫呢?

這是我下班后寫的一個Promise:

const PENDING = "pending";
const RESOLVE = "resolve";
const REJECTED = "rejected";
function JPromise(fn){
	const that = this;
	that.state = PENDING;
	that.value = null;
	that.resolvedCallbacks = [];
	that.rejectedCallbacks = [];

	function resolve(value){
		if(that.state === PENDING){
			that.state = RESOLVE;
			that.value = value;
			that.resolvedCallbacks.map(cb=>cb(that.value));
		}
	}
	function reject(value){
		if(that.state === PENDING){
			that.state = REJECTED;
			that.value = value;
			that.rejectedCallbacks.map(cb=>cb(that.value))
		}
	}
	try{
		fn(resolve,reject)
	}catch(e){
		reject(e)
	}
}

JPromise.prototype.then = function(onFulfilled,onRejected){
	onFulfilled = typeof onFulfilled === "function"");"function"");if(this.state === PENDING){
		this.resolvedCallbacks.push(onFulfilled)
		this.rejectedCallbacks.push(onRejected)
	}
	if(this.state ===RESOLVE){
		onFulfilled(this.value);
	}
	if(this.state === REJECTED){
		onRejected(this.value)
	}
}

然后運行一下:

new JPromise((resolve,rejected)=>{
		resolve(1)
}).then(res=>{
	console.log(res);
},error=>{
  console.log(error);
})

然后自測了一下,完美能實現簡單的promise。但是跟大佬交流之后才知道:原來promise有自己的一套標準,我這套代碼雖然能夠簡單的實現promise的功能 但是沒有達到那套標準。

五、 符合規范的promise

首先關于promise規范,我就簡單的提一下吧:

每個then方法都返回一個新的Promise對象(原理的核心)

如果then方法中顯示地返回了一個Promise對象就以此對象為準,返回它的結果

如果then方法中返回的是一個普通值(如Number、String等)就使用此值包裝成一個新的Promise對象返回。

如果then方法中沒有return語句,就視為返回一個用Undefined包裝的Promise對象

若then方法中出現異常,則調用失敗態方法(reject)跳轉到下一個then的onRejected

如果then方法沒有傳入任何回調,則繼續向下傳遞(值的傳遞特性)。

最后張貼一下 我改進的代碼:

const PENDING =  "pending";//初始態
const FULFILLED =  "fulfilled";//初始態
const REJECTED =  "rejected";//初始態
function Promise(executor){
  let self = this;//先緩存當前promise實例
  self.status = PENDING;//設置狀態
  //定義存放成功的回調的數組
  self.onResolvedCallbacks = [];
  //定義存放失敗回調的數組
  self.onRejectedCallbacks = [];
  //當調用此方法的時候,如果promise狀態為pending,的話可以轉成成功態,如果已經是成功態或者失敗態了,則什么都不做
  function resolve(value){ //2.1.1
    if(value!=null &&value.then&&typeof value.then == "function"){
      return value.then(resolve,reject);
    }
    //如果是初始態,則轉成成功態
    //為什么要把它用setTimeout包起來
    setTimeout(function(){
      if(self.status == PENDING){
        self.status = FULFILLED;
        self.value = value;//成功后會得到一個值,這個值不能改
        //調用所有成功的回調
        self.onResolvedCallbacks.forEach(cb=>cb(self.value));
      }
    })

  }
  function reject(reason){ //2.1.2
    setTimeout(function(){
      //如果是初始態,則轉成失敗態
      if(self.status == PENDING){
        self.status = REJECTED;
        self.value = reason;//失敗的原因給了value
        self.onRejectedCallbacks.forEach(cb=>cb(self.value));
      }
    });

  }
  try{
    //因為此函數執行可能會異常,所以需要捕獲,如果出錯了,需要用錯誤 對象reject
    executor(resolve,reject);
  }catch(e){
    //如果這函數執行失敗了,則用失敗的原因reject這個promise
    reject(e);
  };
}
function resolvePromise(promise2,x,resolve,reject){
  if(promise2 === x){
    return reject(new TypeError("循環引用"));
  }
  let called = false;//promise2是否已經resolve 或reject了
  if(x instanceof Promise){
    if(x.status == PENDING){
      x.then(function(y){
        resolvePromise(promise2,y,resolve,reject);
      },reject);
    }else{
      x.then(resolve,reject);
    }
  //x是一個thenable對象或函數,只要有then方法的對象,
  }else if(x!= null &&((typeof x=="object")||(typeof x == "function"))){
    //當我們的promise和別的promise進行交互,編寫這段代碼的時候盡量的考慮兼容性,允許別人瞎寫
   try{
     let then = x.then;
     if(typeof then == "function"){
       //有些promise會同時執行成功和失敗的回調
       then.call(x,function(y){
         //如果promise2已經成功或失敗了,則不會再處理了
          if(called)return;
          called = true;
          resolvePromise(promise2,y,resolve,reject)
       },function(err){
         if(called)return;
         called = true;
         reject(err);
       });
     }else{
       //到此的話x不是一個thenable對象,那直接把它當成值resolve promise2就可以了
       resolve(x);
     }
   }catch(e){
     if(called)return;
     called = true;
     reject(e);
   }

  }else{
    //如果X是一個普通 的值,則用x的值去resolve promise2
    resolve(x);
  }
}
//onFulfilled 是用來接收promise成功的值或者失敗的原因
Promise.prototype.then = function(onFulfilled,onRejected){
  //如果成功和失敗的回調沒有傳,則表示這個then沒有任何邏輯,只會把值往后拋
  //2.2.1
  onFulfilled = typeof onFulfilled == "function"");function(value){return  value};
  onRejected = typeof onRejected == "function"");let self = this;
  let promise2;
  if(self.status == FULFILLED){
    return promise2 = new Promise(function(resolve,reject){
      setTimeout(function(){
        try{
          let x =onFulfilled(self.value);
          //如果獲取到了返回值x,會走解析promise的過程
          resolvePromise(promise2,x,resolve,reject);
        }catch(e){
          //如果執行成功的回調過程中出錯了,用錯誤原因把promise2 reject
          reject(e);
        }
      })

    });
  }
  if(self.status == REJECTED){
    return promise2 = new Promise(function(resolve,reject){
      setTimeout(function(){
        try{
          let x =onRejected(self.value);
          resolvePromise(promise2,x,resolve,reject);
        }catch(e){
          reject(e);
        }
      })
    });
  }
  if(self.status == PENDING){
   return promise2 = new Promise(function(resolve,reject){
     self.onResolvedCallbacks.push(function(){
         try{
           let x =onFulfilled(self.value);
           //如果獲取到了返回值x,會走解析promise的過程
           resolvePromise(promise2,x,resolve,reject);
         }catch(e){
           reject(e);
         }

     });
     self.onRejectedCallbacks.push(function(){
         try{
           let x =onRejected(self.value);
           resolvePromise(promise2,x,resolve,reject);
         }catch(e){
           reject(e);
         }
     });
   });
  }
}

具體的代碼細節呢?我在代碼中已經做了標注,如果又不懂的可以私聊、微信 歡迎來擾。

說在最后

已經有好幾個中午好怎么睡午覺了,這篇文章寫的有點敷衍。本來下班的時候就8點半了,然后又整理/調試代碼 整了半天,結果在寫文章的時候想例子想個半天都想不出來。我明天會利用休息時間好好來修改一下這篇文章的,太困了 先這樣吧,我洗澡去睡覺了。您如果對現在的這篇文章不太滿意,就請過一天再來看。

最后提一句,能不能給我漲點人氣啊,寫了一年多,還是這點人氣..

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/6770.html

相關文章

  • Java 征途:行者的地圖

    摘要:老實說,當時一進入世界的大門就暈了,各種規范概念和英文縮寫詞能把人整的暈暈乎乎。等新的英文縮寫又出現了,一口老血還沒來得及噴出,又重新振作開始新的學習征程。 showImg(http://upload-images.jianshu.io/upload_images/1131767-1c5d16e39435df10.jpg?imageMogr2/auto-orient/strip%7Ci...

    dkzwm 評論0 收藏0
  • 在 PHP 中使用 Promise + co/yield 協程

    摘要:只要在調用異步函數時設置一個或多個回調函數,函數就會在完成時自動調用回調函數。要解決的問題是,如何將回調方法的參數從回調方法中傳遞出來,讓它可以像同步函數的返回結果一樣,在回調函數以外的控制范圍內,可以傳遞和復用。 摘要: 我們知道 JavaScript 自從有了 Generator 之后,就有了各種基于 Generator 封裝的協程。其中 hprose 中封裝的 Promise 和...

    appetizerio 評論0 收藏0
  • 從頭開始,徹底理解服務端渲染原理(8千字匯總長文)

    摘要:到此,就初步實現了一個組件是服務端渲染。服務端渲染完成頁面結構,瀏覽器端渲染完成事件綁定。但是,在服務端渲染中卻出現了問題。根據這個思路,服務端渲染中異步數據的獲取功能就完成啦。 大家好,我是神三元,這一次,讓我們來以React為例,把服務端渲染(Server Side Render,簡稱SSR)學個明明白白。 這里附上這個項目的github地址:https://github.com/...

    hiyang 評論0 收藏0
  • 馬蹄疾 | 詳解 JavaScript 異步機制及發展歷程(萬字長文)

    摘要:本文從入手,系統的回顧的異步機制及發展歷程。需要提醒的是,文本沒有討論的異步機制。這就是之前提到的事件觸發線程。其實無論是請求還是定時器還是事件,我們都可以統稱它們為事件。第二階段,引擎線程專注于處理事件。將外元素的事件回調放入調用棧。 functionshowImg(url){ varframeid=frameimg+Math.random(); window.img=window....

    shaonbean 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<