摘要:簡單實現前言你可能知道,的任務執行的模式有兩種同步和異步。你已經實現了方法方法是一個很好用的方法。感興趣的朋友可以自行去研究哈附上代碼完整的實現個人博客鏈接
Promise 簡單實現 前言
你可能知道,javascript 的任務執行的模式有兩種:同步和異步。
異步模式非常重要,在瀏覽器端,耗時很長的操作(例如 ajax 請求)都應該異步執行,避免瀏覽器失去響應。
在異步模式編程中,我們經常使用回調函數。一不小心就可能寫出以下這樣的代碼:
//事件1 doSomeThing1(function() { //事件2 doSomeThing2(function() { //事件3 doSomeThing3(); }); });
當你的需要異步執行的函數越來越多,你的層級也會越來越深。
這樣的寫法存在的缺點是:
不利于閱讀
各個任務之間的高度耦合,難以維護
對異常的處理比較難
用 Promise 可以避免這種回調地獄,可以寫成這樣
//事件1 doSomeThing1() .then(function() { //事件2 return doSomeThing2(); }) .then(function() { //事件3 return doSomeThing3(); }) .catch(function() { //這里可以很方便的做異常處理 });
在市面上有許多庫都實現了 Promise,例如:Q.js 、when.js ,es6 也將 Promise 納入了標準中
es6 的 Promise 使用方法可以參考阮一峰的 http://es6.ruanyifeng.com/#do... ,我就不在做具體介紹
接下來,我們模仿 ES6 的 promise,一步一步來實現一個簡單版的 Promise。
構造函數我們使用 Promise 的時候,
const promise = new Promise((resolve, reject)=>{ // ... some code if (/* 異步操作成功 */){ resolve(value); } else { reject(error); } });
Promise 是一個構造函數,接收一個函數,函數里有兩個參數,resolve、reject。
我們可以這樣子實現:
class PromiseA { constructor(executor) { const resolve = value => { this.resolve(value); }; const reject = err => { this.reject(err); }; try { executor(resolve, reject); } catch (err) { reject(err); } } resolve(value) { this.resultValue = value; } reject(error) { this.resultValue = error; } }then 方法
promise 中,用的最頻繁的就是 then 方法,then 方法有兩個參數,一個是 promise 成功時候的回調,一個是失敗的回調。
實現方式為:
class PromiseA { constructor(executor) { const resolve = value => { this.resolve(value); }; const reject = err => { this.reject(err); }; try { executor(resolve, reject); } catch (err) { reject(err); } } resolve(value) { this.resultValue = value; if (this.fullfillCallback) { //++++++++ this.fullfillCallback(value); } } reject(error) { this.resultValue = error; if (this.rejectCallback) { //++++++++ this.rejectCallback(value); } } then(resolve, reject) { this.fullfillCallback = resolve; this.rejectCallback = resolve; } }then 方法有以下幾個特性:
支持鏈式操作
每次 then 方法都是返回新的 Promise
當前 promise 的狀態通過返回值傳遞給下一個 promise
錯誤冒泡,即如果當前 promise 沒有提供 onReject 方法,會把錯誤冒泡到下一個 promise,方便處理
then(onResolve,onReject){ //返回新的Promise并支持鏈式操作 return new PromiseA((resolve,reject)=>{ this.fullfillCallback = (value)=>{ try { if (onResolve) { let newValue = onResolve(value); resolve(newValue); //將當前promise執行結果,傳遞給下一個promise } else { resolve(value); } } catch (err) { reject(err); } } //類似fullfillCallback this.rejectCallback = (value)=>{ try { if (onReject) { let newValue = onReject(value); resolve(newValue); } else { //錯誤冒泡 reject(value); } } catch (err) { reject(err); } } }); }
這樣我們就實現了一個簡單版的 then 方法了
加強版 then上面的實現,模擬了 then 方法的邏輯,但是還有一些缺點:
then 方法只能添加一個,例如
let p = new PromiseA(resolve => { setTimeout(() => { resolve(1); }, 0); }); p.then(value => { console.log("then-->" + value); }); //無輸出,因為沒觸發到,被后一個覆蓋了 p.then(value => { console.log("then2-->" + value); }); ////then---> 1
promise 沒有狀態,當 promsie 在添加 then 的時候已經完成了,沒法得到結果
沒有實現:如果上一個 promise 的返回值也是一個 Promise 對象時,則會等到這個 Promise resolve 的時候才執行下一個
為了解決第一點,引入了事件監聽,簡單的實現如下:export default class EventEmitter { constructor() { this._events = {}; } on(type, fn, context = this) { if (!this._events[type]) { this._events[type] = []; } this._events[type].push([fn, context]); } trigger(type) { let events = this._events[type]; if (!events) { return; } let len = events.length; let eventsCopy = [...events]; for (let i = 0; i < len; i++) { let event = eventsCopy[i]; let [fn, context] = event; if (fn) { fn.apply(context, [].slice.call(arguments, 1)); } } } }
所以進一步對 PromiseA 進行改造:
const STATUS = { PENDING: "pending", FULFILLED: "fulfilled", REJECTED: "rejected" }; const EventType = { fulfill: "fulfill", reject: "reject" }; class PromiseA { constructor(executor) { //初始化事件監聽及狀態 this.eventEmitter = new EventEmitter(); this.status = STATUS.PENDING; const resolve = value => { if (value instanceof PromiseA) { value.then( value => { this.resolve(value); }, error => { this.reject(error); } ); } else { this.resolve(value); } }; const reject = err => { this.reject(err); }; try { executor(resolve, reject); } catch (err) { reject(err); } } resolve(value) { //增加狀態 if (this.status === STATUS.PENDING) { this.status = STATUS.FULFILLED; this.resultValue = value; this.eventEmitter.trigger(EventType.fulfill, value); } } reject(error) { //增加狀態 if (this.status === STATUS.PENDING) { this.status = STATUS.REJECTED; this.resultValue = error; this.eventEmitter.trigger(EventType.reject, error); } } then(onResolve, onReject) { //根據狀態不同處理 if (this.status === STATUS.PENDING) { return new PromiseA((resolve, reject) => { //增加事件監聽 this.eventEmitter.on("fulfill", value => { try { if (onResolve) { let newValue = onResolve(value); resolve(newValue); } else { resolve(value); } } catch (err) { reject(err); } }); //增加事件監聽 this.eventEmitter.on("reject", value => { try { if (onReject) { let newValue = onReject(value); resolve(newValue); } else { reject(value); } } catch (err) { reject(err); } }); }); } if ( this.status === STATUS.FULFILLED || this.status === STATUS.REJECTED ) { return new PromiseA((resolve, reject) => { let callback = returnValue; if (this.status === STATUS.FULFILLED) { callback = onResolve; } if (this.status === STATUS.REJECTED) { callback = onReject; } try { let newValue = callback(this.resultValue); resolveValue(newValue, resolve, reject); } catch (err) { reject(err); } }); } } }
到這里,我們的 then 方法基本就完成了。
最后還有一個小知識點,就是執行時機的問題:setTimeout(function() { console.log(4); }, 0); new Promise(function(resolve) { console.log(1); resolve(); }).then(function() { console.log(3); }); console.log(2); //輸出結果會是: 1、2、3、4
promise.then,是異步的,屬于 microtask,執行時機是本次事件循環結束之前,而 setTimeout 是 macrotask,執行時機是在下一次事件循環的開始之時
實現這個功能,我利用了第三方庫 microtask 來模擬。所以 PromiseA 修改為:
resolve(value) { microtask(() => { if (this.status === STATUS.PENDING) { this.status = STATUS.FULFILLED; this.resultValue = value; this.eventEmitter.trigger(EventType.fulfill, value); } }) } reject(error) { microtask(() => { if (this.status === STATUS.PENDING) { this.status = STATUS.REJECTED; this.resultValue = error; this.eventEmitter.trigger(EventType.reject, error); } }); }
到此為止,我們的 then 方法已經大功告成了。最困難的一步已經解決了
catchPromise 跟普通回調的一大優勢就是異常處理,我們推薦使用 Promise 的時候,總是使用 catch 來代替 then 的第二個參數。即是:
//bad let p = new Promise((resolve, reject) => { //...異步操作 }).then( value => { //成功 }, () => { //失敗 } ); //good let p = new Promise((resolve, reject) => { //...異步操作 }) .then(value => { //成功 }) .catch(() => { //失敗 });
接下來讓我們實現 catch 方法:
catch(reject) { return this.then(null, reject); }
哈哈~ , 你沒看錯。你已經實現了 catch 方法
all 方法Promise.all 是一個很好用的方法。接受一個 promise 數組,然后等到所有的異步操作都完成了,就返回一個數組,包含對應的值
具體實現如下:
static all(promiseList = []) { //返回promise以便鏈式操作 return new PromiseA((resolve, reject) => { let results = []; let len = promiseList.length; let resolveCount = 0; //用于計數 let resolver = function (index, value) { resolveCount++; results[index] = value; if (resolveCount === len) { resolve(results); } }; //遍歷執行所有的promise promiseList.forEach((p, i) => { if (p instanceof PromiseA) { p.then((value) => { resolver(i, value); }, (err) => { reject(err); }) } else { resolver(i, p); } }) }); }race 方法
race 方法為競速,第一執行完的為準。所以只需循環一遍執行就可以了。
當有第一個將 Promise 的狀態改變成 fullfilled 或 reject 之后,其他的就都無效了
static race(promiseList = []) { return new PromiseA((resolve, reject) => { promiseList.forEach((p, i) => { if (p instanceof PromiseA) { p.then((value) => { resolve(value); }, (err) => { reject(err); }) } else { resolve(p); } }) }) }小結
我們實現了一個簡單版的 promise, 還有一些其他的方法在這里沒有講到。感興趣的朋友可以自行去研究哈~
附上代碼完整的實現 : https://github.com/chen434202...
個人博客鏈接:https://chen4342024.github.io...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/100233.html
摘要:近幾年隨著開發模式的逐漸成熟,規范順勢而生,其中就包括提出了規范,完全改變了異步編程的寫法,讓異步編程變得十分的易于理解。最后,是如此的優雅但也只是解決了回調的深層嵌套的問題,真正簡化異步編程的還是,在端,建議考慮。 本篇,簡單實現一個promise,主要普及promise的用法。 一直以來,JavaScript處理異步都是以callback的方式,在前端開發領域callback機制...
摘要:實現的一個簡單的如果有錯誤的地方,希望大家能夠不吝賜教僅實現及方法最下方有完整代碼開始一個對象接收的是一個這個接收兩個參數當我們在內執行或的時候,就會調用內定義的和函數然后,和函數會改變的狀態所以它應該是像下面這樣的保存值記錄狀態為,為,為 實現的一個簡單的ES6 Promise(如果有錯誤的地方,希望大家能夠不吝賜教) 僅實現Promise及.then方法最下方有完整代碼 開始 一個...
摘要:在和方法執行的時候訂閱事件,將自己的回調函數綁定到事件上,屬性是發布者,一旦它的值發生改變就發布事件,執行回調函數。實現和方法的回調函數都是,當滿足條件對象狀態改變時,這些回調會被放入隊列。所以我需要在某個變為時,刪除它們綁定的回調函數。 前言 按照文檔說明簡單地實現 ES6 Promise的各個方法并不難,但是Promise的一些特殊需求實現起來并不簡單,我首先提出一些不好實現或者容...
摘要:為了降低異步編程的復雜性,所以。難理解請參考的誤區以及實踐異步編程的模式異步編程的種方法 異步編程 javascript異步編程, web2.0時代比較熱門的編程方式,我們平時碼的時候也或多或少用到,最典型的就是異步ajax,發送異步請求,綁定回調函數,請求響應之后調用指定的回調函數,沒有阻塞其他代碼的執行。還有像setTimeout方法同樣也是異步執行回調的方法。 如果對異步編程...
摘要:近幾年隨著開發模式的逐漸成熟,規范順勢而生,其中就包括提出了規范,完全改變了異步編程的寫法,讓異步編程變得十分的易于理解。最后,是如此的優雅但也只是解決了回調的深層嵌套的問題,真正簡化異步編程的還是,在端,建議考慮。 前段時間頻頻看到Promise這個詞,今天發現騰訊AlloyTeam寫得這篇很贊,遂轉之。 原文鏈接 本篇,主要普及promise的用法。 一直以來,JavaScrip...
閱讀 1830·2021-11-11 16:55
閱讀 749·2019-08-30 15:53
閱讀 3587·2019-08-30 15:45
閱讀 671·2019-08-30 14:10
閱讀 3262·2019-08-30 12:46
閱讀 2122·2019-08-29 13:15
閱讀 2025·2019-08-26 13:48
閱讀 933·2019-08-26 12:23