promise應用及原生實現promise模型

1、先看一個應用場景javascript

  發送一個請求得到用戶id, 而後根據所得到的用戶id去執行另外處理。固然這裏咱們徹底可使用回調,即在請求成功以後執行callback;java

可是若是又添加需求呢?好比得到用戶id以後,再發送請求去獲取用戶名,以後再獲取用戶其餘信息。。。。這就陷入了callback-hell,並且代碼很難維護。promise能夠解決這樣的問題。ios

function getUserId() {
    return new Promise(function(resolve) {
        //異步請求
        http.get(url, function(res) {
            resolve(res.id)
        })
    })
}

getUserId().then(function(id) {
    //其餘處理
})

上面的回調是否是看起來不叫順手了。 es6

 

2、Promise是什麼?ajax

它簡單說就是一個容器,就是一個構造函數。裏面保存着某個將來纔會結束的事件(一般是一個異步操做)的結果。從語法上說,Promise 是一個對象,從它能夠獲取異步操做的消息。Promise 提供統一的 API,各類異步操做均可以用一樣的方法進行處理。編程

也能夠說Promise 是異步編程的一種解決方案,其實咱們常用$.ajax()就是一種promise的解決方案axios

更加詳細的內容能夠看阮一峯的《Promise對象》數組

 

3、can i use promise

如今更多的瀏覽器對Promise提供了原生的支持。axios基於 Promise 的 HTTP 請求就是一個經常使用的應用栗子。瀏覽器

 

3、寫點demo

當對概念有點燃的時候,回頭看看本身寫過的demo,確定有不同的理解,這便是博客的一個好處之一

function getNumber(){
    return new Promise(function(resolve, reject){
        //作一些異步操做
        setTimeout(function(){
            var num = Math.ceil(Math.random()*10);
            if(num<=5){
                resolve(num);
            }
            else{
                reject('num的值大了');
            }
        }, 2000);
    });        
}

getNumber()
.then(function(data){
        console.log('resolved');
        console.dir(data);
    })
.catch(function(data){
	console.log('rejected');
        console.dir(data);
});

  

封裝一個函數,參數是定時器的時間參數

 function sleep (time) {
    return new Promise((resolve) => setTimeout(resolve, time));
 }
sleep(5000).then(function(val){
  console.log(`${val}秒以後起牀`);
});

 

4、怎麼實現一個promise?  

一、最簡單的promise雛形

function Promise(fn){

if(fn && typeof fn !== 'function'){ throw new Error('Promise resolver is not a function') }; //回調函數數組,肯能同時有多個回調 this.callBacks = []; //執行函數 const resolve = val =>{ //執行所有回調 setTimeout(()=>{ this.callBacks.forEach(cb=>{ cb(val) }); },0) }; //將resolve做爲實參傳入fn中,fn的異步何時有結果再去執行resolve fn(resolve); } //註冊回調函數,successCallback將會被壓進Promise容器中的callBacks隊列 Promise.prototype.then = function(successCallback){ this.callBacks.push(successCallback); }; //測試用例 const promise = new Promise(function(resolve){ //模擬一個異步 setTimeout(()=>{ resolve('我是傳給Promise內執行函數的參數') },2000) }) promise.then(function(val){ console.log(val); });

  

說明:
(1)、建立promise實例時候傳進的函數函數被賦予一個函數類型的參數resolve,resolve接收一個參數,表明異步但返回的結果;當異步操做成功以後,就會執行resolve方法;
(2)、調用then方法,將想要在Promise異步操做成功時執行的回調放入callBacks隊列,其實也就是註冊回調函數,
(3)、第一步中執行resolve方法就就是執行callbacks數組中的回調函數,依次執行;
(4)、加入延遲機制,經過setTimeout(),保證在resolve執行以前,then方法已經註冊完全部的回調。

二、加入狀態

pending :即將發生的狀態
fulfilled : 成功狀態
rejected : 失敗狀態

function Promise(fn){
     if(fn && typeof fn !== 'function'){ throw new Error('Promise resolver is not a function') };
	//回調函數數組
	this.callBacks = [];

	//一開始的狀態是發生的狀態
	this.status = 'pending';

	//當即執行的參數,初始爲null
	this._val = Object.create(null);

	//執行函數
	const resolve = val =>{
		//改變狀態
		this.status = 'fulfill';

		//當即執行的參數
		this._val = val;

		//執行所有回調
		setTimeout(()=>{
			this.callBacks.forEach(cb => {
				cb(val)
			});
		})
	};
	//將resolve做爲實參傳入fn中並執行,fn的異步何時有結果再去執行resolve函數
	fn(resolve);
}

Promise.prototype.then = function(successCallback){
	if (this.status === 'pending') {
		this.callBacks.push(successCallback);    
	}
	successCallback(this._val); 
};

const promise = new Promise(function(resolve){
	//模擬一個異步
	setTimeout(()=>{
		resolve('我是傳給Promise內執行函數的參數')
	},2000)
})

promise.then(function(val){
	console.log(val);
});

//此時promise實例執行的時候,status已經變爲了‘fulfill’,在此以後調用then添加的新回調,都會當即執行。
setTimeout(() => {
     promise.then(function(val) {
	   console.log(val);
    });
}, 3000)

  

三、鏈式

若是在then函數裏面再注入一個promise呢?

主要是對then函數和resolve啊含糊的額改造,看這裏~

《30分鐘,讓你完全明白Promise原理》

 

四、es6實現寫法

class CutePromise {
 constructor(executor) {
 if (typeof executor !== 'function') {
  throw new Error('Executor must be a function');
 }

 this.state = 'PENDING';
 this.chained = [];
 const resolve = res => {
  if (this.state !== 'PENDING') {
  return;
  }

  this.state = 'FULFILLED';
  this.internalValue = res;
  for (const { onFulfilled } of this.chained) {
  onFulfilled(res);
  }
 };
 const reject = err => {
  if (this.state !== 'PENDING') {
  return;
  }
  this.state = 'REJECTED';
  this.internalValue = err;
  for (const { onRejected } of this.chained) {
  onRejected(err);
  }
 };

 try {
  executor(resolve, reject);
 } catch (err) {
  reject(err);
 }
 }
 
 then(onFulfilled, onRejected) {
 if (this.state === 'FULFILLED') {
  onFulfilled(this.internalValue);
 } else if (this.$state === 'REJECTED') {
  onRejected(this.internalValue);
 } else {
  this.chained.push({ onFulfilled, onRejected });
 }
 }
}

//test
let p = new CutePromise(resolve => {
 setTimeout(() => resolve('Hello'), 100);
});
p.then(res => console.log(res));
p = new CutePromise((resolve, reject) => {
 setTimeout(() => reject(new Error('woops')), 100);
});
p.then(() => {}, err => console.log('Async error:', err.stack));
p = new CutePromise(() => { throw new Error('woops'); });
p.then(() => {}, err => console.log('Sync error:', err.stack));

  

 

5、總結

一、Promise的構造函數接收一個函數類型的參數,而且傳入兩個參數:resolve,reject,分別表示異步操做執行成功後的回調函數和異步操做執行失敗後的回調函數。二、resolve()執行時傳入的參數,傳到了以後調用的.then()中。三、reject()執行時傳入的參數,傳到以後調用的.catch()中。在裏面拋出代碼函數的異常,不會卡死js.四、調用getNumber()時候返回Promise的實例對象,這樣就 支持了了鏈式的調用。

相關文章
相關標籤/搜索