原文链接 https://medium.freecodecamp.org/here-are-examples-of-everything-new-in-ecmascript-2016-2017-and-2018-d52fa3b5a70e
很难跟踪 JavaScript(ECMAScript)中的新功能, 更难找到有用的代码示例.
因此, 在本文中, 我将介绍在 ES2016,ES2017 和 ES2018(最终草案)中添加的 TC39 已完成提案中列出的所有 18 个功能, 并向他们展示有用的示例.
这是一个相当长的帖子, 但应该是一个简单的阅读. 把这看作是 "Netflix binge reading". 在这之后, 我保证你将对这些功能有很多的了解.
让我们一个接一个地看看:
- ECMAScript 2016
- 1. Array.prototype.includes
includes 是数组上的简单实例方法, 并有助于轻松查找某个项是否在 Array 中(包括 NaN 不像 indexOf)
- const arr = [1, 2, 3, 4, NaN];
- // es2016 不使用
- if (arr.indexOf(3) >= 0) {
- console.log(true);
- }
- // 使用
- if (arr.includes(3)) {
- console.log(true);
- }
- //ps: 注意 indexOf 是不支持查找 NaN 的
- arr.includes(NaN) // true
- arr.indexOf(NaN) // -1 (indexOf 不支持 NaN)
琐事: 人们想要命名的 JavaScript 规范 contains, 但这显然已被 Mootools 使用, 因此他们使用了 includes.
2. 指数运算符
数学运算如加法和减法分别有 + 和 - 等运算符. 与他们类似,** 运算符通常用于指数运算. 在 ECMAScript 2016 中, 引入了 ** 而不是 Math.pow.
- // 不使用
- Math.pow(7,2) //49
- // 使用
- 7**2 //49
- ECMAScript 2017
- 1. Object.values()
Object.value()是一个与 Object.keys()类似的新函数, 但会返回 Object 自身属性的所有值, 排除原型链中的任何值.
- const cars = { 'BMW': 3, 'Tesla': 2, 'Toyota': 1 };
- //ES2015
- //ES2017 不使用
- const vals = Object.keys(cars).map(key => cars[key]);
- console.log(vals); //[3, 2, 1]
- //ES2017 and 未来
- // 使用
- const values = Object.values(cars);
- console.log(values); // [3, 2, 1]
- 2. Object.entries()
Object.entries()与 Object.keys 相关, 但不是仅返回 keys, 而是以数组方式返回 keys 和 values. 这使得像循环中使用对象或将对象转换为 Maps 这样的事情变得非常简单.
例 1:
- const cars = { 'BMW': 3, 'Tesla': 2, 'Toyota': 1 };
- // ES5.1
- // 而不是提取键然后再循环
- Object.keys(cars).forEarch(function(key) {
- console.log('key:' + key + 'value:' + cars[key]);
- })
- //ES2017 (ES8)
- // 使用 Object.entries
- for(let [key value] of Object.entries(cars)) {
- console.log(`key: ${key} value: ${value}`);
- }
例 2:
- const cars = { 'BMW': 3, 'Tesla': 2, 'Toyota': 1 };
- //ES2015
- // 不使用
- // 获取对象键, 然后添加每个项目以在循环中映射
- const maps = new Map();
- Object.keys(cars).forEarch(key => {
- map1.set(key, cars[key]);
- })
- console.log(map1) // Map { 'BMW' => 3, 'Tesla' => 2, 'Toyota' => 1}
- //ES2017 and 以后
- // 使用
- const map = new Map(Object.entries(cars));
- console.log(map) //Map { 'BMW' => 3, 'Tesla' => 2, 'Toyota' => 1}
- 3. String padding
向字符串 -
String.prototype.padStart
和
String.prototype.padEnd
添加了两个实例方法 - 它们允许在原始字符串的开始或结尾附加 / 预先添加空字符串或其他字符串.
- 'someString'.padStart(numberOfCharcters [,stringForPadding]);
- '5'.padStart(10) // '5'
- '5'.padStart(10, '=*') //'=*=*=*=*=5'
- '5'.padEnd(10) // '5'
- '5'.padEnd(10, '=*') //'5=*=*=*=*='
当我们想要在诸如漂亮的打印显示或终端打印等场景中对齐事物时, 这很方便.
3.1 padStart 例子
在下面的例子中, 我们有一个不同长度的数字列表. 我们希望预先设置 "0", 以便所有项目具有相同长度的 10 位数字用于显示目的. 我们可以使用 padStart(10, '0')轻松实现这一点.
- //ES2017
- // 如果你有一个不同长度的项目列表, 并希望格式化它们的显示目的, 你可以使用 padStart
- const formatted = [0, 1, 12, 123, 1234, 12345].map(num =>
- num.toString().padStart(10, '0') // 添加 0 直到长度为 10
- );
- console.log(formatted);
- // 打印
- // [
- // '0000000000',
- // '0000000001',
- // '0000000012',
- // '0000000123',
- // '0000001234',
- // '0000012345',
- // ]
3.2 padEnd 例子:
我们打印多个不同长度的项目并想要正确对齐它们时, padEnd 真的很方便.
下面的例子是 padEnd,padStart 和 Object.entries 如何聚合在一起产生漂亮输出的一个很好的实例.
- const cars = {
- 'BMW': '10',
- 'Tesla': '5',
- 'Lamborghini': '0'
- }
- Object.entries(cars).map(([name, count]) => {
- //padEnd appends '-' until the name becomes 20 characters
- //padStart prepends '0' until the count becomes 3 characters.
- console.log(`${name.padEnd(20, '-')} Count: ${count.padStart(3, '0')}`)
- });
- // 打印结果..
- // BMW - - - - - - - Count: 010
- // Tesla - - - - - - Count: 005
- // Lamborghini - - - Count: 000
3.3 Emojis(表情符号)和其他双字节字符上的 padStart 和 padEnd
Emojis 和其他双字节字符使用 unicode 的多个字节表示. 所以 padStart 和 padEnd 可能无法按预期工作!
例如: 假设我们正在尝试用表情符号填充字符串 heart 以达到 10 个字符. 结果如下所示
- // 注意, 不是 5 颗心, 只有 2 颗心和 1 颗看起来很奇怪的心!
- 'heart'.padStart(10, ""); // prints.. 'heart'
这是因为长 2 个字符('\u2764\uFE0F')! 单词 heart 本身是 5 个字符, 所以我们只剩下 5 个字符. 所以, JS 填塞两颗心用'\u2764\uFE0F'和生产. 为最后一个只是用心灵的第一个字节 \ u2764 产生
所以结果是:
heart
PS: 您可以使用此链接 https://encoder.internetwache.org/#tab_uni 查看 unicode 字符转换
4. Object.getOwnPropertyDescriptors
此方法返回给定对象的所有属性的所有详细信息(包括 get set 方法). 添加这个的主要动机是允许浅拷贝 / 克隆一个对象到另一个对象中, 这个对象也拷贝 getter 和 setter 函数, 而不是 Object.assign
Object.assign 浅克隆除原始源对象的 getter 和 setter 函数以外的所有详细信息.
以下示例显示了 Object.assign 和
Object.getOwnPropertyDescriptors
与
Object.defineProperties
之间的区别, 以将原始对象 Car 复制到新对象 ElectricCar,
Object.getOwnPropertyDescriptors
看到, discount 获取器和设置器函数也被复制到目标对象中.
- ######之前...
- // 之前...
- var Car = {
- name: 'BMW',
- price: 1000000,
- set discount(x) {
- this.d = x;
- },
- get discount() {
- return this.d;
- },
- };
- // 打印 Car 对象的 "discount" 属性的详细信息
- console.log(Object.getOwnPropertyDescriptor(Car, 'discount'));
- // 打印结果..
- // {
- // get: [Function: get],
- // set: [Function: set],
- // enumerable: true,
- // configurable: true
- // }
- // 使用 Object.assign 将 Car 的属性复制到 ElectricCar
- const ElectricCar = Object.assign({},
- Car);
- // 打印 ElectricCar 对象的 "discount" 属性的详细信息
- console.log(Object.getOwnPropertyDescriptor(ElectricCar, 'discount'));
- // 打印
- // {
- // value: undefined,
- // writable: true,
- // enumerable: true,
- // configurable: true
- // }
- // 请注意 ElectricCar 对象中的 "discount" 属性中缺少 getter 和 setter!
- ######以后...
- // 以后...
- var Car = {
- name: 'BMW',
- price: 1000000,
- set discount(x) {
- this.d = x;
- },
- get discount() {
- return this.d;
- },
- };
- // 使用 Object.defineProperties 将 Car 的属性复制到 ElectricCar2
- // 并使用 Object.getOwnPropertyDescriptors 提取 Car 的属性
- const ElectricCar2 = Object.defineProperties({},
- Object.getOwnPropertyDescriptors(Car));
- // 打印 ElectricCar2 对象 "discount" 属性的详细信息
- console.log(Object.getOwnPropertyDescriptor(ElectricCar2, 'discount'));
- //prints..
- // { get: [Function: get],
- // set: [Function: set],
- // enumerable: true,
- // configurable: true
- // }
- // 请注意, getter 和 setter 存在于 ElectricCar2 对象中, 用于'discount'属性!
5. 在函数参数中添加尾随逗号(Add trailing commas in the function parameters)
这是一个小的更新, 它允许我们在最后一个函数参数后面加上尾随逗号. 为什么? 为了帮助像 git 这样的工具, 只会让新开发人员受到指责.
以下示例显示了问题和解决方案:
- // 问题
- // #1 开发者创建
- function Person(
- name,
- age
- ) {
- this.name = name;
- this.age = age;
- }
- //#2 开发者添加 address
- function Person(
- name,
- age, // 添加尾随逗号 <-------- 因为逗号, 开发人员会被 git 等工具所警告
- address // 添加新的参数
- ) {
- this.name = name;
- this.age = age;
- this.address = address; // 添加的这行
- }
- //ES2017 解决
- // 允许 #1 开发者添加尾随逗号
- //#1 创建如下
- function Person(
- name,
- ange, //<------- 由于尾随逗号,#2 开发者不需要改变这行
- ) {
- this.name = name;
- this.age = age;
- }
注意: 您也可以使用尾随逗号来调用函数
6. Async/Await
如果你问我, 这是迄今为止最重要也是最有用的功能. 异步函数 (Async) 允许我们不处理调用地狱, 并使整个代码看起来很简单.
async 关键字告诉 JavaScript 编译器以不同的方式处理函数, 编译器在该函数内到达 await 关键字时暂停. 它假设在 await 之后的表达式返回一个承诺, 并等待承诺解决或拒绝后继续前进.
在下面的例子中, getAmount 函数调用两个异步函数 getUser 和 getBankBalance. 我们可以承诺做到这一点, 但使用 async await 更优雅和简单
- // 不使用
- // ES2015 Promise
- function getAmount(userId) {
- getUser(userId).then(getBankBalance).then(amount =>{
- console.log(amount);
- });
- }
- // 使用...
- // ES2017
- async
- function getAmount2(userId) {
- var user = await getUser(userId);
- var amount = await getBankBalance(user);
- console.log(amount);
- }
- getAmount('1'); //$1,000
- getAmount2('1'); //$1,000
- function getUser(userId) {
- return new Promise(resolve =>{
- setTimeout(() =>{
- resolve('john')
- },
- 1000)
- });
- }
- function getBankBalance(user) {
- return new Promise((resolve, reject) =>{
- setTimeout(() =>{
- if (user == 'john') {
- resolve('$1,000');
- } else {
- reject(unknown user);
- }
- },
- 1000)
- });
- }
6.1 异步函数本身返回一个 Promise(Async functions themselves return a Promise.)
如果您正在等待异步函数的结果, 则需要使用 Promise 的 then 语法来捕获其结果.
在下面的例子中, 我们想使用 console.log 记录结果, 但不是在 doubleAndAdd 中. 所以我们想等待并使用 then 语法将结果传递给 console.log.
- // 异步函数本身返回一个 Promise
- async
- function doubleAndAdd(a, b) {
- a = await doubleAfterlSec(a);
- b = await doubleAfterlSec(b);
- return a + b;
- }
- // 用法
- doubleAndAdd(1, 2).then(console.log);
- function doubleAfterlSec(param) {
- return new Promise(resolve =>{
- setTimeout(resolve(param * 2), 1000);
- });
- }
6.2 并行调用异步 / 等待(Calling async/await in parallel)
在前面的例子中, 我们调用等待两次, 但每次我们等待一秒(总共 2 秒). 相反, 我们可以让它并行, 因为 a 和 b 不依赖于使用 Promise.all 的其他对象.
- // 异步函数本身返回一个 Promise
- async
- function doubleAndAdd(a, b) {
- // 注意: 这边使用 Promise.all
- // 注意到使用数组解构, 捕获结果
- [a, b] = await Promise.all([doubleAfterlSec(a), doubleAfterlSec(b)]);
- return a + b;
- }
- // 用法
- doubleAndAdd(1, 2).then(console.log);
- function doubleAfterlSec(param) {
- return new Promise(resolve =>{
- setTimeout(resolve(param * 2), 1000);
- });
- }
6.3 异步 / 等待函数时的错误处理(Error handling async/await functions
)
下面使用异步等待时有各种方法来处理错误.
Option 1 - 在函数中使用 try catch
- // 1. 使用 try catch
- async
- function doubleAndAdd(a, b) {
- try {
- a = await doubleAfterlSec(a);
- b = await doubleAfterlSec(b);
- } catch(e) {
- return NaN; // return something
- }
- return a + b;
- }
- // 用法
- doubleAndAdd('one', 2).then(console.log) //NaN
- doubleAndAdd(1, 2).then(console.log) // 6
- function doubleAfterlSec(param) {
- return new Promise((resolve, reject) =>{
- setTimeout(function() {
- let val = param * 2;
- isNaN(val)> reject(NaN) : resolve(val);
- },
- 1000);
- });
- }
Option 2 - catch 获每个等待表达式(Catch every await expression)
由于每个 await 表达式都会返回 Promise, 因此您可以在每行上捕获错误, 如下所示.
- //Option 2 - *Catch* 每个等待线上的错误
- // 因为每个等待表达本身就是一个 Promise
- async function doubleAndAdd(a, b) {
- a = await doubleAfter1Sec(a).catch(e => console.log('"a" is NaN')); //
- b = await doubleAfter1Sec(b).catch(e => console.log('"b" is NaN')); //
- if (!a || !b) {
- return NaN;
- }
- return a + b;
- }
- // 用法:
- doubleAndAdd('one', 2).then(console.log); // NaN and logs: "a" is NaN
- doubleAndAdd(1, 2).then(console.log); // 6
- function doubleAfter1Sec(param) {
- return new Promise((resolve, reject) => {
- setTimeout(function() {
- let val = param * 2;
- isNaN(val) ? reject(NaN) : resolve(val);
- }, 1000);
- });
- }
Option 3-捕获整个异步等待函数(Catch the entire async-await function)
- //Option 3 - 不要做任何事情, 在 function 外捕获
- // 因为异步 / 等待返回一个 Promise, 我们可以捕捉整个函数的错误
- async
- function doubleAndAdd(a, b) {
- a = await doubleAfter1Sec(a);
- b = await doubleAfter1Sec(b);
- return a + b;
- }
- // 用法
- doubleAndAdd('one', 2).then(console.log).
- catch(console.log); // <------- use "catch"
- function doubleAfter1Sec(param) {
- return new Promise((resolve, reject) =>{
- setTimeout(function() {
- let val = param * 2;
- isNaN(val) ? reject(NaN) : resolve(val);
- },
- 1000);
- });
- }
- ECMAScript 2018
ECMAScript 目前处于最终草案中, 将于 2018 年 6 月或 7 月发布. 以下所有功能均在 Stage-4 中, 并将成为 ECMAScript 2018 的一部分
1. 共享内存和原子 (Shared memory and atomics https://github.com/tc39/ecmascript_sharedme )
这是一个巨大的, 非常先进的功能, 是对 JS 引擎的核心增强.
主要思想是为 JavaScript 提供某种多线程功能, 以便 JS 开发人员可以编写高性能, 通过允许自己管理内存而不是让 JS 引擎管理内存来实现并发程序.
这是通过一种名为 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer 的新型全局对象来完成的, 它基本上将数据存储在共享内存空间中. 所以这个数据可以在主 JS 线程和网络工作者线程之间共享.
到现在为止, 如果我们想要在主 JS 线程和 Web 工作者之间共享数据, 我们必须复制数据并使用 postMessage 将其发送到其他线程. 就这样子!
您只需使用 SharedArrayBuffer, 主线程和多个 Web 工作线程即可访问数据.
但在线程之间共享内存可能会导致竞争状况. 为了避免竞争条件, 引入了 "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics" 全局对象. 当一个线程正在使用它的数据时, Atomics 提供了各种方法来锁定共享内存. 它还提供了安全地更新共享内存中的数据的方法.
建议通过某个库使用此功能, 但现在没有构建在此功能之上的库.
- // 标准字符串插值
- const firstName = 'Raja';
- const greetings = `Hello ${firstName}!`;
- console.log(greetings); // "Hello Raja!"
- // "标记" 函数返回一个自定义字符串文字.
- // 在这个例子中, greet 调用 timeGreet()来追加 Good
- // Morning/Afternoon/Evening 视当天的时间而定.
- function greet(hardCodedPartsArray, ...replacementPartsArray) {
- console.log(hardCodedPartsArray); //[ 'Hello', '!' ]
- console.log(replacementPartsArray); //[ 'Raja' ]
- let str = '';
- hardCodedPartsArray.forEach((string, i) => {
- if (i <replacementPartsArray.length) {
- str += `${string} ${replacementPartsArray[i] || ''}`;
- } else {
- str += `${string} ${timeGreet()}`; //<-- 插入 Good morning/afternoon/evening here
- }
- });
- return str;
- }
- // 用法
- const firstName = 'Raja';
- const greetings = greet`Hello ${firstName}!`; //<-- 标记文字
- console.log(greetings); //'Hello Raja! Good Morning!'
- function timeGreet() {
- const hr = new Date().getHours();
- return hr < 12
- ? 'Good Morning!'
- : hr < 18 ? 'Good Afternoon!' : 'Good Evening!';
- }
- function myTagFunc(str) {
- return { "cooked": "undefined", "raw": str.raw[0] }
- }
- var str = myTagFunc `hi \ubla123abla`; // 调用 myTagFunc
- str // { cooked: "undefined", raw: "hi \\unicode" }
- // 之前
- /first.second/.test('first\nsecond'); //false
- //ECMAScript 2018
- /first.second/s.test('first\nsecond'); //true Notice: /s
- const re = /foo.bar/s;
- // 或者 const re = new RegExp('foo.bar', 's');
- re.test('f00\nbar'); //true
- re.dotAll // true
- re.flags // 's'
- // 之前
- let re1 = /(\d{4})-(\d{2})-(\d{2})/;
- let result1 = re1.exec('2015-01-02');
- console.log(result1);
- /$/ ["2015-01-02", "2015", "01", "02", index: 0, input: "2015-01-02", groups: undefined]
- // 以后(ES2018)
- let re2 = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
- let result2 = re2.exec('2015-01-02');
- console.log(result2);
- //["2015-01-02", "2015", "01", "02", index: 0, input: "2015-01-02", groups: { year: '2015', month: '01', day: '02'}]
- // 用法:
- // 你想要知道年份, 你可以:
- console.log(result2.groups.year) //2015
- // 下面例子, 有个组叫 "fruit"
- // 它既能匹配 "apple" 又能匹配 "orange". 我们可以使用 "\k<group name>" 来回引用这个组的结果,(\k<fruit>)
- // 所以它匹配 == 两侧的同一个单词
- let sameWords = /(?<fruit>apple|orange)==\k<fruit>/u;
- sameWords.test('apple==apple'); //true
- sameWords.test('orange==orange'); //true
- sameWords.test('apple==orange'); //false
- // 更换 "firstName, lastName" ==> "lastName, firstName"
- let re = /(?<firstName>[A-Za-z]+) (?<lastName>[A-Za-z]+$)/u;
- 'Raja Rao'.replace(re, '$<lastName>, $<firstName>'); // "Rao, Raja"
- // 提取 firstName 和 age
- // 将其余的项存储在 "remaining" 变量中.
- let { fristName, age, ...remaining } = {
- fristName: 'john',
- lastName: 'smith',
- age: 20,
- height: '5.10',
- race: 'martian',
- };
- firstName; //john
- age //20
- remaining //{ lastName: 'smith', height: '5.10', race: 'martian' }
- // 假设我们想要移除 SSN, 我们不需要遍历整个对象并创建一个新的 cleanObj.
- // 简单地使用 rest 解构来提取出 SSN, 并将其余的保存在 cleanObj 中.
- let { SSN, ...cleanObj } = {
- firstName: 'john',
- lastName: 'smith',
- SSN: '123-45-6789',
- race: 'martian',
- }
- cleanObj; //{ firstName: 'john', lastName: 'smith', race: 'martian' }
- // person 和 account 合并
- const person = { fName: 'john', age: 20 };
- const account = { name: 'bofa', amount: '$1000' };
- // 通过扩展运算符提取 person 和 account 的属性, 并将其添加到一个新对象中.
- const personAndAccount = { ...person, ...account };
- personAndAccount; // {fName: 'john', age: 20, name: 'bofa', amount: '$1000' }
- /(?<=#).*/.test('winning'); // false
- /(?<=#).*/.test('#winning'); // true
- // 之前
- '#winning'.match(/#.*/)[0]; // '#wining', 包含 #
- //ES2018 后
- '#winning'.match(/(?<=#).*/)[0]; // 'wining' 没有 #, #是用来验证的.
- // 3.00 不能匹配, 因为符号 $ 在前面
- 'A gallon of milk is $3.00'.match(/(?<\$)\d+\.?\d+/); // null
- // 2.43 匹配成功, 因为前面没有符号 $
- // 当匹配时, 不包含符号
- 'A gallon of milk is 3.00'.match(/(?<\$)\d+\.?\d+/)[0]; //2.43
- // 他跟随匹配多个印地文字符
- /^\p{Script=Devanagari}+$/u.test(''); //true
- //PS: 有 3 个印地文字符 h
- // 以下匹配一个希腊字符
- /\p{Script_Extensions=Greek}/u.test('π'); // true
- // 下面是匹配表情字符
- /\p{Emoji}/u.test(''); //true
- // 下面的操作失败了, 因为黄色的 emojis 没有 Emoji_Modifier!
- /\p{Emoji}\p{Emoji_Modifier}/u.test(''); //false
- // 下面的操作是匹配的,\p{Emoji} 紧跟后面是 \p{Emoji_Modifier}
- /\p{Emoji}\p{Emoji_Modifier}/u.test(''); //true
- // 解释:
- // 默认情况下, 胜利表情是黄色的. 如果我们使用相同表情的棕色, 黑色或其他, 它们被认为是原始表情的变体, 并使用两个 unicode 字符表示. 一个是原始表情符号, 另一个是颜色的 unicode 字符. 在下面的例子中, 虽然我们只看到一个棕色的胜利表情符号, 但它实际上使用了两个 unicode 字符, 一个用于表情符号, 另一个用于棕色.
- // 在 Unicode 数据库中, 这些颜色具有 Emoji_Modifier 属性. 因此, 我们需要使用 \ p{Emoji}和 \ p{Emoji_Modifier}来正确和完全匹配棕色的表情符号.
- /\p{Emoji}\p{Emoji_Modifier}/u.test(''); //true
- // Resolve
- let started = true;
- let myPromise = new Promise(function(resolve, reject) {
- resolve('all good');
- })
- .then(val => {
- console.log(val); //all good
- })
- .catch(e => {
- console.log(e)l // 跳过
- })
- .finally(() => {
- console.log('This function is always executed!');
- started = false; // 清除
- })
- // Reject
- let started = true;
- let myPromise = new Promise(function(resolve, reject) {
- resolve('reject apple');
- })
- .then(val => {
- console.log(val); //reject apple
- })
- .catch(e => {
- console.log(e)l //catch 被跳过
- })
- .finally(() => {
- // 注意这里没有任何值
- console.log('This function is always executed!');
- started = false; // 清除
- })
- // 错误情况 1
- //Promise 抛出错误
- let started = true;
- let myPromise = new Promise(function(resolve, reject) {
- throw new Error('Error');
- })
- .then(val => {
- console.log(val); // 跳过
- })
- .catch(e => {
- console.log(e)l // 出现错误 catch 被调用
- })
- .finally(() => {
- // 注意这里没有任何值
- console.log('This function is always executed!');
- started = false; // 清除
- })
- // 错误从 "catch" 事件中抛出
- let started = true;
- let myPromise = new Promise(function(resolve, reject) {
- throw new Error('something happend');
- })
- .then(val => {
- console.log(val); // 跳过
- })
- .catch(e => {
- throw new Error('throw another error')
- })
- .finally(() => {
- // 注意这里没有任何值
- console.log('This function is always executed!');
- started = false; // 清除
- // 错误形式的捕获将需要在其他地方调用函数来处理
- })
- const promises = [
- new Promise(resolve => resolve(1)),
- new Promise(resolve => resolve(2)),
- new Promise(resolve => resolve(3)),
- ]
- // 之前
- // for-of 使用常规同步迭代器
- // 不等待 Promise 去解决
- async function test1() {
- for (const obj of promise) {
- console.log(obj) // 记录 3 条 Promise 对象
- }
- }
- // 以后
- // for-wait-of 使用异步迭代
- async function test2() {
- for await (const obj of promises) {
- console.log(obj); // 1,2, 3
- }
- }
- test1() // promise, promise, promise
- test2() // 1,2,3
来源: https://juejin.im/entry/5acdcc0b518825482e393f07