[译] 深切明白 Promise 五部曲:3. 牢靠性问题

原文地点:http://blog.getify.com/promis…

假如你须要遇上我们关于Promise的进度,可以看看这个系列前两篇文章深切明白Promise五部曲–1.异步题目深切明白Promise五部曲–2.控制权转移题目

Promise状况 == 信托

在前面,我们说清楚明了几个关于Promises怎样事变的要点,这些要点是我们之所以可以信托promise机制作为控制转移的一种处置惩罚方案的基础。

这些要点直接来自Promises/A+范例。任何当地完成或许polyfill或许库都必需经由过程一个周全严厉的测试来一定是不是相符范例。

关于promises牢靠性是最基础的,因为假如没有牢靠性,那末你就跟运用一般的回调一样了。你必需郑重地编写那些触及到异步挪用第三方库的代码。你必需本身来处置惩罚状况跟踪的题目然后确保第三方库不会出题目。

假如没有牢靠的promises你本身可以完成异步使命吗?固然可以。然则题目是,你本身没法处置惩罚得很圆满,你得把很多分外的变量加到你的代码中而且你会发生一个将来的庇护风险,代码会变得很难庇护。

Promises是被设想用来范例和集合这类逻辑的。你可以运用一个范例的promise体系而不必忧郁牢靠性题目,因为它会依据Promises机制来实行。

可依靠吗?

在理论上这个牢靠性保证合同听起来很棒。然则在JavaScript中真的有可以有这么一个机制吗?

牢靠性

在我最先说这个题目之前,我们起首消除一些JS代码中的牢靠性题目:

  1. 我们这里的议论跟暗码/加密中的“私有性”和“平安”无关。

  2. 和JS代码可以被用户经由过程检察源码看到无关。

  3. 和一个黑客可以侵入你的服务器来发送一些恶意代码或许经由过程中间人进击来挟制浏览器和服务器之间的衔接来完成一样的目标或许甚至在运行时运用XSS破绽来注入恶意代码无关。

  4. 同时,也和恶意代码一旦存在你的页面就可以理论上修正JavaScript运行时功用(比方经由过程修正Object.prototype或许Function.prototype)来损坏你的顺序这个现实无关。

  5. 类似的,和一些大意的代码可以会心外埠经由过程非规范的体式格局来修正规范JS函数无关。

  6. 末了,和假如你页面中依靠于第三方库那末他们的服务器,衔接和代码也会涌现上面所说的破绽无关。

如今我可以继续了,然则我以为你已找到症结点了。我们在经由过程一个假定来减少我们的议论局限:当一切的代码以及主机环境都在一种预期的平安的状况中时,你的顺序会怎样实行?

这并非说我们运用Promise所做的事变对上面这些题目没有协助。这仅仅是因为这些题目在一个更高的层面上—这些题目远离了编写API和形式,这些题目留给专家来议论。

在Promise状况下的牢靠性

我们看看下面这个例子:

var myPromise = {
    state: {
        status: 1,
        value: "Hello World"
    },
    then: function(success,failure) {
        // implement something like a thenable's behavior
    }
};

我可以新建一个像如许的对象,然后在日常平凡运用它而且说我在用promises。现实是,我可以再完美一下使它可以经由过程全部Promises/A+ 测试网站的测试。

然则我真的是运用了Promises吗?

你怎样回复这个题目比你意想到的更主要。在很多开发者社区中很多人的回复是,是的。
我很一定的说,不是!

为何?假如你经由过程了promises测试网站,那末它就是一个promise 了,不是吗?而且,它在一切状况下都依据范例来实行,不是吗?

不是

promises的精华远不是范例说的那末简朴,是牢靠性

牢靠性是一个promise就是一个状况(状况会从”pending”转变成”resolved”或许”rejected”个中一个)的容器,这些状况会附带一个效果值(胜利信息或许毛病信息)。牢靠性是一旦一个promise的状况变成”resolved”或许”rejected”,那末就不能转变也不会转变。牢靠性就是完成的promise是不可变的。

然则promises的精华另有一些更深条理的东西,这些是没法经由过程浏览范例看出来的:转变一个promise状况和设置它的完成值的才只存在于原始的promise的完成。也就是说这个才的完成控制在开发者手里。

范例的初期版本中,把resolve/reject的功用分离出来放在一个对象中,叫做Deferred。把这想成一个对象对:在建立的时刻,我们建立一个promise和一个deferred,deferred可以resolve这个promise。主要的是,这两个可以被离开,一部份代码可以resolve/reject一个promise而别的一部份只能监听这个变化然后做出回应。

范例的后续版本中简化了promises,经由过程删除deferred对象,取而代之的是简朴的暴露出本来属于deferred的resolve()reject()要领。

var p = new Promise( function(resolve,reject){
    // I have `resolve()` and `reject()` from the
    // hidden `deferred`, and I **alone** control
    // the state of the promise.
} );

// now, I can pass around `p` freely, and it can't
// be changed by anyone else but the creator.

看看之前的谁人myPromise对象。你注重到了什么吗?

var myPromise = {
    state: {
        status: 1,
        value: "Hello World"
    },
    then: function(success,failure) {
        // implement something like a thenable's behavior
    }
};

假如你随处通报myPromise,然后不论恶意代码照样不测的代码都可以转变myPromise.state.status或许myPromise.state.value属性,我们是不是是开了一个很大的后门,失去了Promises的牢靠性。

固然,答案是一定的。把状况暴露给要领使得这不是一个真正的promise。因为如今promise的保证已完整不牢靠了。

假如你从一个第三方库中得到了一个如许的对象,你不会信托它的,不是吗?更主要的,假如你把这个对象通报给其他第三方库,你一定不会置信只需原始的建立者才修正它,不是吗?

固然不会置信。那就太无邪了。

你看,运用promises是基于牢靠性的。然后牢靠性是基于promise的状况是与外部影响断绝的,只需建立者能转变。注重到我并没有说状况必需是私有的,只需它不会被外界转变就可以。

假如没有promise的对象不会被除了建立者转变的牢靠性,那末promise就险些失去了它的意义。

毛病的牢靠性?

注重,这正是事变变得隐约的处所,是不可无视的现实。

大多数为了在旧的JS环境下可以支撑promise的polyfill会把状况经由过程可变的体式格局暴露出来。

Ouch!!!

在这方面,我的ES6 Promise polyfill”Native Promise Only“没有把state暴露出来。据我所知,这是唯一一个没有把promise状况暴露出来的polyfill。
为何?因为我不单单议体贴Promise范例,我更在乎Promises的精华。

Tradeoffs

然则终究为何一切这些高度可托的Promise polyfill和库会忘了promise中这么主要的东西呢?因为在原生Javascript有一些限定,这是一些内置机制不须要遵照的。

简朴的说,行将到来的ES6规范指出Promise是一个“class”,所以作为一个“class”,promise必需可以被子类化。换句话说,你必需可以建立一个class CustomPromise extends Promise{..}子类,在这个基础上你可以扩大内置promises的功用。

比方,你须要一个自定义的promise,这个promise可以处置惩罚凌驾一条音讯。最少理论上,完成这个只须要你继续内置Promise类然后扩大它。

鉴于我对JS中类观点的私见,我以为Promise子类化是一种没有意义的闹剧或许转移注重力的幌子。我勤奋让本身想出一些Promise子类化的优点,但是我着实想不出来。

而且,假如要继续坚持一些特征来遵照Promises/A+ Test Suite,这些子类的完成极可以变得相称愚笨。

末了,我关于promise的子类化没有任何好感。

怎么办呢!?

不触及太多JS的细节,把Promise表杀青一个可以被继续的”class”须要你把实例要领加入到Promise.prototype对象中。

然则当你这么做的时刻,你就把then..()catch(..)变成同享要领,一切Promise实例都可以接见到,然后这些要领只能经由过程this接见每一个实例上的大众属性。

换句话说,假如要使得promise可以子类化,只运用简朴的JS是不可以的,必需运用闭包或其他要领来为每一个实例建立私有的promise状况。

我晓得如今你已最先想种种你见过的可以完成闭包私有和this大众继续夹杂的要领。

我可以写一整本书来讲明为何如许行不通,然则我这里就简朴的说下:不要管你所听到的,只运用ES5中可以运用的要领,你是不可以建立私有状况同时又可以有用子类化的promise。

这两个观点在ES5以下是相互排挤的。

Promise 减弱

另一个ES6中的新特征是WeakMap。简朴的说,一个WeakMap实例可以运用对象援用作为键,然后和一个数据相联系,而不须要真正把数据存储在对象上。

这正是我们须要的,不是吗?我们须要一个我们大众的then(..)catch(..)可以接见的WeakMap,不管this绑定的是什么,它们都可以依据this接见到而且查找对应的被庇护的状况值。这个特权Promise要领可以获得这个内部状况,然则外部不能。

不过,事变并没有这么优美:

  1. WeakMap基础不可以经由过程原生JS用机能可接受的要领完成。

  2. 就算我们在ES5及以下可以运用WeakMap,它照样没有完整处置惩罚子类化的题目,因为你必需隐蔽WeakMap实例使得只需你的Promise要领可以接见,然则如许的话另一个Promise的子类也能接见到。

假定我们可以处置惩罚第二个题目—实在我们不能,就做一个假定。那末WeakMap的完成应该是什么样的呢?

var WeakMap = function(){
    var objs = [], data = [];

    function findObj(obj) {
        for (var i=0; i<objs.length; i++) {
            if (objs[i] === obj) return i;
        }

        // not found, add it onto the end
        objs.push( obj );
        data.push( undefined );

        return i;
    }

    function __set(key,value) {
        var idx = findObj( key );
        data[idx] = value;
    }

    function __get(key) {
        var idx = findObj( key );
        return data[idx];
    }

    return {
        "set": __set,
        "get": __get
    };
};

var myMap = new WeakMap();
var myObj = {};

myMap.set( myObj, "foo" );

myObj.foo; // undefined

myMap.get( myObj ); // "foo"

OK,基础的头脑就是我们庇护两个数组(objsdata),经由过程下标相对应。在第一个数组中保存对象援用,在另一个保存数据。

美丽,不是吗?

看看机能怎样吧。看看findObj(..),它要轮回全部数组来找到响应的数据。援用越多机能就越低。

然则这还不是最坏的处所。WeakMap之所以叫做“Weak”是因为渣滓接纳行动。在我们WeakMap的完成中,会保存每一个对象的援用,这就意味着就算顺序已没有关于对象的援用了,这些对象照样不能被接纳。然则真正的WeakMap就是这么“weak”,所以你不须要做任何事变来优化渣滓接纳。

好的,WeakMap是一个毛病的愿望。它并没有处置惩罚ES6中的题目而且使得事变在ES5及以下变得更糟。

庇护state照样子类化?

这是个题目!

我真的愿望我能建立一个忠厚的Peomisepolyfill给ES5及以下。然则必需做一个挑选,在这里涌现了一个不合。要不就摒弃子类化的功用,要不就摒弃作为promise的牢靠性。

那末我们该怎么做呢?

总结

我会做另一个promise polyfill,这个polyfill挑选保存子类化的才,以可变的state为价值。

我已挑选了扬弃子类化使得我的promise polyfill可以很牢靠。就像我之前说的,我以为promise的子类化终究会被证实是一个虚有其表的东西。我不会捐躯promise的牢靠性来依从子类化。

很显然,其他人关于这个题目会有差别的意见。然则我只想让你问问你本身:一个不牢靠的promise可以用来干吗?什么代码能真正挽救你?什么代码可以做得更好?

现有的Promise polyfill和库的题目比不可变的state vs 子类化更深层面。在第四部份:扩大题目中,我会指出很多现有polyfill和库中的题目。

译者注

这篇文章不大好翻译也不大好明白,所以在这里总结下我的明白,愿望对人人的明白有所协助,假如人人有什么差别的意见,迎接议论。

这篇文章缭绕Promise的牢靠性睁开,Promise的牢靠性是它的精华地点。要完成Promise的牢靠性最症结的就是要保证Promise的状况值state不能被外部转变,如许才保证状况值的不可逆。

而如今险些一切的Promise库都疏忽了这个症结,而它们会疏忽这个症结点一个很主要的缘由就是在ES6的范例中,Promise被规定为一个类,也就是说Promise是可以被子类化的。然而在ES5及以下的范例中,在没有private症结字的状况下,是不可以完成可子类化同时又能保证Promise的状况值不会被外部转变(真的吗?我坚持疑心立场)。而在ES6中涌现的新对象WeakMap确切给完成Promise带来了新的思绪,可以在ES5及以下环境中完成WeakMap,利用它的特性可以完成相符要求的Promise。详细完成思绪就是:定义一个全局私有的WeakMap,这个WeakMap只需大众的要领then()catch()可以接见到,在这个WeakMap中以每一个Promise实例的this作为键,状况值state作为值举行存储。如许在每一个Promise实例中都可以经由过程本身的this对象查找本身的状况值,而不能查找到其他Promise实例的状况值,如许就完成了状况值的外部不可修正。然则WeakMap有一个很大的题目就是机能比较低而且不利于渣滓接纳,所以这并非一个抱负的处置惩罚方案。

综上两个缘由就致使了如今大部份库暴露state状况值,它们为了完成子类化挑选了暴露状况值,抛弃了Promise的精华地点。

而在作者看来子类化关于Promise的主要性远远比不上Promise的牢靠性,所以它挑选了摒弃子类化而保证Promise的牢靠性。现实确切是如许,假如不能保证Promise的牢靠性,那末就会涌现第一篇中涌现的谁人不牢靠的状况,如许Promise除了改良了回调金字塔的题目,跟一般的回调也就没有什么区别了,也就失去了它更主要的意义。

深切明白Promise五部曲–1.异步题目
深切明白Promise五部曲–2.转换题目
深切明白Promise五部曲–3.牢靠性题目
深切明白Promise五部曲–4.扩大性题目
深切明白Promise五部曲–5.乐高题目

末了,安利下我的个人博客,迎接接见:http://bin-playground.top

    原文作者:lakb248
    原文地址: https://segmentfault.com/a/1190000000593885
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞