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