摘要:三模式模式其實(shí)包含兩部分和。六化在編碼的時(shí)候,想要用進(jìn)行異步操作流程控制,就要將當(dāng)前的異步回調(diào)函數(shù)封裝成。
一、什么是promise/deferred 模式
promise/deferred 模式是,根據(jù)promise/A 或者它的增強(qiáng)修改版promise/A+ 規(guī)范 實(shí)現(xiàn)的promise異步操作的一種實(shí)現(xiàn)方式。
異步的廣度使用使得回調(diào),嵌套出現(xiàn),但是一但出現(xiàn)深度的嵌套,就會讓coding的體驗(yàn)變得相當(dāng)不愉快,而且代碼后期的維護(hù)也是相當(dāng)吃力的。promise/deferred模式的出現(xiàn),會在一定程度上緩解這個(gè)問題。接下來我會根據(jù)promise/a 規(guī)范來介紹promise/deferred模式。
(題外話:什么是規(guī)范,規(guī)范其實(shí)就相當(dāng)于制定的規(guī)則,但卻沒有在代碼層面上有默認(rèn)的具體實(shí)現(xiàn))
promise/a 提議對單個(gè)異步操作作出了這樣的抽象定義:
1.promise操作只會在以下3種狀態(tài)中的一種:等待態(tài)(Pending)、執(zhí)行態(tài)(Fulfilled)和拒絕態(tài)(Rejected)。
2.promise的狀態(tài)只會出現(xiàn)從等待狀態(tài)向執(zhí)行態(tài)或者拒絕態(tài)轉(zhuǎn)化,不可以逆轉(zhuǎn)。執(zhí)行態(tài)和拒絕態(tài)之間不能相互轉(zhuǎn)換
3.promise狀態(tài)一旦被轉(zhuǎn)換,就不能被更改。
4.在api上,規(guī)范定義比較簡單,只要求promise 必修提供有一個(gè)then方法,以訪問當(dāng)前值、最終值和拒絕原因
then方法接受兩個(gè)參數(shù)
promise.then(onFulfilled,onRejected)
5.then方法的onFulfilled,onRejected 方法都是可選參數(shù),且不是function,都被忽略
6.then()方法返回promise對象,以實(shí)現(xiàn)鏈?zhǔn)綄懛ā?/p>
三、promise/deferred模式
promise/deferred 模式 其實(shí)包含兩部分:Promise 和 Deferred。
Deferred主要是用于內(nèi)部,來維護(hù)異步模型的狀態(tài)。
Promise只要用于外部,通過then()方法,暴露給外部調(diào)用,以添加業(yè)務(wù)邏輯和業(yè)務(wù)的組裝。
promise 和 deferred的關(guān)系圖
從圖中可以看到:
1.deferred對象通過resolve方法,改變自身狀態(tài)為執(zhí)行態(tài),并觸發(fā)then()方法的onfulfilled回調(diào)函數(shù) 2.deferred對象通過reject方法,改變自身狀態(tài)為拒絕態(tài),并觸發(fā)then()方法的onrejected回調(diào)函數(shù)
下面 我們就用代碼來實(shí)現(xiàn)一下:
/** * Promise 類 * @constructor */ function Promise() { this.handler = {}; } /** * promise 對象的then方法 * @param onFulfilled 當(dāng) promise 執(zhí)行結(jié)束后其必須被調(diào)用,其第一個(gè)參數(shù)為 promise 的終值,其調(diào)用次數(shù)不可超過一次 * @param onRejected 當(dāng) promise 被拒絕執(zhí)行后其必須被調(diào)用,其第一個(gè)參數(shù)為 promise 的據(jù)因,其調(diào)用次數(shù)不可超過一次 * @returns {Promise} 規(guī)范定義必修返回 primise對象 */ Promise.prototype.then = function (onFulfilled, onRejected) { var handler = {} if (typeof onFulfilled === "function") { handler.resolve = onFulfilled } if (typeof onRejected === "function") { handler.reject = onRejected } this.handler = handler return this }
這里可以看到then方法所做的事情就是講回調(diào)函數(shù)存放起來,為了完成整個(gè)流程,還需要觸發(fā)執(zhí)行這些回調(diào)函數(shù)的地方,而實(shí)現(xiàn)這些功能的對象就叫做deferred(延遲對象)。示范代碼如下
function Deferred() { /* 狀態(tài):默認(rèn) 等待態(tài) pending */ this.state = "pending"; this.promise = new Promise() } Deferred.prototype.resolve = function (obj) { this.state = "fulfilled" var handler = this.promise.handler if (handler && handler.resolve) { handler.resolve(obj) } } Deferred.prototype.reject = function (obj) { this.state = "rejected" var handler = this.promise.handler if (handler && handler.reject) { handler.reject(obj) } }
以上已經(jīng)定義好了Promies 和Deferred ,那我們怎么對一個(gè)異步操作函數(shù)進(jìn)行封裝呢?
假如我們有這樣的異步函數(shù)
function asyncDoSomeing(flag, message) { setTimeout(function () { if (flag) { return message } }, 3000) }
對其封裝的代碼就是
function asyncDoSomeing(flag, message) { var deferred = new Deferred() setTimeout(function () { if (flag) { deferred.resolve(message) } else { deferred.reject({code: 400, message: "拒絕"}) } }, 3000) return deferred.promise }
最后我們就可以這么使用了
asyncDoSomeing(true, "測試執(zhí)行成功").then(function (message) { console.log(message) }, function (err) { console.log(err) })
到這里只是單個(gè)promise對象的簡單異步的操作控制,但是有熟悉node.js 和angular.js 的同學(xué)就會發(fā)現(xiàn),這個(gè)寫法跟node.js 里面的一個(gè)異步控制流程 q 模塊(https://github.com/kriskowal/q )寫法是一樣的。是的哦 它就是promise/deferred 模式。隨便提一下 Angularjs的$q對象是q的精簡版。
四、鏈?zhǔn)秸{(diào)用做到以上的簡單實(shí)現(xiàn),理想的coding方式,應(yīng)該前一個(gè)調(diào)用結(jié)果作為下一個(gè)調(diào)用的輸入,這就是鏈?zhǔn)秸{(diào)用。
為了避免回調(diào)地獄,可以借鑒jquery的鏈?zhǔn)綄懛ā?/p>
$("#tab").eq($(this).index()).show().siblings().hide();
鏈?zhǔn)綄懛ǖ暮诵脑谟?,每個(gè)方法都返回 自身 this。
我們現(xiàn)在需要實(shí)現(xiàn)promise的鏈?zhǔn)秸{(diào)用,前一個(gè)調(diào)用結(jié)果作為下一個(gè)調(diào)用的輸入
step1.then(step2).then(step3)
現(xiàn)在我們實(shí)現(xiàn)的then方法確實(shí)是返回this的,也就是promise本身,是可以實(shí)現(xiàn)鏈?zhǔn)降摹?br>但是前一個(gè)調(diào)用的結(jié)果卻做不到是下一個(gè)調(diào)用的輸入
下面來改造一下上面的代碼,讓他實(shí)現(xiàn)這個(gè)要求。
function Promise() { this.handlerQueue = []; this.isPromise = true }
1.將原本的handler對象改為 一個(gè)數(shù)組,存放所有then方法的回調(diào)。
Promise.prototype.then = function (onFulfilled, onRejected) { var handler = {} if (typeof onFulfilled === "function") { handler.resolve = onFulfilled } if (typeof onRejected === "function") { handler.reject = onRejected } this.handlerQueue.push(handler) return this } function Deferred() { this.state = "pending" this.promise = new Promise() } Deferred.prototype.resolve = function (obj) { this.state = "fulfilled" var promise = this.promise var handler = {} while (handler = promise.handlerQueue.shift()) { if (handler && handler.resolve) { var res = handler.resolve(obj) if (res && res.isPromise) { res.handlerQueue = promise.handlerQueue this.promise = res return; } else { obj = res } } } } Deferred.prototype.reject = function (obj) { this.state = "rejected" var promise = this.promise var handler = {} while (handler = promise.handlerQueue.shift()) { if (handler && handler.reject) { var res = handler.reject(obj) if (res && res.isPromise) { res.handlerQueue = promise.handlerQueue this.promise = res return; } else { obj = res } } } } //------ test-------// function asyncDosomeing(flag, name) { const deferred = new Deferred() setTimeout(function () { if (flag) { deferred.resolve({code: 200, message: "成功", name: name}) } else { deferred.reject({code: 400, message: "失敗", name: name}) } }, 2000) return deferred.promise } asyncDosomeing(true, "asyncDosomeing1").then(result => { console.info(result) return asyncDosomeing(false, "asyncDosomeing2") }).then(result => { console.info(result) return "dadds" }).then(result => { console.info(result) })五、統(tǒng)一的異常處理(拒絕處理)
那現(xiàn)在,我們有個(gè)需求,想實(shí)現(xiàn)所有的拒絕統(tǒng)一在一個(gè)地方處理。而不是每個(gè)then方法都傳一個(gè)rejected 回調(diào),只希望then()方法可以,安安心心的處理成功的回調(diào)。
step1().then(step2).then(step3).catch(function(err){ // do something when err })
加一個(gè)catch err 的回調(diào),當(dāng)出現(xiàn)異常就直接到這個(gè)流程上處理。
那我們就在promise 的原型上架一個(gè)catch方法,如下
Promise.prototype.catch = function (onRejected) {
var handler = {} if (typeof onRejected === "function") { handler.reject = onRejected } this.handlerQueue.push(handler) return this } //------ test-------// function asyncDosomeing(flag, name) { const deferred = new Deferred() setTimeout(function () { if (flag) { deferred.resolve({code: 200, message: "成功", name: name}) } else { deferred.reject({code: 400, message: "失敗", name: name}) } }, 2000) return deferred.promise } asyncDosomeing(true, "asyncDosomeing1").then(result => { console.info(result) return asyncDosomeing(false, "asyncDosomeing2") }).then(result => { console.info(result) return "dadds" }).then(result => { console.info(result) }).catch(err => { console.info("catch") console.info(err) return asyncDosomeing(true, "asyncDosomeing3----catch") })
這樣就可以實(shí)現(xiàn),只要異步操作流程中有一步被拒絕,下面流程就自然中斷,直接到catch回調(diào)中處理異常。
六、API Promise化在編碼的時(shí)候,想要用promise進(jìn)行異步操作流程控制,就要將當(dāng)前的異步回調(diào)函數(shù)封裝成promise。在自己開發(fā)的時(shí)候,往往會去引用第三方的模塊,然后發(fā)現(xiàn)這些模塊的異步回調(diào)API 不支持promise寫法。難道我們自己全部封裝實(shí)現(xiàn)一遍?!這明顯是不合理的。那我們就可以實(shí)現(xiàn)一個(gè) 方法可以批量將方法Promise化,相關(guān)代碼如下:
1.在deferred原型上實(shí)現(xiàn)一個(gè)異步回調(diào)函數(shù),回調(diào)執(zhí)行后觸發(fā)deferred resolve 和 reject的方法
Deferred.prototype.callBack = function () { var that = this return function (err, result) { if (err) { that.reject(err) } else { that.resolve(result) } } } 2.定義一個(gè)Api Promise化 方法 /** * 將異步操作轉(zhuǎn)換成promise */ var promisify = function (method) { if (typeof method !== "function") { throw new TypeError("is not a function") } return function () { const defrred = new Deferred() var args = Array.prototype.slice.call(arguments, 0) // 克隆參數(shù) args.push(defrred.callBack()) method.apply(this, args) return defrred.promise } }
最后我們就可以簡化代碼
var readFile = promisify(fs.readFile); readFile("file.text").then(function(file){ return readFile(file.trim()) }).then(function(file2){ console.log(file2) })
這里只是對promise/deferred 原理的簡單實(shí)現(xiàn),還有很多情況沒有考慮。希望大家在做promise異步流程操作的時(shí)候,還是選擇現(xiàn)在成熟的模塊。比如 q模塊、bulebird、when、或者 es6 的promise 去做。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/87136.html
摘要:簡介項(xiàng)目命名為就是一個(gè)服務(wù)器單純開發(fā)一個(gè)服務(wù)器的想法,變成構(gòu)建網(wǎng)絡(luò)應(yīng)用的一個(gè)基本框架發(fā)展為一個(gè)強(qiáng)制不共享任何資源的單線程,單進(jìn)程系統(tǒng)。單線程弱點(diǎn)無法利用多核錯(cuò)誤會引起整個(gè)應(yīng)用退出,應(yīng)用的健壯性大量計(jì)算占用導(dǎo)致無法繼續(xù)調(diào)用異步。 NodeJs簡介 Ryan Dahl項(xiàng)目命名為:web.js 就是一個(gè)Web服務(wù)器.單純開發(fā)一個(gè)Web服務(wù)器的想法,變成構(gòu)建網(wǎng)絡(luò)應(yīng)用的一個(gè)基本框架.Node發(fā)展...
摘要:為的項(xiàng),取出來的分別為和,所以上的和方法,調(diào)用的是中的方法,實(shí)質(zhì)是往各自的回調(diào)列表中添加回調(diào)函數(shù)。進(jìn)度回調(diào)函數(shù)數(shù)組。參數(shù)為異步對象的索引值,參數(shù)為對應(yīng)的上下文數(shù)組,即或,為對應(yīng)的回調(diào)函數(shù)數(shù)組,即或。 Deferred 模塊也不是必備的模塊,但是 ajax 模塊中,要用到 promise 風(fēng)格,必需引入 Deferred 模塊。Deferred 也用到了上一篇文章《讀Zepto源碼之C...
摘要:所謂的能對狀態(tài)進(jìn)行操作的特權(quán)方法,指的就是能對對象的狀態(tài)進(jìn)行等調(diào)用的方法,而通常的的話只能在通過構(gòu)造函數(shù)傳遞的方法之內(nèi)對對象的狀態(tài)進(jìn)行操作。一般會在構(gòu)造函數(shù)中編寫邏輯,什么時(shí)候執(zhí)行回調(diào),什么時(shí)候執(zhí)行回調(diào)。 原文地址 1. 在then中使用reject 如果一個(gè)promise最初只定義了resolve,但是還想要使用reject怎么辦? 可以在then中返回一個(gè)新的promise。這個(gè)...
摘要:的異步完成整個(gè)異步環(huán)節(jié)的有事件循環(huán)觀察者請求對象以及線程池。執(zhí)行回調(diào)組裝好請求對象送入線程池等待執(zhí)行,實(shí)際上是完成了異步的第一部分,回調(diào)通知是第二部分。異步編程是首個(gè)將異步大規(guī)模帶到應(yīng)用層面的平臺。 showImg(https://segmentfault.com/img/remote/1460000011303472); 本文首發(fā)在個(gè)人博客:http://muyunyun.cn/po...
摘要:方法完成回調(diào)注冊模式下,對象通過方法調(diào)用,注冊完成態(tài)和失敗態(tài)的回調(diào)函數(shù)。這些回調(diào)函數(shù)組成一個(gè)回調(diào)隊(duì)列,處理的值。調(diào)用實(shí)例的方法,能使注冊的回調(diào)隊(duì)列中的回調(diào)函數(shù)依次執(zhí)行。 之前寫了一篇關(guān)于ES6原生Promise的文章。近期又讀樸靈的《深入淺出Node》,里面介紹了一個(gè)Promise/Deferred模式。 Promise是解決異步問題的利器。它其實(shí)是一種模式。Promise有三種狀態(tài),...
閱讀 2858·2023-04-26 01:02
閱讀 1887·2021-11-17 09:38
閱讀 812·2021-09-22 15:54
閱讀 2915·2021-09-22 15:29
閱讀 906·2021-09-22 10:02
閱讀 3464·2019-08-30 15:54
閱讀 2026·2019-08-30 15:44
閱讀 1609·2019-08-26 13:46