首頁  >  文章  >  web前端  >  使用Js實作Promise函式庫

使用Js實作Promise函式庫

php中世界最好的语言
php中世界最好的语言原創
2018-06-07 13:46:541865瀏覽

這次帶給大家使用Js實作Promise函式庫,使用Js實作Promise函式庫的注意事項有哪些,以下就是實戰案例,一起來看一下。

前言

ECMAScript 是 JavaScript 語言的國際標準,JavaScript 是 ECMAScript 的實作。 ES6 的目標,是使得 JavaScript 語言可以用來編寫大型的複雜的應用程序,成為企業級開發語言。

概念

ES6 原生提供了 Promise 物件。

所謂 Promise,就是一個對象,用來傳遞非同步操作的訊息。它代表了某個未來才會知道結果的事件(通常是一個非同步操作),而這個事件提供統一的 API,可供進一步處理。

三道思考題

剛開始寫前端的時候,處理非同步請求常用callback,簡單又順手。後來寫著寫著就拋棄了callback,開始用promise來處理非同步問題。 promise寫起來確實更加優美,但由於缺乏對它內部結構的深刻認識,每次在遇到一些複雜的情況時,promise用起來總是不那麼得心應手,debug也得搞半天。

所以,這篇文章我會帶大家從零開始,手寫一個基本上能用的promise。跟著我寫下來以後,你會對promise是什麼以及它的內部結構有一個清楚的認知,未來在復雜場景下使用promise也能如魚得水。

而且,為了檢驗大家是否真的完全掌握了promise,我會在文章結尾出幾道跟promise相關的練習題。說是練習題,其實都是大家專案中會遇到的真實場景的抽象,熟練可以幫助大家在前端方面更上一層樓。

提前將三道練習題給出來,大家可以先不看下文的內容,在腦海裡大概構思下你會怎麼解決:

  • promise array的鍊式呼叫?

  • promise怎麼做並發控制?

  • promise怎麼做非同步快取?

以上三道思考題其實跟你用不用promise並沒有太大關係,但是如果你不深刻理解promise想要解決這三個問題還真不是那麼輕鬆的。

什麼是Promise

回到正文,什麼是Promise?說穿了,promise就是一個容器,裡面保存著某個未來才會結束的事件(通常是一個非同步操作)的結果。

首先,ES6規定Promise物件是一個建構函數,用來產生Promise實例。然後,這個建構子接受一個函式(executor)作為參數,該函式的兩個參數分別是resolve和reject。最後,Promise實例產生以後,可以用then方法分別指定resolved狀態和rejected狀態的回呼函數(onFulfilled和onRejected)。

具體的使用方法,用程式碼表現是這樣:

const promise = new Promise(function(resolve, reject) {
 // ... some code
 if (/* 异步操作成功 */){
 resolve(value);
 } else {
 reject(error);
 }
});
promise.then(function(value) {
 // success
}, function(error) {
 // failure
});

理解了這個後,我們就可以大膽的開始構造我們自己的promise了,我們給它取個名字:CutePromise

實作一個Promise:CutePromise

#我們直接用ES6的class來建立我們的CutePromise,對ES6語法還不熟悉的,可以先讀我的另外兩篇介紹ES6核心語法的文章再回來。 30分鐘掌握ES6/ES2015核心內容(上) 、30分鐘掌握ES6/ES2015核心內容(下)

class CutePromise {
 // executor是我们实例化CutePromise时传入的参数函数,它接受两个参数,分别是resolve和reject。
 // resolve和reject我们将会定义在constructor当中,供executor在执行的时候调用
 constructor(executor) {
 const resolve = () => {}
 const reject = () => {}
 executor(resolve, reject)
 }
 // 为实例提供一个then的方法,接收两个参数函数,
 // 第一个参数函数必传,它会在promise已成功(fulfilled)以后被调用
 // 第二个参数非必传,它会在promise已失败(rejected)以后被调用
 then(onFulfilled, onRejected) {}
}

創建了我們的CutePromise後,我們再來搞清楚一個關鍵點:Promise 物件的狀態。

Promise 物件透過自身的狀態,來控制非同步操作。一個Promise 實例有三種狀態:

  • 非同步操作未完成(pending)

  • 非同步操作成功(fulfilled)

  • 非同步操作失敗(rejected)

上面三種狀態裡面,fulfilled和rejected合在一起稱為resolved(已定型)。狀態的切換只有兩條路徑:第一種是從pending=>fulfilled,另一種是從pending=>rejected,狀態一旦切換就無法再改變。

現在我們來為CutePromise新增狀態,大概流程就是:

首先,实例化初始过程中,我们先将状态设为PENDING,然后当executor执行resolve的时候,将状态更改为FULFILLED,当executor执行reject的时候将状态更改为REJECTED。同时更新实例的value。

constructor(executor) {
 ...
 this.state = 'PENDING';
 ...
 const resolve = (result) => {
  this.state = 'FULFILLED';
  this.value = result;
 }
 const reject = (error) => {
  this.state = 'REJECTED';
  this.value = error;
 }
 ...
}

再来看下我们的then函数。then函数的两个参数,onFulfilled表示当promise异步操作成功时调用的函数,onRejected表示当promise异步操作失败时调用的函数。假如我们调用then的时候,promise已经执行完成了(当任务是个同步任务时),我们可以直接根据实例的状态来执行相应的函数。假如promise的状态还是PENDING, 那我们就将onFulfilled和onRejected直接存储到chained这个变量当中,等promise执行完再调用。

constructor(executor) {
 ...
 this.state = 'PENDING';
 
 // chained用来储存promise执行完成以后,需要被依次调用的一系列函数
 this.chained = [];
 const resolve = (result) => {
  this.state = 'FULFILLED';
  this.value = result;
  
  // promise已经执行成功了,可以依次调用.then()函数里的onFulfilled函数了
  for (const { onFulfilled } of this.chained) {
   onFulfilled(res);
  }
 }
 ...
}
then(onFulfilled, onRejected) {
 if (this.state === 'FULFILLED') {
 onFulfilled(this.value);
 } else if (this.state === 'REJECTED') {
 onRejected(this.value);
 } else {
 this.$chained.push({ onFulfilled, onRejected });
 }
}

这样我们就完成了一个CutePromise的创建,下面是完整代码,大家可以复制代码到控制台测试一下:

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 });
 }
 }
}

提供一下测试代码:

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));

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

JS实现输入框内灰色文字提示

路径中#号怎样除去

以上是使用Js實作Promise函式庫的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn