亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何用promsie實現觀察者模式

發布時間:2022-01-05 16:11:29 來源:億速云 閱讀:169 作者:iii 欄目:大數據

這篇文章主要介紹“如何用promsie實現觀察者模式”,在日常操作中,相信很多人在如何用promsie實現觀察者模式問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”如何用promsie實現觀察者模式”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

下面我們用一個示例來演示一下什么是觀察者模式,有這樣一個場景,在一個院子里,有一個小偷,和若干條狗,小偷只要一行動,狗就會叫,這個場景如果用圖來展示的話如圖:


如何用promsie實現觀察者模式

我們看到狗叫的動作是依賴小偷的,如果小偷不行動,狗是不會叫的,也就是說狗的叫的狀態依賴小偷的行動,小偷的行動狀態發生變化,依賴小偷的狗都會受到影響,從而發出叫聲。

這個場景用代碼來展示的話如下:
// 第一版class Thief {    constructor(){
   }    // thief的方法,調用dog的方法;    action(){        dog1.call()        dog2.call()        dog3.call()    }}
class Dog {    call(){        console.log("狗叫")    }}
let dog1 = new Dog()let dog2 = new Dog()let dog3 = new Dog()let thief = new Thief();thief.action()
上面的代碼中,小偷調用action方法的時候,其內部會分別調用每條狗的call方法。這段代碼有個明顯的缺點,對象耦合,不方便維護,假如需求中增加了一條狗,此時如何更改代碼呢?代碼如下:
// 第一版-新增dog4class Thief {    constructor() {
   }    // thief的方法,調用dog的方法;    action() {        dog1.call()        dog2.call()        dog3.call()        // 新增代碼        dog4.call()    }}
class Dog {    call() {        console.log("狗叫")    }}
let dog1 = new Dog()let dog2 = new Dog()let dog3 = new Dog()// 新增代碼:let dog4 = new Dog()
let thief = new Thief();thief.action()
觀察代碼,我們增加了dog4,然后在小偷的action方法中,再增加don4.call的調用,對象間存在了互相調用的耦合,這樣的代碼非常不便于后期維護,因為每次增加dog都需要去更改thief的代碼。  
那有沒有另外一種代碼的書寫方式,增加dog,但是不修改thief的代碼,同樣達到上面的效果呢?

下面我們用觀察者模式來改寫這段代碼,在改寫之前,先來了解一下觀察者模式的特點,我們再次回顧一下文中對觀察者模式的介紹:"觀察者模式定義了一種依賴關系,當某一個對象的狀態發生變化,其它依賴這個對象的對象都會受到影響"。

仔細閱讀我們發現觀察者模式中一般會存在觀察者和被觀察者,通常被觀察者是少數一方(并不固定,為了方便先這樣理解)。

上面的例子中,小偷是少數一方,只有一個。小偷明顯是被觀察者,狗是觀察者,被觀察者通常會有兩個方法和一個屬性,一個方法叫做subscribe,這個方法用來收集觀察者或者觀察者的行為,另外一個方法叫做publish,用來發布消息,還有一個屬性list,這個屬性通常是一個數組,用來存儲觀察者或者觀察者的行為。

下面我們用觀察者模式來改寫上面的代碼,代碼如下:
// 第二版// 1、thief增加了list屬性,是一個數組// 2、subscrible方法,追加方法// 3、publish 發布消息class Thief {    constructor() {        this.list = []    }    //     subscrible(call) {        this.list.push(call)    }    // publish遍歷數組,調用所有方法。    publish() {        for (let i = 0; i < this.list.length; i++) {            this.list[i]()        }    }    // thief的方法內部不會直接調用dog的方法了,    // 而是調用publish    action() {        this.publish()    }}class Dog {    call() {        console.log("狗叫")    }}
let thief = new Thief();let dog1 = new Dog()thief.subscrible(dog1.call)// 每增加一條狗就將狗的call方法追加到list
let dog2 = new Dog()thief.subscrible(dog2.call)let dog3 = new Dog()thief.subscrible(dog3.call)thief.action()
仔細閱讀代碼,我們首先重新定義了Thief類,并為其添加了subscribe方法、publish方法、list屬性,并重新定義了dog。然后我們用thief的subscribe方法收集dog的call方法,將其添加到小偷的list屬性中。當小偷調用action時,其內部調用publish方法,publish會遍歷執行list數組中的方法。

這段代碼相較于上一段代碼就比較方便維護了,假如我們在這個基礎上再添加一條狗,代碼如下:
// 第二版,新增dog4// 1、thief增加了list屬性,是一個數組// 2、subscrible方法,追加方法// 3、publish 發布消息class Thief {    constructor() {        this.list = []    }    //     subscrible(call){        this.list.push(call)    }    // publish遍歷數組,調用所有方法。    publish(){        for(let i= 0 ;i<this.list.length;i++){            this.list[i]()        }    }    // thief的方法內部不會直接調用dog的方法了,    // 而是調用publish    action() {       this.publish()    }}class Dog {    call() {        console.log("狗叫")    }}
let thief = new Thief();let dog1 = new Dog()thief.subscrible(dog1.call)// 每增加一條狗就將狗的call方法追加到list
let dog2 = new Dog()thief.subscrible(dog2.call)let dog3 = new Dog()thief.subscrible(dog3.call)// 增加代碼:let dog4 = new Dog()thief.subscrible(dog4.call)thief.action()
我們看到,代碼中第41行增加dog4,然后調用thief的scrible收集狗的call方法,此時我們調用thief的publish方法,依然能調用所有dog的call方法,但是我們沒有修改thief內部的代碼,非常優雅的完成了需求,但是如果需求是再增加一個小偷呢?此時代碼是什么樣的呢?代碼如下:
// 第二版,新增thiefclass Thief {    constructor() {        this.list = []    }    //     subscrible(call){        this.list.push(call)    }    // publish遍歷數組,調用所有方法。    publish(){        for(let i= 0 ;i<this.list.length;i++){            this.list[i]()        }    }    // thief的方法內部不會直接調用dog的方法了,    // 而是調用publish    action() {       this.publish()    }}class Dog {    call() {        console.log("狗叫")    }}
let thief = new Thief();// 新增thief代碼let thief1 = new Thief()
let dog1 = new Dog()thief.subscrible(dog1.call)// 新增代碼thief1.subscrible(dog1.call)let dog2 = new Dog()thief.subscrible(dog2.call)// 新增代碼thief1.subscrible(dog2.call)let dog3 = new Dog()thief.subscrible(dog3.call)// 新增代碼thief1.subscrible(dog3.call)
thief.action()// 新增代碼thief1.action()
看看代碼,我們在第30行新增了thief1對象,然后分別在第35、39、43行調用thief1的subsctible方法收集dog的call方法。

真是按下葫蘆起了瓢,能不能繼續優化呢,在使用觀察者模式的時候,我們可以將觀察者模式抽離出來,抽離成一個pubsub對象,這個對象有擁有兩個方法一個屬性,代碼如下:
class Pubsub{    constructor(){        this.list = []    }    subscrible(call){        this.list.push(call)    }    publish(){        for(let i= 0 ;i<this.list.length;i++){            this.list[i]()        }    }}
仔細閱讀源碼,我們只是將觀察者的一個屬性和兩個方法抽離出來封裝成了一個類,使用這個類時,實例化一下就可以了,然后用這個對象改寫上面的代碼:
let pubsub = new Pubsub();class Dog {    call() {        console.log("狗叫")    }}
class Thief {    constructor() {
   }    action() {        pubsub.publish()    }}
let thief = new Thief();let dog1 = new Dog()pubsub.subscrible(dog1.call)let dog2 = new Dog()pubsub.subscrible(dog2.call)let dog3 = new Dog()pubsub.subscrible(dog3.call)
thief.action()
觀察代碼,小偷在調用action時,不是直接調用狗的call方法,而是通過pubsub,并且收集狗的call方法,也是由pubsub來完成,完全將小偷和狗解耦了。然后我們在添加一個dog4和一個thief1,代碼如下:
let pubsub = new Pubsub();class Dog {    call() {        console.log("狗叫")    }}
class Thief {    constructor() {
   }    action() {        pubsub.publish()    }}
let thief = new Thief();
// 新增thief1代碼let thief1 = new Thief();
let dog1 = new Dog()pubsub.subscrible(dog1.call)let dog2 = new Dog()pubsub.subscrible(dog2.call)let dog3 = new Dog()pubsub.subscrible(dog3.call)
// 新增dog4代碼let dog4 = new Dog()pubsub.subscrible(dog4.call)
thief.action()

仔細閱讀源碼,第20行和第30行分別添加了thief1和dog4,依然能夠實現小偷偷東西,狗會叫的功能,并且不會去修改thief和dog內部的代碼,實現了對象之間的解耦。

觀察者模式也可以叫做訂閱發布模式,本質是一種消息機制,用這種機制我們可以解耦代碼中對象互相調用。

第三版代碼,我們可以用如下圖示來理解:

如何用promsie實現觀察者模式

觀察上圖,第三版中圖片第一張圖多了一個pubsub,我們用一個衛星來代替pubsub,這個版本也比較好維護,添加刪除thief或者dog都不會影響到對象。我們在前端應用中使用的redux和vuex都運用了觀察者模式,或者叫做訂閱者模式,其運行原理也如上圖。

文章寫到這里,觀察者模式基本就聊完了,但是我在觀察pubsub這個對象的時候突然想到了promsie,promise天生就是觀察者模式,我們可以用promise來改造一下pubsub,代碼如下:
class Pubsub {    constructor() {        let promise = new Promise((resolve,reject)=>{            this.resolve = resolve;        })        this.promise = promise;    }    subscrible(call) {       this.promise.then(call)    }    publish() {        this.resolve();    }}
Promise天然支持觀察者模式,我們將其改造一下,改造成一個Pubsub類,與我們前面實現的Pubsub類效果是一樣的。

首先我們在構造函數內部實例化一個promise,并且將這個promsie的resolve的控制權轉交到this的resolve屬性上。前面寫過一篇文章  如何取消promise的調用  ,在這篇文章中我們介紹了如何獲取promise的控制權。大家有興趣可以去看一看。

回歸正題,我們用promise改寫的pubsub來測試下上面的案例,代碼如下:
class Pubsub {    constructor() {        let promise = new Promise((resolve,reject)=>{            this.resolve = resolve;        })        this.promise = promise;    }    subscrible(call) {       this.promise.then(call)    }    publish() {        this.resolve();    }}
let pubsub = new Pubsub();class Dog {    call() {        console.log("狗叫")    }}
class Thief {    constructor() {
   }    action() {        pubsub.publish()    }}
let thief = new Thief();
// 新增thief1代碼let thief1 = new Thief();
let dog1 = new Dog()pubsub.subscrible(dog1.call)let dog2 = new Dog()pubsub.subscrible(dog2.call)let dog3 = new Dog()pubsub.subscrible(dog3.call)
// 新增dog4代碼let dog4 = new Dog()pubsub.subscrible(dog4.call)
thief.action()
測試代碼,我們發現用promise改造的pubsub也能很好的實現觀察者模式,這里我們利用了promise的兩個知識點,一個是promise的then方法,then方法可以無限追加函數。另外一個是我們得到promise的resolve的控制權,從而控制promise的then鏈的執行時機。

講到這里填一下前面文章挖的坑,前面的  如何取消ajax請求的回調  的文章中我們留了一個坑,axios實現取消ajax請求的回調的原理,我們可以回顧下使用axios時如何取消回調,代碼如下:  
const axios = require('axios')// 1、獲取CancelTokenvar CancelToken = axios.CancelToken;// 2、生成sourcevar source = CancelToken.source();console.log(source.token)axios.get('/user/12345', {//get請求在第二個參數    // 3、注入source.token    cancelToken: source.token}).catch(function (thrown) {    console.log(thrown)});axios.post('/user/12345', {//post請求在第三個參數    name: 'new name'}, {    cancelToken: source.token}).catch(e => {    console.log(e)});// 4、調用source.cancel("原因"),終止注入了source.token的請求source.cancel('不想請求了');
閱讀代碼,在第一步和第二步中,我們通過調用axios.CancelToken.source方法得到了一個source對象,第三步中我們在axios調用異步請求時傳遞cancelToken參數,第四步,在合適的時機調用source.cancle方法取消回調。

我們先看一下CancelToken這個靜態方法的代碼是如何的:
'use strict';var Cancel = require('./Cancel');/** * A `CancelToken` is an object that can be used to request cancellation of an operation. * * @class * @param {Function} executor The executor function. */function CancelToken(executor) {    if (typeof executor !== 'function') {        throw new TypeError('executor must be a function.');    }    var resolvePromise;    this.promise = new Promise(function promiseExecutor(resolve) {        resolvePromise = resolve;    });    var token = this;    executor(function cancel(message) {        if (token.reason) {            // Cancellation has already been requested            return;        }        token.reason = new Cancel(message);        resolvePromise(token.reason);    });}/** * Throws a `Cancel` if cancellation has been requested. */CancelToken.prototype.throwIfRequested = function throwIfRequested() {    if (this.reason) {        throw this.reason;    }};/** * Returns an object that contains a new `CancelToken` and a function that, when called, * cancels the `CancelToken`. */CancelToken.source = function source() {    var cancel;    var token = new CancelToken(function executor(c) {        cancel = c;    });    return {        token: token,        cancel: cancel    };};module.exports = CancelToken;
為了直觀一些我們將注釋和一些基礎條件判斷去除后,代碼如下:
function CancelToken(executor) {
   var resolvePromise;    this.promise = new Promise(function promiseExecutor(resolve) {        resolvePromise = resolve;    });    var token = this;    executor(function cancel(message) {        if (token.reason) {            return;        }        token.reason = message        resolvePromise(token.reason);    });}
CancelToken.source = function source() {    var cancel;    var token = new CancelToken(function executor(c) {        cancel = c;    });    return {        token: token,        cancel: cancel    };};
閱讀源碼,我們發現CancelToken是一個類,其構造函數需要傳遞一個參數,這個參數必須是一個函數,CancelToken通過調用source方法來實例化一個對象。

在CancelToken的構造函數中,實例化一個Promise對象,通過在Promise的外部定義ResolvePromise變量,值實例化promise的時候獲取了Promise實例resolve的控制權,然后將控制權封裝到cancel函數中,在將cancel函數交給CancelToken構造函數的參數executor函數。

CancelToken在調用cancel方法時,先實例化CancelToken,在實例化過程中,我們將cancel交給了變量cancel,最后將CancelToken的實例token和cancel方法返回出去。

token的實質就是一個promise對象,而cancel方法內部則保存了這個promise的resolve方法。所有我們可以通過cancel來控制promise對象的執行。

接著我們再看一下axios中配置cancelToken參數的核心代碼:
if (config.cancelToken) {    // Handle cancellation    config.cancelToken.promise.then(function onCanceled(cancel) {        if (!request) {            return;        }        request.abort();        reject(cancel);        // Clean up request        request = null;    });}
閱讀源碼,我們發現,當axios發送異步請求配置了acncelToken參數后,axios內部會執行一段代碼:
config.cancelToken.promise.then(function onCanceled(cancel) {    if (!request) {        return;    }    request.abort();    reject(cancel);    // Clean up request    request = null;});
這段代碼會調用傳入的axios的cancelToken的promise.then的執行,但是這個promise.then的執行的控制權在cancel函數中,如果我們在這個異步請求的返回前,我們調用了cancle函數就會執行promise.then從而執行request.abort來取消回調。

axios取消異步回調的原理涉及到了兩個知識點,首先是利用了xmlhttprequest的abort方法修改readystate的值,其次利用了觀察值模式,只不過這個觀察者模式用的是promise來實現的。

到此,關于“如何用promsie實現觀察者模式”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

舞钢市| 兴仁县| 岑巩县| 招远市| 百色市| 江门市| 普宁市| 永城市| 潜山县| 方山县| 岳普湖县| 涞水县| 汉阴县| 丹江口市| 凉城县| 三穗县| 呼和浩特市| 水富县| 伊春市| 上高县| 杂多县| 兴国县| 普兰县| 青冈县| 乐平市| 丁青县| 敦煌市| 红桥区| 滦平县| 中西区| 正宁县| 威信县| 永州市| 临夏县| 陵川县| 南开区| 潜江市| 象州县| 海宁市| 赤壁市| 耒阳市|