摘要:方法完成回調(diào)注冊(cè)模式下,對(duì)象通過方法調(diào)用,注冊(cè)完成態(tài)和失敗態(tài)的回調(diào)函數(shù)。這些回調(diào)函數(shù)組成一個(gè)回調(diào)隊(duì)列,處理的值。調(diào)用實(shí)例的方法,能使注冊(cè)的回調(diào)隊(duì)列中的回調(diào)函數(shù)依次執(zhí)行。
之前寫了一篇關(guān)于ES6原生Promise的文章。近期又讀樸靈的《深入淺出Node》,里面介紹了一個(gè)Promise/Deferred模式。
Promise是解決異步問題的利器。它其實(shí)是一種模式。Promise有三種狀態(tài),未完成態(tài)、完成態(tài)、失敗態(tài),相信大家一定不陌生,Promise對(duì)象允許使用.then的形式,將回調(diào)放到IO操作等異步方法的主體之外,使代碼優(yōu)美不少。
下面我結(jié)合《深入淺出Node》,介紹一下如何用ES5實(shí)現(xiàn)Promise/Deferred模式。相信研究完該實(shí)現(xiàn)代碼之后,我們會(huì)對(duì)Promise的理解更進(jìn)一步。
Promisethen方法完成回調(diào)注冊(cè)
Promise/Deferred模式下,Promise對(duì)象通過then方法調(diào)用,注冊(cè)完成態(tài)和失敗態(tài)的回調(diào)函數(shù)。
由于then方法支持鏈?zhǔn)交卣{(diào),因此then方法的返回值一定也是Promise對(duì)象,我們?cè)诖撕?jiǎn)單的返回自身,也就是this。
那么一定有人要問了:then中的回調(diào)函數(shù),可能返回一個(gè)新的Promise對(duì)象,此后的then調(diào)用是否是在新的Promise對(duì)象上調(diào)用的呢?
答案是:不一定。
這個(gè)問題其實(shí)困擾我很久,直到看了Promise的實(shí)現(xiàn)代碼我才想明白。其實(shí)then方法的調(diào)用,只不過是注冊(cè)了完成態(tài)和失敗態(tài)下的回調(diào)函數(shù)而已。這些回調(diào)函數(shù)組成一個(gè)回調(diào)隊(duì)列,處理resolve的值。
Promise構(gòu)造函數(shù)注冊(cè)回調(diào)隊(duì)列
Promise構(gòu)造函數(shù),給每個(gè)實(shí)例一個(gè)queue屬性,將then方法注冊(cè)的回調(diào)隊(duì)列,保存在Promise實(shí)例的回調(diào)隊(duì)列中。
代碼
var Promise = function(){ this.queue = []; } Promise.prototype.then = function(fulfilledHandler, unfulfilledHandler){ var handler = {}; if (typeof fulfilledHandler === "function"){ handler.fulfilled = fulfilledHandler; } if (typeof unfulfilledHandler === "function"){ handler.unfulfilled = unfulfilledHandler; } this.queue.push(handler); return this; }
我們看到,Promise的代碼很簡(jiǎn)單,只是通過then方法將一系列的回調(diào)函數(shù)push到隊(duì)列中而已。Promise實(shí)例暴露給用戶的也只有一個(gè)then方法。
這樣我們就可以這樣調(diào)用了:
promise.then(fulfilledFunc1, unfulfilledFunc1) .then(fulfilledFunc2, unfulfilledFunc2) .then(fulfilledFunc3, unfulfilledFunc3)
那么如何進(jìn)行狀態(tài)轉(zhuǎn)換呢?下面我就來講一下帶有resolve方法(reject方法同理,下面均以resolve舉例)的Deferred。
DeferredDeferred實(shí)例決定Promise實(shí)例的狀態(tài)
每個(gè)Deferred實(shí)例的對(duì)應(yīng)一個(gè)Promise實(shí)例。調(diào)用Deferred實(shí)例的resolve方法,能使Promise注冊(cè)的回調(diào)隊(duì)列中的回調(diào)函數(shù)依次執(zhí)行。
先寫部分代碼:
var Deferred = function(){ this.promise = new Promise(); } Deferred.protoype.resolve = function(val){ var handler, value = val; while(handler = this.promise.queue.shift()){ if (handler && handler.fulfilled){ value = handler.fulfiller(value) && value; } } }
這樣我們就能使用Deferred實(shí)例返回Promise實(shí)例,并且使用Deferred實(shí)例的resolve方法來觸發(fā)Promise實(shí)例的完成態(tài)回調(diào),并且將上一個(gè)回調(diào)如果有返回值,我們將該返回值作為新的resolve值傳遞給后面的回調(diào)。
處理回調(diào)方法返回的Promise實(shí)例
根據(jù)Promise模式,回調(diào)函數(shù)返回Promise實(shí)例時(shí),下一個(gè)then()中的回調(diào)處理的是新的Promise實(shí)例。
在之前的代碼實(shí)現(xiàn)中,then方法注冊(cè)了一系列的回調(diào)函數(shù),這些回調(diào)函數(shù)應(yīng)該處理新的promise實(shí)例。這里我們用了一個(gè)小技巧,見代碼:
Deferred.protoype.resolve = function(val){ var handler, value = val; while(handler = this.promise.queue.shift()){ if (handler && handler.fulfilled){ value = handler.fulfiller(value) && value; // 修改之處在這里: if (value && value.isPromise){ value.queue = this.promise.queue; // 最后再加一個(gè)小技巧 this.promise = value; return; } } } }
我們將返回promise實(shí)例之后的回調(diào)列表原封不動(dòng)的注冊(cè)到返回的promise中,這樣就保證之前then注冊(cè)的回調(diào)隊(duì)列能繼續(xù)調(diào)用。最后的小技巧可以使舊的deferred實(shí)例對(duì)應(yīng)新的promise實(shí)例,這樣可以繼續(xù)使用deferred.resolve方法。
為了判斷實(shí)例是否是Promise實(shí)例,這里簡(jiǎn)單的修改Promise構(gòu)造函數(shù):
var Promise = function(){ this.queue = []; this.isPromise = true; }
封裝callback方法用于異步調(diào)用
Promise之所以是解決異步的利器,一方面是then方法的鏈?zhǔn)秸{(diào)用,一方面也是因?yàn)?b>resolve方法可以異步調(diào)用,觸發(fā)回調(diào)隊(duì)列。
由于以NodeJS為標(biāo)志的異步方法其回調(diào)函數(shù)類似于這樣:
asyncFunction(param, function(err, data){ // do something... });
我們可以封裝一個(gè)自己的callback方法,用于異步觸發(fā)resolve方法。
Deferred.prototype.callback = function(err, data){ if (err){ this.reject(err); } this.resolve(data); }
此后我們可以這樣promisify一個(gè)異步函數(shù):
var async = function(param){ var defer = new Deferred(); var args = Array.prototype.silce.call(arguments); args.push(defer.callback); asyncFunc.apply(null, args); return defer.promise; }Promisify
由上面的promisify思路,我們寫一個(gè)更一般化的promisify函數(shù):
var promisify = function(method){ return function(){ var defer = new Deferred(); var args = Array.prototype.silce.call(arguments, 1); args.push(defer.callback); asyncFunc.apply(null, args); return defer.promise; } }
舉一個(gè)Node中文件操作的例子:
readFile = promisify(fs.readFile); readFile("file1.txt", "utf8").then(function(file1){ return readFile(file1.trim(), "utf8"); }).then(function(file2){ console.log(file2); })
倆字:優(yōu)雅。
結(jié)束文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/80181.html
摘要:隨著前端的發(fā)展,異步這個(gè)詞真是越來越常見了。真正帶來革命性改變的是規(guī)范。借助,我們可以這樣完成異步任務(wù)好棒寫起來像同步處理的函數(shù)一樣別著急,少年??偨Y(jié)以上就是筆者總結(jié)的幾種異步編程模式。 隨著前端的發(fā)展,異步這個(gè)詞真是越來越常見了。假設(shè)我們現(xiàn)在有這么一個(gè)異步任務(wù): 向服務(wù)器發(fā)起數(shù)次請(qǐng)求,每次請(qǐng)求的結(jié)果作為下次請(qǐng)求的參數(shù)。 來看看我們都有哪些處理方法: Callbacks ...
摘要:以前其實(shí)寫過一篇和的對(duì)比但是后來發(fā)現(xiàn)里面有不少謬誤所以一直惦記著糾正一下之前的錯(cuò)誤尤其關(guān)于中間件部分的對(duì)比這里的就拿更加簡(jiǎn)單的代替的執(zhí)行流程通常我們都說的中間件模型是線性的也就是一個(gè)一個(gè)往下執(zhí)行的如下圖這么說當(dāng)然是沒錯(cuò)的但是當(dāng)我們執(zhí)行下面代 以前其實(shí)寫過一篇express和koa的對(duì)比, 但是后來發(fā)現(xiàn)里面有不少謬誤. 所以一直惦記著糾正一下之前的錯(cuò)誤, 尤其關(guān)于中間件部分的對(duì)比. 這里...
摘要:前言前幾天在理解的事件環(huán)機(jī)制中引發(fā)了我對(duì)瀏覽器里的好奇。接下來理解瀏覽器中的,先看一張圖堆和棧堆是用戶主動(dòng)請(qǐng)求而劃分出來的內(nèi)存區(qū)域,比如你,就是將一個(gè)對(duì)象存入堆中,可以理解為存對(duì)象。廢話不多說,直接上圖個(gè)人理解。參考資料運(yùn)行機(jī)制詳解再談 前言 前幾天在理解node的事件環(huán)機(jī)制中引發(fā)了我對(duì)瀏覽器里Event Loop的好奇。我們都知道javascript是單線程的,任務(wù)是需要一個(gè)一個(gè)按順...
摘要:一直以來,對(duì)的執(zhí)行機(jī)制都是模棱兩可,知道今天看了文章這一次,徹底弄懂執(zhí)行機(jī)制和的規(guī)范和實(shí)現(xiàn),才對(duì)的執(zhí)行機(jī)制有了深入的理解,下面是我的學(xué)習(xí)總結(jié)。個(gè)要點(diǎn)是單線程語言是的執(zhí)行機(jī)制,為了實(shí)現(xiàn)主線程的不阻塞,就這么誕生了。 一直以來,對(duì)JS的執(zhí)行機(jī)制都是模棱兩可,知道今天看了文章—《這一次,徹底弄懂JavaScript執(zhí)行機(jī)制》和《Event Loop的規(guī)范和實(shí)現(xiàn)》,才對(duì)JS的執(zhí)行機(jī)制有了深入的...
摘要:眾所周知和都屬于上述異步任務(wù)的一種那到底為什么和會(huì)有順序之分這就是我想分析總結(jié)的問題所在了和的作用是為了讓瀏覽器能夠從內(nèi)部獲取的內(nèi)容并確保執(zhí)行棧能夠順序進(jìn)行。只要執(zhí)行棧沒有其他在執(zhí)行,在每個(gè)結(jié)束時(shí),隊(duì)列就會(huì)在回調(diào)后處理。 前言 我是在做前端面試題中看到了setTimeout和Promise的比較,然后第一次看到了microtask和macrotask的概念,在閱讀了一些文章之后發(fā)現(xiàn)沒有...
閱讀 3043·2021-09-10 10:51
閱讀 2406·2021-09-02 15:21
閱讀 3368·2019-08-30 15:44
閱讀 1041·2019-08-29 18:34
閱讀 1810·2019-08-29 13:15
閱讀 3475·2019-08-26 11:37
閱讀 2840·2019-08-26 10:46
閱讀 1253·2019-08-26 10:26