写点什么

2019 谷歌 I/O 大会上提出的 JavaScript 新特性,你错过了吗?

  • 2019-08-06
  • 本文字数:10518 字

    阅读完需:约 35 分钟

2019谷歌I/O大会上提出的JavaScript新特性,你错过了吗?

本文总结了 2019 年谷歌 I/O 大会上 Mathias 和 Sathya 提出来的 JavaScript 新规范功能。


本文从对线程认识的简要刷新、事件循环、关于事件循环的常见问题和错误认识三个不同层面进行说明,进一步挖掘了 Node 的核心。

正则表达式 lookbehind

正则表达式(RegEx 正则表达式)在任何语言里都是强大的功能。在字符串中搜索复杂的模式时正则表达式就能大显身手了。之前 JavaScript 中的正则表达式实现已经非常齐全了,唯一缺少的就是 lookbehind

Lookahead

首先我们来了解一下正则表达式中的 lookahead 是什么含义,JavaScript 已经支持 lookahead 了。正则表达式中的 lookahead 语法允许你在字符串中选出的模式具有以下属性:另一个已知模式正好紧靠这个模式或不与其相邻,或者在这个模式之后。例如在字符串“MangoJuice,VanillaShake,GrapeJuice”中,我们可以使用正向 lookahead 语法来查找旁边有 J​​uice 的单词,即 Mango Grape


有两种类型的 lookahead,分别是正向 lookahead 和负向或否定的 lookahead。

正向 lookahead

正向 lookahead 选出的模式具有以下属性:另一个已知模式位于选出模式之后。正向 lookahead 的语法如下。


/[a-zA-Z]+(?=Juice)/
复制代码


上面的模式选出了大写或小写字母的单词,单词旁边都会有 Juice。不要把它和正则表达式中的捕获组混淆。Lookahead 和 Lookbehind 都是写在括号里的,但它们没有被捕获。下面看一个正向 lookahead 的实际例子。


const testString = "MangoJuice, VanillaShake, GrapeJuice";const testRegExp = /[a-zA-Z]+(?=Juice)/g;const matches = testString.match( testRegExp );console.log( matches ); // ["Mango", "Grape"]
复制代码

负向 lookahead

类似地,上面的例子中如果使用负向 lookahead,就是选出所有后面没有 Juice 的单词。负向 lookahead 的语法与正向 lookahead 的语法类似,但有一处不同。我们需要把=符号换成!符号。


/[a-zA-Z]+(?!Juice)/
复制代码


上面的正向表达式模式会选出所有后面不跟着 Juice 的单词。但上面的模式会选出给定字符串中的所有单词,因为给定字符串中的所有单词都不以 Juice 结尾,因此我们需要更具体些的规则。


/(Mango|Vanilla|Grape)(?!Juice)/
复制代码


这种模式将选出 MangoVanilla Grape 几个单词,它们后面没有跟着 Juice。来看具体的代码。


const testString = "MangoJuice, VanillaShake, GrapeJuice";const testRegExp= /(Mango|Vanilla|Grape)(?!Juice)/g;const matches = testString.match( testRegExp );console.log( matches ); // ["Vanilla"]
复制代码

Lookbehind

与 lookahead 类似,Lookbehind 也是正则表达式中的一种语法,用它选出的模式具有以下属性:字符串中的某个已知模式位于或不在它的前面。例如,在字符串“FrozenBananas,DriedApples,FrozenFish”中,我们可以使用正向 lookbehind 来找到前面有 Frozen 的单词,比比如 Bananas Fish


与 lookahead 类似,这里也有一个正向 lookbehind 和负向或否定 lookbehind。

正向 lookbehind

正向 lookbehind 选出的模式具有以下属性:另一个已知模式位于它的前面。正向 lookbehind 的语法如下。


/(?<=Frozen)[a-zA-Z]+/
复制代码


lookbehind 的模式类似于 lookahead,但带有额外的<符号,表示在前。上面的模式会选出所有以 Frozen 开头的单词或者在前面有 Frozen 的单词。来看具体的操作。


const testString = "FrozenBananas, DriedApples, FrozenFish";const testRegExp = /(?<=Frozen)[a-zA-Z]+/g;const matches = testString.match( testRegExp );console.log( matches ); // ["Bananas", "Fish"]
复制代码

负向 lookbehind

负向 lookbehind 选出的模式具有以下属性:另一个已知模式不在它的前面。例如要选出“FrozenBananas,DriedApples,FrozenFish”字符串中前面没有 Frozen 的单词,我们将使用以下语法。


/(?<!Frozen)[a-zA-Z]+/
复制代码


但上面的模式将选出字符串中的所有单词,因为所有单词前面都没有 Frozen(FrozenBannanas 这样的单词这里会被视为一整个单词),我们需要更具体一些。


/(?<!Frozen)(Bananas|Apples|Fish)/
复制代码


写在代码中:


const testString = "FrozenBananas, DriedApples, FrozenFish";const testRegExp = /(?<!Frozen)(Bananas|Apples|Fish)/g;const matches = testString.match( testRegExp );console.log( matches ); // ["Apples"]
复制代码


支持范围——TC39:阶段 4;Chrome:62+;Node:8.10.0+

类字段

类字段(class field)是一种新的语法,用来从类构造函数外部定义实例(对象)的属性。有两种类型的类字段,公共类字段私有类字段

公共类字段

之前我们必须在类构造函数中定义对象上的属性。这些属性是公共的,意味着可以在类(对象)的实例上访问它们。


class Dog {    constructor() {        this.name = 'Tommy';    }}
复制代码


每当我们有一个扩展父类的类时,必须先从构造函数中调用 super,然后才能在子类上添加属性,如下所述。


class Animal {}class Dog extends Animal {    constructor() {        super(); // call super before using `this` in constructor        this.sound = 'Woof! Woof!';    }    makeSound() {        console.log( this.sound );    }}// create instanceconst tommy = new Dog();tommy.makeSound(); // Woof! Woof!
复制代码


现在有了公共类字段语法,我们就可以在类的构造函数之外定义类字段,JavaScript 将隐式调用 super。


class Animal {}class Dog extends Animal {    sound = 'Woof! Woof!'; // public class field    makeSound() {        console.log( this.sound );    }}// create instanceconst tommy = new Dog();tommy.makeSound(); // Woof! Woof!
复制代码


当 JavaScript 隐式调用 super 时,它会在实例化类时传递用户提供的所有参数(这是标准的 JavaScript 行为,与私有类字段无关)。因此,如果你的父构造函数需要定制参数,请确保手动调用 super。


class Animal {    constructor( ...args ) {        console.log( 'Animal args:', args );    }}class Dog extends Animal {    sound = 'Woof! Woof!'; // public class fieldmakeSound() {        console.log( this.sound );    }}// create instanceconst tommy = new Dog( 'Tommy', 'Loves', 'Toys!' );tommy.makeSound(); // Animal args: [ 'Tommy', 'Loves', 'Toys!' ]
复制代码


支持范围——TC39:阶段 3;Chrome:72+;Node:12+

私有类字段

众所周知,JavaScript 没有 publicprivate protected 之类的属性修饰符。默认情况下,对象上的所有属性都是公共的,这意味着任何人都可以访问它们。想要定义一个对外界隐藏的属性,最接近的方法是使用 Symbol 这个属性名称。你可能会使用_前缀来表示应该是私有的属性,但它只是一种表示法,不能解决问题。


现在有了私有类字段,我们可以让类属性只能在类中访问,并防止它们反映在实例(对象)上。来看前面的一个例子,先看一个暴露的属性。


class Dog {    _sound = 'Woof! Woof!'; // this is private        makeSound() {        console.log( this._sound );    }}// create instanceconst tommy = new Dog();console.log( tommy._sound ); // Woof! Woof!
复制代码


添加_前缀并不能解决我们的问题。私有类字段的定义方式与定义公共类字段的方式相同,但我们不必添加下划线前缀,而是添加 #前缀。访问对象上的私有属性将导致 SyntaxError: Undefined private field。


class Dog {    #sound = 'Woof! Woof!'; // this is private    makeSound() {        console.log( this.#sound );    }}// create instanceconst tommy = new Dog();tommy.makeSound() // Woof! Woof!//console.log( tommy.#sound ); // SyntaxError
复制代码


私有属性只能在定义它们的类中访问。因此在父类或子类内无法访问私有属性。


我们还可以使用未定义的值定义私有(和公共)属性。


class Dog {    #name;    constructor( name ) {        this.#name = name;    }    showName() {        console.log( this.#name );    }}// create instanceconst tommy = new Dog( 'Tommy' );tommy.showName(); // Tommy
复制代码


支持范围——TC39:阶段 3;Chrome:74+;Node:12+

string.matchAll

我们在 string 数据类型上有 match 原型方法,它根据给定的正则表达式或关键字返回字符串中的匹配模式。


const colors = "#EEE, #CCC, #FAFAFA, #F00, #000";const matchColorRegExp = /([A-Z0-9]+)/g;console.log( colors.match( matchColorRegExp ) );// Output:["EEE", "CCC", "FAFAFA", "F00", "000"]
复制代码


但这种方法不提供其他附加信息,例如字符串中每个匹配的索引。删除 g 标志后可以生成其他信息,但之后我们只能获得第一个匹配项。


const colors = "#EEE, #CCC, #FAFAFA, #F00, #000";const matchColorRegExp = /#([A-Z0-9]+)/;console.log( colors.match( matchColorRegExp ) );// Output: (result shortned for viewing purpose)["#EEE", "EEE", index: 0, input: "<colors>"]
复制代码


最后,我们需要在正则表达式对象和语法上使用.exec 方法,这样写起来没那么复杂。我们需要使用 while 循环,直到 exec 返回 null 为止。但要注意,exec 不会返回迭代器。


const colors = "#EEE, #CCC, #FAFAFA, #F00, #000";const matchColorRegExp = /#([A-Z0-9]+)/g;// in strict mode,// Uncaught ReferenceError: match is not definedwhile( match = matchColorRegExp.exec( colors ) ) {  console.log( match );}// Output: (result shortned for viewing purpose)["#EEE", "EEE", index: 0, input: "<colors>"]["#CCC", "CCC", index: 6, input: "<colors>"]["#FAFAFA", "FAFAFA", index: 12, input: "<colors>"]["#F00", "F00", index: 21, input: input: "<colors>"]["#000", "000", index: 27, input: input: "<colors>"]
复制代码


为了解决这个问题,我们现在有了 matchAll 方法,它返回一个迭代器,并且这个迭代器的每次 next()调用都会连续返回匹配的项。


const colors = "#EEE, #CCC, #FAFAFA, #F00, #000";const matchColorRegExp = /#([A-Z0-9]+)/g;console.log( ...colors.matchAll( matchColorRegExp ) );// Output: (result shortned for viewing purpose)["#EEE", "EEE", index: 0, input: "<colors>"]["#CCC", "CCC", index: 6, input: "<colors>"]["#FAFAFA", "FAFAFA", index: 12, input: "<colors>"]["#F00", "F00", index: 21, input: input: "<colors>"]["#000", "000", index: 27, input: input: "<colors>"]
复制代码


支持范围——TC39:阶段 4;Chrome:73+;Firefox:67+;Node:12+

命名捕获组

与其他语言相比,JavaScript 中捕获或捕获组的概念略有不同。每当我们在括号内放置一个正则表达式模式(lookahead 和 lookbehind 除外)时,它就会变成一个捕获组,所有匹配的模式都会反映在匹配的输出项中。


在前面的示例中,下面数组的第一项是正则表达式模式的完整匹配,而第二项是捕获组的匹配值。


["#EEE", "EEE", index: 0, input: "<colors>"]
复制代码


如果有多个捕获组,它们将连续显示在结果中。我们来看一个例子。


const str = "My name is John Doe.";const matchRegExp = /My name is ([a-z]+) ([a-z]+)/i;const result = str.match( matchRegExp );console.log( result );// error, if result is nullconsole.log( { firstName: result[1], lastName: result[2] } );// Output:["My name is John Doe", "John", "Doe", index: 0, input: "My name is John Doe.", groups: undefined]{firstName: "John", lastName: "Doe"}
复制代码


如上所示,输出的第一个元素是完整匹配的字符串,而第二个和第三个元素是捕获组的结果。


现在有了命名捕获组后,我们可以使用标签将单个捕获组结果保存在 groups 对象中。定义命名捕获组的语法是(?$pattern)。


const str = "My name is John Doe.";const matchRegExp = /My name is (?<firstName>[a-z]+) (?<lastName>[a-z]+)/i;const result = str.match( matchRegExp );console.log( result );console.log( result.groups );// Output:["My name is John Doe", "John", "Doe", index: 0, input: "My name is John Doe.", groups: {firstName: "John", lastName: "Doe"}]{firstName: "John", lastName: "Doe"}
复制代码


命名捕获组也适用于 matchAll 方法。


支持范围——TC39:阶段 4;Chrome:64+;Node:10+

数字分隔符

我们写较大的整数或小数时,可读性一直是个大问题。例如,十亿写成数字是 1000000000,但你得数对零的个数才行,很多时候这都很让人头疼。


在较新版本的 JavaScript 中,我们可以使用_ 分隔符来分隔数字的各个部分,以增强可读性。


var billion = 1_000_000_000;console.log( billion ); // 1000000000
复制代码


我们可以随意将_放在数字中,而 JavaScript 只会忽略它。这种方法适用于任何类型的数字,无论是整数、十进制、二进制、十六进制还是八进制数字都行。


console.log( 1_000_000_000.11 ); // 1000000000.11console.log( 1_000_000_000.1_012 ); // 1000000000.1012console.log( 0xFF_00_FF ); // 16711935console.log( 0b1001_0011 ); // 147console.log( 0o11_17 ); // 591
复制代码


支持范围——TC39:阶段 3;Chrome:75+;Node:12.5+

BigInt

JavaScript 中的数字是从 Number 函数(也是构造函数)创建的。一个数字可以可靠表示的最大值是(2⁵³ - 1),也就是 9007199254740991。也可以使用 Number.MAX_SAFE_INTEGER 生成这个数。


当我们写下数字时,JavaScript 用 Number 构造函数包装它,以生成一个在其原型上包含数字方法的对象。所有原始数据类型都会这样处理。参阅 Primitives vs Objects 这篇文章来理解这个理念。


那么如果我们继续加大这个数字会怎么样?


console.log( Number.MAX_SAFE_INTEGER ); // 9007199254740991console.log( Number.MAX_SAFE_INTEGER + 10 ); // 9007199254741000
复制代码


上面代码中的最后一个日志输出返回了错误的结果。发生这种情况是因为 JavaScript 无法计算超过 Number.MAX_SAFE_INTEGER 值的数字。


现在有了 BigInt 就能解决这个问题了。BigInt 能让我们表示一个比 Number.MAX_SAFE_INTEGER 值更高的整数。与 Number 类似,BigInt 同时表现为一个函数和一个构造函数。加入 BigInt 后,JavaScript 有了新的 bigint 内置原始数据类型来表示大整数。


var large = BigInt( 9007199254740991 );console.log( large ); // 9007199254740991nconsole.log( typeof large ); // bigint
复制代码


JavaScript 会在整数的末尾添加 n 下标以表示 BigInt 整数形式。因此我们只需在整数的最末尾附加 n 就能写成 BigInt 了。


现在我们有了 BigInt,就可以安全地对具有 bigint 数据类型的大数字执行数学运算了。


var large = 9007199254740991n;console.log( large + 10n ); // 9007199254741001n
复制代码


使用 number 数据类型的数字与使用 bigint 数据类型的数字不同,因为 bigint 只能表示整数。因此程序不允许 bigint 和 number 数据类型之间的算术运算。


BigInt 函数可以接受任何类型的数字,如整数、二进制、十六进制、八进制等。它会在内部统一转换为十进制。


BigInt 还支持数字分隔符。


var large = 9_007_199_254_741_001n;console.log( large ); // 9007199254741001n
复制代码


支持范围——TC39:阶段 3;Chrome:67+;Firefox:68+;Node:10.4+

数组:flat 和 flatMap

数组对象上的 flat 和 flatMap 原型方法。

Array.flat

我们现在能在一个数组使用一个新的 flat(n)原型方法,它将数组展平到第 n 个深度并返回一个新数组。默认情况下 n 为 1。我们可以将 n 作为 Infinity 传递,以展平所有嵌套数组。


var nums = [1, [2, [3, [4, 5]]]];console.log( nums.flat() ); // [1, 2, [3, [4,5]]]console.log( nums.flat(2) ); // [1, 2, 3, [4,5]]console.log( nums.flat(Infinity) ); // [1, 2, 3, 4, 5]
复制代码


支持范围——TC39:阶段 4;Chrome:69+;Firefox:62+;Node:12+

Array.flatMap

日常编程工作中有时可能会使用 map 变换数组,然后将其展平。例如计算一些整数的平方。


var nums = [1, 2, 3];var squares = nums.map( n => [ n, n*n ] )console.log( squares ); // [[1,1],[2,4],[3,9]]console.log( squares.flat() ); // [1, 1, 2, 4, 3, 9]
复制代码


我们可以使用 flatMap 原型方法,用一个语法同时执行映射和展平。它只能将从回调函数返回的数组展平到 1 的深度。


var nums = [1, 2, 3];var makeSquare = n => [ n, n*n ];console.log( nums.flatMap( makeSquare ) ); // [1, 1, 2, 4, 3, 9]
复制代码


支持范围——TC39:阶段 4;Chrome:69+;Firefox:62+;Node:11+

对象:fromEntries

我们可以使用对象的 entries 静态方法提取对象的 key:value 对,该方法返回一个数组,其中每个元素都是一个数组,后者的第一项是 key,第二项是 value。


var obj = {x:1,y:2,z:3};var objEntries = Object.entries(obj);console.log(objEntries); // [[“x”,1],[“y”,2],[“z”,3]]
复制代码


我们现在可以在对象上使用 fromEntries 静态方法,它会将条目转换回对象。


var entries = [["x", 1],["y", 2],["z", 3]];var obj = Object.fromEntries( entries );console.log( obj ); // {x: 1, y: 2, z: 3}
复制代码


之前我们使用 entries 就能很容易地过滤和映射对象值,但将条目放回到对象表单却很麻烦。这里就可以使用 fromEntries 简化工作了。


var obj = { x: 1, y: 2, z: 3 };// [["x", 1],["y", 2],["z", 3]]var objEntries = Object.entries( obj );// [["x", 1],["z", 3]]var filtered = objEntries.filter( ( [key, value] ) => value % 2 !== 0 // select odd);console.log( Object.fromEntries( filtered ) ); // {x: 1, z: 3}
复制代码


当我们使用 Map 按插入顺序存储键值对时,内部数据结构与条目格式类似。我们可以使用 fromEntries 轻松地从 Map 构造一个对象。


var m = new Map([[“x”,1],[“y”,2],[“z”,3]]);console.log(m); // {“x”=> 1,“y”=> 2,“z”=> 3}console.log(Object.fromEntries(m)); // {x:1,y:2,z:3}
复制代码


支持范围——TC39:阶段 4;Chrome:73+;Firefox:63+;Node:12+

globalThis

之前我们很熟悉 JavaScript 中的 this 关键字。它没有确定的值,其值取决于访问它的上下文。在任何环境中,当从程序的最顶层上下文访问时 this 指向全局对象,这就是所谓的全局 this。


例如,在 JavaScript 中全局 this 是 window 对象,你可以在 JavaScript 文件的顶部(最外层上下文)或 JavaScript 控制台内添加 console.log(this)语句来验证这一点。



this 全局值在 Node.js 内部会指向 global 对象,而在 web worker 内部会指向 web worker 本身。但是要获取全局 this 值不太容易,因为我们不能在所有位置使用 this 关键字;例如,在类构造函数中 this 值指向类实例。


因此其他环境为我们提供了像 self 这样的关键字,与 JavaScript 和 web worker 中的全局 this 一样;而在 Node.js 中使用的是 global。使用这些替代关键字时,我们可以创建一个通用函数来返回全局 this 值。


const getGlobalThis = () => { if (typeof self !== 'undefined') return self; if (typeof window !== 'undefined') return window; if (typeof global !== 'undefined') return global; if (typeof this !== 'undefined') return this; throw new Error('Unable to locate global `this`');};var globalThis = getGlobalThis();
复制代码


但是用这个 polyfill 获取全局 this 对象会出问题,这篇文章解释了原因。为了解决这个问题,JavaScript 现在提供了 globalThis 关键字,它可以从任何地方返回全局 this 对象。


var obj = { fn: function() {  console.log( 'this', this === obj ); // true  console.log( 'globalThis', globalThis === window ); // true} };obj.fn();
复制代码


支持范围——TC39:阶段 3;Chrome:71+;Firefox:65+;Node:12+

稳定排序

我们对数组排序时,ECMAScript 不会为 JavaScript 引擎提出排序算法,而只会强制执行排序 API 的语法。因此排序性能和/或排序稳定性会随着浏览器或 JavaScript 引擎的不同而变化。


但现在 ECMAScript 强制数组排序算法保持稳定。这个答案介绍了排序稳定性更新。简而言之,如果排序结果(变异数组)中那些不受排序影响的项目顺序不变,与一开始插入的顺序一致,则排序算法就是稳定的。我们来看一个例子吧。


var list = [  { name: 'Anna', age: 21 },  { name: 'Barbra', age: 25 },  { name: 'Zoe', age: 18 },  { name: 'Natasha', age: 25 }];// possible result[  { name: 'Natasha', age: 25 }  { name: 'Barbra', age: 25 },  { name: 'Anna', age: 21 },  { name: 'Zoe', age: 18 },]
复制代码


如上所示,在 list 数组中,名为 Barbra 的对象位于名为 Natasha 的对象之前。由于这些对象有着相同的年龄,我们希望排序结果中它们保持相同的顺序,但有时结果并非如此。排序算法的结果会取决于你使用的 JavaScript 引擎。


但是现在,所有现代浏览器和 Node.js 默认使用 sort 方法进行稳定排序。这将始终产生以下结果。


// stable sort result[  { name: 'Barbra', age: 25 },  { name: 'Natasha', age: 25 }  { name: 'Anna', age: 21 },  { name: 'Zoe', age: 18 },]
复制代码


一些 JavaScript 引擎以前支持稳定排序,但仅适用于较小的数组。为了提高大型数组的性能,他们可能会使用更快的算法并牺牲排序稳定性。


支持范围——Chrome:70+;Firefox:62+;Node:12+

国际化 API

国际化 API 是由 JavaScript 中的 ECMAScript 标准提供的 API,用于格式化指定语言中的数字、字符串、日期和时间。此 API 在 Intl 对象上可用。此对象提供构造函数,以便为指定的区域设置创建与区域相关数据的格式化程序。可在此处查看支持的区域设置列表。

Intl.RelativeTimeFormat

在许多应用程序中,我们通常需要以相对格式显示时间,例如 5 分钟前、昨天1 周前等。当我们的网站需要区分不同区域的显示内容时,我们需要在分发包中存放所有可能的相对时间输出组合 。


JavaScript 现在在 Intl 对象上提供了 RelativeTimeFormat9(locale, config)构造函数,它允许你为特定的区域设置创建时间格式化程序。这将创建一个具有 format(value, unit)原型方法的对象来生成时间格式。


// español (spanish)var rtfEspanol= new Intl.RelativeTimeFormat('es', {  numeric: 'auto'});log( rtfEspanol.format( 5, 'day' ) ); // dentro de 5 díaslog( rtfEspanol.format( -5, 'day' ) ); // hace 5 díaslog( rtfEspanol.format( 15, 'minute' ) ); // dentro de 15 minutos
复制代码


支持范围——TC39:阶段 3;Chrome:71+;Firefox:65+;Node:12+

Intl.ListFormat

ListFormat API 允许我们将列表中的项目基于 and 或 or 格式组合在一起。例如,[apples,mangoes,bananas]使用并列格式就是 apples,mangoes and bananas,使用分离格式就是 apples,mangoes or bananas。


首先,我们需要根据区域环境从 ListFormat(locale, config)构造函数创建格式化程序实例,并使用 format(list)原型方法生成特定于区域环境的列表格式。


// español (spanish)var lfEspanol = new Intl.ListFormat('es', {  type: 'disjunction'});var list = [ 'manzanas', 'mangos', 'plátanos' ];log( lfEspanol.format( list ) ); // manzanas, mangos o plátanos
复制代码


支持范围——TC39:阶段 3;Chrome:72+;Node:12+

Intl.Locale

除了语种名称外,区域设置通常还有很多内容,如日历类型、小时制、语言等。Intl.Locale(localeId, config)构造函数用来基于提供的配置生成格式化的语言环境字符串。它创建的对象包含所有区域设置属性,并暴露 toString 原型方法以获取格式化的区域设置字符串。


const krLocale = new Intl.Locale( 'ko', {  script: 'Kore', region: 'KR',  hourCycle: 'h12', calendar: 'gregory'} );log( krLocale.baseName ); // ko-Kore-KRlog( krLocale.toString() ); // ko-Kore-KR-u-ca-gregory-hc-h12
复制代码


在此处(https://unicode.org/reports/tr35/#unicode_locale_id)了解区域设置标识符和 Unicode 区域设置标记。


支持范围——TC39:阶段 3;Chrome:74+;Node:12+

Promise

之前,我们在 Promise 构造函数上有 all 和 race 两种静态方法。Promise.all([… promises])返回一个 promise,它在输入的所有 promises 解析后才解析,输入的任何 promise 被拒绝时它也会被拒绝。Promise.race([… promises])返回一个 promise,输入的任何 promise 解析后它就会解析,输入的任何 promise 被拒绝后它也会被拒绝。


我们迫切需要一个静态方法来返回一个 promise,它要在所有 promise 完成后(解析或拒绝)解析。我们还需要一个类似 race 的方法来返回一个 promise,等输入的任何 promise 解析后它就会解析。

Promise.allSettled

Promise.allSettled 方法获取一组 promise,并在所有 promise 都被解析或拒绝后解析。因此,此方法返回的 promise 不需要 catch 回调,因为它总是会解析。then 回调按照各个 promise 的顺序接收每个 promise 的 status 和 value。


var p1 = () => new Promise(  (resolve, reject) => setTimeout( () => resolve( 'val1' ), 2000 )); var p2 = () => new Promise(  (resolve, reject) => setTimeout( () => resolve( 'val2' ), 2000 ));  var p3 = () => new Promise(  (resolve, reject) => setTimeout( () => reject( 'err3' ), 2000 ));var p = Promise.allSettled( [p1(), p2(), p3()] ).then(  ( values ) => console.log( values ));// Output[ {status: "fulfilled", value: "val1"}  {status: "fulfilled", value: "val2"}  {status: "rejected", value: "err3"}]
复制代码


支持范围——TC39:阶段 3;Chrome:76+

Promise.any

Promise.any 方法类似于 Promise.race,但是只要任何 promise 被拒绝,后者返回的 promise 就不会执行 catch 块。相比之下,前者等任何 promise 解析后它返回的 promise 也会解析。如果没有解析任何 promise,catch 块将被执行。如果有任何 promise 先解析了,就会执行 then 块。


支持范围——TC39:阶段 1


观看本文视频


英文原文:https://itnext.io/whats-new-in-javascript-google-i-o-2019-summary-d16bd230841


2019-08-06 10:063165

评论

发布
暂无评论
发现更多内容

Go1.16 发布

Rayjun

Go 语言

Dart 后台开发 Aqueduct @Column标记

人生如梦

关于Linux系统中Message中的Session日志详解

运维研习社

Centos 7

关于智商测试的一点闲话 Day1

道伟

科普 28天写作

如何解决Nginx实现动静分离或反向代理时资源路径不匹配

运维研习社

nginx 反向代理 动静分离

创业公司人力资源体系建设的几点思考

一笑

人力资源 28天写作

IO 模型知多少 | 理论篇

圣杰

io

MySQL 批量修改所有表字段字符集及排序规则

运维研习社

MySQ

说说规则引擎

张老蔫

28天写作

实例详解Linux下ulimit每个参数

运维研习社

Linux ulimit linux系统资源管理 open file

Dart 后台开发 Aqueduct 插入数据 获取数据API

人生如梦

flutter dart

面试官一上来就问我Chrome底层原理和HTTP协议(万字长文)

我是哪吒

大前端 后端 chorme 28天写作 2月春节不断更

Nginx零成本、易操作实现网站视频加速

运维研习社

nginx 流媒体 网站优化

Let's Encrypt签发工具CertBot-auto不再维护

运维研习社

2021 Flutter从零开始之全栈开发,后台到在线教育APP上线。

人生如梦

flutter dart

Dart 后台开发 Aqueduct ORM初始化数据库

人生如梦

28天瞎写的第二百三十九天:什么是正念冥想?

树上

冥想 28天写作 正念

Jenkins通过OpenSSH实现Windows下的CI/CD

运维研习社

jenkins CI/CD Windows Server 2012 R2

一文搞懂Linux下Ulimit资源限制

运维研习社

Linux linux命令 ulimit

Nginx如何监控各server的流量

运维研习社

nginx Prometheus zabbix upstream

管理笔记 [9]:组织与督导,管理者的两个宝

L3C老司机

28天写作

Nginx加密套件配置不当,造成SSL无法建立连接

运维研习社

nginx zabbix SSL证书 证书监控

你好,2021~

数据社

程序员 2021年展望

为什么做这样一个产品之容量评估篇

数列科技杨德华

28天写作

Flutter安卓项目第一次启动失败解决方案

人生如梦

flutter

抓包带你详解TCP的11种状态

运维研习社

三次握手 四次挥手 TCP/IP 抓包

做出赋能其他人的产品是技术牛人最好的证明

刘华Kenneth

敏捷 平台

心理学与游戏之现学现卖系列

Justin

心理学 28天写作 游戏设计

Dart 后台开发 Aqueduct集成Swagger客户端

人生如梦

flutter dart

【STM32】CubeMX+HAL 输出PWM

AXYZdong

硬件 stm32 2月春节不断更

牛启新春|优质文章人气大挑战

InfoQ写作社区官方

热门活动

2019谷歌I/O大会上提出的JavaScript新特性,你错过了吗?_大前端_Uday Hiwarale_InfoQ精选文章