欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

異步編程之Promise(3):拓展進階

系統 2040 0

異步編程系列教程:

  1. (翻譯)異步編程之Promise(1)——初見魅力
  2. 異步編程之Promise(2):探究原理
  3. 異步編程之Promise(3):拓展進階
  4. 異步編程之Generator(1)——領略魅力
  5. 異步編程之Generator(2)——剖析特性
  6. 異步編程之co——源碼分析

拓展功能

在前面的文章中,通過了解promise能做什么,實踐動手從原理上了解 promise/deferred 模式的用法,相信大家應該更期待這次的功能拓展。我們不僅需要讓單異步操作promise化,我們還需要從實際出發,拓展更多有用的功能。直接看一下我們這一次需要做的兩個功能:

  1. 多異步并行控制
  2. 多異步串行隊列

這兩個功能用我們之前自己寫的簡陋promise庫,是無法做到的。我們不能在指定多個promise異步完成后,再觸發回調。也不能讓多個promise異步像排隊一樣,一個一個的進行,甚至下一個promise的參數是依賴上一個promise的。這就是我們接下來需要解決的問題:

多異步并行控制

在凍手之前,我們先想一想大致的思路吧。首先我們肯定是并發了多個異步,我們需要做的僅僅就是,監控所有并發的異步,并讓最后一個異步觸發 resolve 回調函數。當然錯誤處理的話,就是當有一個異步錯誤,直接就 reject 掉宣布異步失敗結束。一般監視并發,我們都會有一個哨兵變量,每完成一個異步,就對哨兵進行維護并檢測異步是否結束。

那我們的API應該怎么設置呢?樸靈老師的書上是這樣的: deferred.all([promise1, promise2]).then() 。從這里我們可以看出,就是由各個小promise組成了一個大的promise,并在大promise中進行接下來的操作。一起看一下代碼吧:

    
      Deferred.prototype.all = function(promises){
    var result = []; // 存儲各個promise的執行結果
    var count = promises.length; // 哨兵變量
    var _this = this;
    promises.forEach(function(promise, index){
        promise.then(function(res){
            result[index] = res;
            count--;
            // 當執行最后一個promise后, 調用大promise的resolve,并把result傳進去
            while(count === 0){
                _this.resolve(result);
            }
        }, function(err){
        	  // 有一個promise出錯,立即return并執行大promise的reject
            return _this.reject(err);
        });
    });
    return this.promise;
};
    
  

我個人認為最不好懂的應該是 _this 到底指的是什么?看過上一篇的朋友,應該知道deferred是延遲對象來的,作用就是觸發即將在 then() 中綁定的 resolve() reject() 。那這里的 _this 必然是指大的promise,我們看一下如何使用的:

    
      // 已經定義好Promise化的readFile(),不懂的同學可以翻閱上一篇文章。
// 這段代碼是輸出兩個文件里,字符串length最大的值。
var r1 = readFile("hello.txt", 'utf-8');
var r2 = readFile("hello2.txt", 'utf-8');

var deferred = new Deferred(); // 初始化一個延時對象。
deferred.all([r1, r2]).then(function(res){
    console.log(res);
    res = res.map(function(item){return item.length});
    console.log(Math.max.apply(null, res));
});
    
  

That's easy, right?! 我們這里僅僅是實現原理,是不成熟的,若實際使用中,更推薦Q.js。現在我們將需要并行的promise放到一個數組里,不出錯就會得到每一次并行的結果,并存儲在 result 中,最后返回得到并進行相應處理。當然我們也可以很清楚感受到它的局限,并行的promise是相互獨立無依賴的。當多個異步開始有依賴了,我們該怎么做呢?這就是我們接下來要討論的。

多異步串行隊列

一般來說,多異步串行執行,通過最簡單的嵌套回調即可解決。但我們可以想象,我們最終的理想形態應該是鏈式結構的。 res 依賴以上的步驟,我們通過鏈式結構可以更清晰易懂,有助于我們進行流程控制。

    
      --------嵌套回調---------
api1(function(v1){
    api2(function(v1, v2){
        api3(function(v2, v3){
            api4(function(v3, res){
                callback(res);
            })
        })
    })
});
--------鏈式調用---------
promise()
	.then(api1)
	.then(api2)
	.then(api3)
	.then(function(res){
		// 用res來做一些事情
	})
    
  

還是從想開始,我們需要做到promise支持鏈式執行,第一感覺的數據結構就是隊列,就是那個FIFO先進先出的隊列。我們將所有的回調都壓入隊列中,完成一個就取一個出來執行。但是更關鍵的問題在于,前面一個promise的值,如何傳到下一個promise中。樸靈大大在這里給出的解決方案是: Promise執行回調時,一旦檢測到返回的是新的Promise對象,會將當前Deferred延遲對象中的promise引用換成新的Promise對象。而那個回調隊列,也同樣轉移到了新Promise上。

不知道大家有沒有聽懂大概個意思,如果還是不太清楚,我們可以思考一下,再對比一下實現的代碼,就應該能看懂了。這次我們需要對以往的代碼,做一個較大的改變,我們不再使用 events.EventEmitter 來進行事件觸發了。為了能鏈式的調用回調,我們會將事件觸發放在數組隊列里,并按順序進行觸發。因為代碼進行了較大的改變,我們逐個逐個看代碼。

    
      var Promise = function(){
    this.isPromise = true; // 用于確定是promise對象
    this.queue = [];       // 回調事件的隊列
};
Promise.prototype.then = function(resolve, reject){
    var handler = {};
    if(typeof resolve === 'function'){
        handler.resolve = resolve;
    }
    if(typeof reject === 'function'){
        handler.reject = reject;
    }
    this.queue.push(handler); // 將回調事件推入到數組隊列中
    return this;
};
    
  

這一段代碼,我們最重要的是定義了一個 queue 屬性。它是用來存放在 then(resolve, reject) 中的 resolve reject 方法的。最后我們會將一次promise的回調函數,推入到 queue 屬性里,以供deffered延遲對象使用。

    
      var Deferred = function(){
    this.promise = new Promise();
};
Deferred.prototype.resolve = function(data){
    var handler; //用于存放當前的回調
    // 若隊列存在回調
    while(handler = this.promise.queue.shift()){
        if(handler && handler.resolve){
            var ret = handler.resolve(data);
            if(ret && ret.isPromise){
                ret.queue = this.promise.queue;
                this.promise = ret;
                return;
            }
        }
    }
};
Deferred.prototype.reject = function(err){
    var handler; //用于存放當前的回調
    // 若隊列存在回調
    while(handler = this.promise.queue.shift()){
        if(handler && handler.reject){
            var ret = handler.reject(err);
            if(ret && ret.isPromise){
                ret.queue = this.promise.queue;
                this.promise = ret;
                return;
            }
        }
    }
};
Deferred.prototype.makeNodeResolver = function(){
    var _this = this;
    return function(err, res){
        if(err) return _this.reject(err);
        _this.resolve(res);
    }
};
    
  

這里,和以往一樣,每一個deferred對象都會有一個promise對象。并且重新定義了 resolve reject 的實現,不再和以往一樣,簡單的通過觸發事件實現。我們仔細分析一下,到底deffered對象的方法做了些什么。我們就取其中一個 resolve 來看,首先我們將隊列promise的回調隊列 queue 最前端的handler推出來,若存在就執行回調。若回調執行的結果是一個新的promise(我們通過isPromise屬性判斷),我們就會進行一個替換。這里是實現的關鍵,我們將原來那個promise的 queue 屬性存到新的新的promise上,然后將deferred對象當前的promise變成新的promise,最后返回出來。通過這一系列的操作,我們就可以將回調隊列進行傳遞,并實現鏈式調用。

    
      --------hello.txt---------
data.json

--------data.json---------
{"message": "Hello World!"}

--------代碼應用---------
var fs = require('fs');

var readFile = function(file){
    var deferred = new Deferred();
    fs.readFile(file, 'utf-8', deferred.makeNodeResolver());
    return deferred.promise;
};
var readJSON = function(file){
    var deferred = new Deferred();
    fs.readFile(file, 'utf-8', function(err, file){
        if(err) return deferred.reject(err);
        deferred.resolve(JSON.parse(file));
    });
    return deferred.promise;
};

readFile('hello.txt').then(function(file){
    return readJSON(file);
}).then(function(data){
    console.log(data.message);
});
    
// 或者利用更簡潔的特性
readFile('hello.txt').then(readJSON).then(function(data){
	console.log(data.message); // hello world!
});
    
  

最后這段代碼是我們多異步并行隊列的實際應用。我們定義了兩個promise化的異步方法,一個是readFile,一個readJSON。我們的readJSON函數是依賴readFile的結果的,最后我們一樣實現了需求。我們這次也僅僅是研究原理實現的代碼,是不成熟的。在實際應用中,還是需要借助成熟的框架Q.js等。

API promise化的封裝

我們可以發現,為了使代碼實現promise,我們需要為現有的異步api都進行一次封裝。為了某些特殊情況,我們可以自己動手用promise/deferred模式,進行手動封裝實現功能。然后很多現有的API,我們是可以從中抽象出相同的部分,借助函數柯里化,進行批量promise轉化的。

    
      var wrapPromise = function(api){
    return function(){
        var deferred = new Deferred();
        var args = [].slice.call(arguments, 0);
        args.push(deferred.makeNodeResolver());
        api.apply(null, args);
        return deferred.promise;
    };
};
var fs = require('fs');
var readFile = wrapPromise(fs.readFile);
    
  

我們通過 wrapPromise(api) ,將實現的細節隱藏在內部,變化的僅僅是需要promise化的api。其實內部實現的細節也是很簡單可以看懂的,就是將promise化后的參數取出來,再多加一個node傳統形式的回調,一同apply進api中。我們通過簡單的wrapPromise直接得到一個promise化的異步api。

總結

到此,promise三部曲,總算是講完了。在我總結寫blog時,也是做了比較多的思考,有些地方也可能表意不清。我們知道其實promise,其實是另一種形式的回調,只是它的形式我們更喜歡,也更自然。我們唯一會煩惱的是,我們需要為不同場景的異步api進行Promise化。但是為了更好的控制,我認為也是值得嘗試的。promise單獨使用,并不能體現它強大的地方。因為接下來我們會講promise和Generator配合,展現強大的異步編程能力。

異步編程之Promise(3):拓展進階


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 九九综合九九综合 | 一级毛片国产真人永久在线 | 性夜黄a爽影免费看 | 国产精品久久久久久久免费大片 | 正在播放国产精品 | 色婷婷久久久 | 啊啊啊好紧好爽 | 久久久久国产精品 | 国产成人精品久久二区二区91 | 成人精品免费视频 | 91免费永久国产在线观看 | 日本视频在线免费 | 欧美一级特黄aaaaaaa在线观看 | 日韩中文字幕 | 一区二区三区亚洲 | av中文字幕在线观看 | 欧洲一级毛片 | 国产日韩一区二区三区在线观看 | 欧美激情午夜 | 欧美一级片毛片 | 精品视频手机在线观看免费 | 午夜在线免费观看视频 | 九九影院理论片 | www.qubook.| 日韩一区二区在线视频 | 中文字幕一区二区三区四区五区 | 超碰97av 在线人人操 | 在线视频成人 | 三级特黄 | 999精品免费视频观看 | 亚洲午夜精品一区二区蜜桃 | 亚洲高清视频一区二区 | 在线观看国产免费高清不卡 | 污网站观看 | 国产成人精品福利站 | 91青青草视频 | 欧美老妇交乱视频 | 中文字幕在线免费观看 | 狠狠干美女 | 日韩精品一区二区三区中文3d | 991av|