控制流

控制流是指 JavaScript 解释器的执行顺序。 语句。如果脚本没有包含改变其流的语句, 从头到尾逐一执行,一次一行。控制结构 用于确定是否根据 一组定义的条件、重复执行一组语句,或中断 语句序列。

条件语句确定是否应根据某个条件或 更多条件。如果存在以下情况,条件语句会执行它包含的代码: 关联条件(或一组条件)的求值结果为 true。否则, 代码。

if 语句对匹配括号内的条件求值, 跟随。如果圆括号内条件的计算结果为 true,则 语句或 block 语句 之后会执行:

if ( true ) console.log( "True." );
> "True."

if ( true ) {
   
const myString = "True.";
    console
.log( myString );
}
> "True."

如果圆括号内条件的计算结果为 false,则语句 之后将被忽略:

if ( false ) console.log( "True." );

else 关键字紧跟在 if 语句及其 有条件执行的语句指定符合以下条件时要执行的语句: “if”条件的求值结果为 false

if ( false ) console.log( "True." )''
else console.log( "False" );
> "False."

如需将多个 if 语句链接在一起,您可以将 在另一个 else if 语句后有条件执行的语句:

const myCondition = 2;
if ( myCondition === 5 ) console.log( "Five." );
else if ( myCondition === 2 ) console.log( "Two." );

我们强烈建议使用遵循条件语句的块语句语法, 以提高可读性,但 else if 子句通常是一个例外情况:

if ( myCondition === 5 ) {
    console
.log( "Five." );
} else if ( myCondition === 3 ) {
    console
.log( "Three" );
} else {
    console
.log( "Neither five nor three." );
}
> "Neither five nor three."

三元运算符

if 有条件地执行语句。三元运算符(更准确地表示 (但不太常用,称为“三元条件运算符”)是简写形式 有条件地执行表达式顾名思义,三元 是唯一使用三个运算数的 JavaScript 运算符:

  • 要评估的条件,后跟问号 (?)。
  • 当条件的计算结果为 true 时要执行的表达式,后跟 冒号 (:)。
  • 条件计算结果为 false 时要执行的表达式。

这通常用于有条件地设置或传递值:

const myFirstResult  = true  ? "First value." : "Second value.";
const mySecondResult = false ? "First value." : "Second value.";

myFirstResult
;
> "First value."

mySecondResult
;
> "Second value."

switchcase

使用 switch 语句将表达式的值与 使用一个或多个 case 关键字定义的潜在值。此语法为 不同寻常,因为它来自 JavaScript 最早的一些设计决策。 switch...case 语法使用 switch 关键字,后跟一个表达式 需要用圆括号和一对匹配的大括号括起来求值。 switch 的正文可以包含 case 个关键字,通常是一个或多个关键字, 后跟一个表达式或值,再后跟一个冒号 (:)。

当解释器到达 case,且值匹配表达式 在 switch 关键字后面的圆括号中进行求值,它会执行任何 该 case 子句后面的语句:

switch ( 2 + 2 === 4 ) {
 
case false:
    console
.log( "False." );
 
case true:
    console
.log( "True." );
}
> "True."

系统会执行匹配 case 之后的所有语句,即使它们 包含在 block 语句中。

switch ( 2 + 2 === 4 ) {
   
case false:
    console
.log( "False." );
 
case true:
    let myVariable
= "True.";
    console
.log( myVariable );

}
> "True."

使用 switch…case 的一个误区是,找到匹配项后, JavaScript 解释器执行匹配 case 之后的任何语句, 甚至是其他 case 子句中的查询。这称为“穿透”添加到 下一个case

switch ( 2 + 2 === 7 ) {
   
case false:
    console
.log( "False." );
 
case true:
    console
.log( "True." );
}
> "False."
> "True."

为防止跳转,请在每种情况末尾以 break 关键字结尾, 会立即停止对 switch 正文的评估:

switch ( 2 + 2 === 7 ) {
   
case false:
    console
.log( "False." );
   
break;
 
case true:
    console
.log( "True." );
   
break;
}
> "False."

如果没有与条件值匹配的 caseswitch 会选择 default 子句(如果有):

switch ( 20 ) {
   
case 5:
    console
.log( "The value was five." );
   
break;
 
case 10:
    console
.log( "The value was ten." );
   
break;
 
default:
    console
.log( "The value was something unexpected." );
}
> "The value was something unexpected."

不过,回退机制也会应用于 default,可能会导致 意外的结果。要解决此问题,请在 default 语句末尾加上 break, 或将其放在支持请求列表的最后。

switch ( 20 ) {
 
default:
    console
.log( "The value was something unexpected." );
 
case 10:
    console
.log( "The value was ten." );
   
break;
 
case 5:
    console
.log( "The value was five." );
   
break;
}
> The value was something unexpected.
> The value was ten.

由于 case 子句不需要 用于分组的 block 语句 多个语句,casedefault 子句不会创建 词法范围

let myVariable;
switch ( true ) {
 
case true:
    let myVariable
= "True.";
   
break;
 
default:
    let myVariable
= "False.";
   
break;
}
> Uncaught SyntaxError: redeclaration of let myVariable

如需管理范围,请使用块语句:

let myVariable;
switch ( true ) {
 
case true: {
    let myVariable
= "True.";
   
break;
 
}
 
default: {
    let myVariable
= "False.";
   
break;
 
}
}

循环和迭代

循环可让您在满足条件时重复一组语句,或者 直到满足某个条件为止。使用循环来执行固定的一系列指令, 直到获得特定的结果 到达可迭代数据结构的末尾(例如, 数组、映射或集、对象的最终属性或 字符串)。

循环会打断“从上到下”的事务通过迭代来更新脚本的执行流程, 一组语句,直到满足一个或多个条件,或者不再具有 满足条件,具体取决于用于创建循环的语法。循环结束后 会继续执行其后面的语句。在以下示例中, 循环正文中的语句会执行三次, 继续翻译:

let iterationCount = 0;
console
.log( "Pre-loop." );
while( iterationCount < 3 ) {
  iterationCount
++;
  console
.log( "Loop iteration." );
}
console
.log( "Continuing on." );
> "Pre-loop."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."
> "Continuing on."

如果在循环执行期间无法满足条件,循环将继续进行 。这些无限循环是编程中常见的隐患 会导致主执行线程 无限暂停,甚至崩溃浏览器标签页。

只要布尔值 true 仍存在,以下示例就会执行 true。由于布尔值不可变, 就会形成无限循环

console.log( "Pre-loop." );
while( true ) {
console
.log( "Loop iteration." );
}
> "Pre-loop."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."

避免在生产代码中留下无限循环。如果您不小心创建了 要解决此问题,您可以关闭正在运行的浏览器标签页 更新代码,使循环不再是无限循环,然后重新打开 页面。

while

while 循环是使用 while 关键字后跟一对 包含要评估的条件的匹配括号。如果指定的 条件的计算结果最初为 true,语句(或 block 语句) 会执行这些括号。否则,循环将永远不会运行。在每个 迭代,系统会重新评估条件,如果条件仍为 true,则循环 重复。

let iterationCount = 0;
while( iterationCount < 3 ) {
  iterationCount
++;
  console
.log( `Loop ${ iterationCount }.` );
}
> "Loop 1."
> "Loop 2."

如果解释器在 while 循环中发现 continue 语句,则会停止该语句 迭代,重新评估条件,并在可能的情况下继续循环:

let iterationCount = 0;
while( iterationCount <= 5 ) {
  iterationCount
++;
 
if( iterationCount === 3 ) {
   
continue;
 
}
  console
.log( `Loop ${ iterationCount }.` );
}
console
.log( "Loop ended." );
> "Loop 1."
> "Loop 2."
> "Loop 4."
> "Loop 5."
> "Loop ended."

如果解释器在 while 循环中找到 break 语句,则该迭代 停止,并且条件不会被重新评估,从而使解释器能够继续运行:

let iterationCount = 1;
while( iterationCount <= 5 ) {
 
if( iterationCount === 3 ) {
    console
.log(`Iteration skipped.``);`
   
break;
 
}
  console
.log( `Loop ${ iterationCount }.` );
  iterationCount
++;
}
console
.log( "Loop ended." );
> "Loop 1."
> "Loop 2."
> "Iteration skipped.
> "
Loop ended."

您可以使用 while 迭代指定的次数,如 但 while 最常见的用例是 长度不确定:

let randomize = () => Math.floor( Math.random() * 10 );
let randomNum
= randomize();
while( randomNum !== 3 ){
  console
.log( `The number is not ${ randomNum }.` );
  randomNum
= randomize();
}
console
.log( `The correct number, ${ randomNum }, was found.` );
> "The number is not 0."
> "The number is not 6."
> "The number is not 1."
> "The number is not 8."
> "The correct number, 3, was found."

dowhile

do...whilewhile 循环的变体,其中条件语句 评估在循环每次迭代的结束时发生。这意味着 循环正文至少会执行一次。

如需创建 do...while 循环,请使用 do 关键字,后跟语句 (或块语句) 在循环的每次迭代时执行。请在该声明后面紧接着添加 while 和包含要计算的条件的匹配括号。时间 此条件的计算结果不再为 true,循环结束。

let iterationCount = 1;
do {
  console
.log( `Loop ${ iterationCount }.` );
  iterationCount
++;
} while ( iterationCount < 3 );
> "Loop 1."
> "Loop 2."
> "Loop 3."

while 循环一样,do...while 最常见的用例是 长度不确定:

let randomNum;
do {
  randomNum
= ( () => Math.floor( Math.random() * 10 ) )();
  console
.log( `Is the number ${ randomNum }?` );
} while ( randomNum !== 3 );
console
.log( `Yes, ${ randomNum } was the correct number.` );
> "Is the number 9?"
> "Is the number 2?"
> "Is the number 8?"
> "Is the number 2?"
> "Is the number 3?"
> "Yes, 3 was the correct number."

for

使用 for 循环可以遍历已知数量。在旧版代码库中,这是 通常用于遍历数组中的元素。

如需创建 for 循环,请使用 for 关键字,后跟一对圆括号 按顺序接受以下三个表达式,并以 分号:

  1. 循环开始时要计算的表达式
  2. 确定循环是否应继续的条件
  3. 每个循环结束时要执行的表达式

在这些圆括号后面添加语句(通常是 block 语句) 在循环期间执行的

for( let i = 0; i < 3; i++ ) {
  console
.log( "This loop will run three times.")
}

第一个表达式用于初始化充当计数器的变量。这个 表达式在循环第一次迭代之前计算一次。您可以 像初始化任何其他变量一样,使用 let(或以往使用 var)初始化此变量 变量,其作用域就是循环的正文。这些变量可以有 有效的标识符,但对于“迭代”系统,系统通常将其作为 i 调用或“index”。 这似乎与 可预测的标识符名称的最佳做法 但该惯例非常成熟,足以让 概览。由于已编入索引的集合未编入索引, 这些变量的初始值几乎总是 0

与其他形式的循环一样,条件是一个表达式,用于确定 是否应执行循环。这通常用于设置 迭代计数器的边界。解释器先评估条件, 系统首次执行 for 循环。如果该条件最初并未执行, 求值为 true,则循环正文不会执行。

最终表达式会在每次循环迭代结束时执行。 它通常用于将标识符递增 1。

在旧版中,您最常看到 for 循环遍历数组 代码库。在这些情况下,为继续循环指定的条件是 迭代计数小于或等于要迭代数组的长度 用于跟踪当前迭代次数的变量可用于 向上查找数组中与该索引关联的值, 要按顺序执行操作的数组:

var myArray = [ true, false, true ];
for( let i = 0; i <= myArray.length; i++ ) {
  console
.log( myArray[ i ] );
}
> true
> false
> true

这种方法已经被淘汰,取而代之的是更现代的 循环遍历可迭代数据结构

for [...] of [...]

使用 for...of... 循环来迭代存储在 可迭代数据结构,例如数组、集或映射。

for...of... 循环使用 for 关键字,后跟一对圆括号 包含变量,后跟 of,然后是迭代的数据结构 结束。此处的变量可以是使用 letconstvar,之前在当前作用域内声明的变量,一个对象 属性,也可以是 解构赋值。 它包含与当前迭代对应的元素的值 循环。

const myIterable = [ true, false, true ];
for( const myElement of myIterable ) {
  console
.log( myElement );
}
> true
> false
> true

在此例中,即使 myElementmyElement,也使用 const 在循环的每次迭代中给出一个新值。这是因为 使用 letconst 声明的作用域限定为 循环。该变量会在每次迭代开始时初始化,并在 迭代结束时。

for...in...

使用 for...in... 循环来迭代对象的可枚举属性, 包括可枚举的继承属性。与 for...of... 循环一样, for...in... 循环使用 for 关键字,后跟一对圆括号 包含一个变量,该变量包含与 循环的当前迭代。该变量后跟 in 关键字,那么要迭代的对象:

const myObject = { "myProperty" : true, "mySecondProperty" : false };
for( const myKey in myObject ) {
  console
.log( myKey );
}
> "myProperty"
> "mySecondProperty"

同样,尽管 myKey 的值会随循环的每次迭代而变化, 您可以毫无错误地使用 const,因为该变量实际上已被舍弃 然后在每次迭代结束时重新创建

与每个属性键相关联的值无法直接提供给 for...in... 语法。不过,由于循环可以访问 每次迭代,您可以使用该键“查找”其值:

const myObject = { "myProperty" : true, "mySecondProperty" : false };
for( const myKey in myObject ) {
 
const myValue = myObject[ myKey ];
  console
.log( `${ myKey } : ${ myValue }` );
}
> "myProperty : true"
> "mySecondProperty : false"

从内置构造函数继承的属性是不可枚举的,这意味着 for...in... 不会遍历从 Object 继承的属性 构造函数。不过,对象的 原型链

const myPrototype = { "protoProperty" : true };
const myObject = Object.create( myPrototype, {
    myProperty
: {
    value
: true,
    enumerable
: true
   
}
});
for ( const myKey in myObject ) {
 
const myValue = myObject[ myKey ];
  console
.log( `${ myKey } : ${ myValue }` );
}
> "myProperty : true"
> "protoProperty : true"

JavaScript 提供了内置方法,用于确定某个属性是否为 对象的直接属性,而不是对象原型上的属性 连锁店:现代 Object.hasOwn() 和旧版 Object.prototype.hasOwnProperty() 方法。这些 方法用于评估指定属性是继承的(还是未声明的), 仅针对指定对象的直接属性返回 true

const myPrototype = { "protoProperty" : true };
const myObject = Object.create( myPrototype, {
    myProperty
: {
    value
: true,
    enumerable
: true
   
}
});
for ( const myKey in myObject ) {
 
const myValue = myObject[ myKey ];
 
if ( Object.hasOwn( myObject, myKey ) ) {
    console
.log( `${ myKey } : ${ myValue }` );
 
}
}
> "myProperty : true"

还有三种静态方法,每个方法返回一个数组,该数组由 对象的可枚举键 (Object.keys())、值 (Object.values()) 或 键值对 (Object.entries()):

const myObject = { "myProperty" : true, "mySecondProperty" : false };
Object.keys( myObject );
> Array [ "myProperty", "mySecondProperty" ]

这样,您就可以使用 解构赋值) 而不包含该对象的原型所拥有的属性:

const myPrototype = { "protoProperty" : "Non-enumerable property value." };
const myObject = Object.create( myPrototype, {
    myProperty
: {
    value
: "Enumerable property value.",
    enumerable
: true
   
}
});

for ( const propKey of Object.keys( myObject ) ) {
  console
.log( propKey );
}
> "myProperty"

for ( const propValue of Object.values( myObject ) ) {
  console
.log( propValue );
}
> "Enumerable property value."

for ( const [ propKey, propValue ] of Object.entries( myObject ) ) {
  console
.log( `${ propKey } : ${ propValue }` );
}
> "myProperty : Enumerable property value."

forEach()

Array 提供的 forEach() 方法, MapSet、 和 NodeList 构造函数提供了一种实用的简写形式,可用于迭代数据 所有对象在回调函数上下文中构建。与其他形式的循环不同, 使用任何 forEach() 方法创建的循环无法使用 breakcontinue

forEach 是每个数据结构的原型拥有的方法。每forEach 方法需要将回调函数作为参数,虽然两者在 调用该函数时包含的参数。第二个(可选) 参数指定 this 值,该值将用作 回调函数。

Array.forEach 配合使用的回调函数会提供包含以下内容的形参: 当前元素的值、当前元素的索引以及调用 forEach 方法的数组:

const myArray = [ true, false ];
myArray
.forEach( ( myElement, i, originalArray ) => {
  console
.log( i, myElement, originalArray  );
});
> 0 true Array(3) [ true, false ]
> 1 false Array(3) [ true, false ]

Map.forEach 配合使用的回调函数会提供包含 与当前元素相关联的值,与当前元素相关联的键 元素,并且会在 Map 中调用 forEach 方法:

const myMap = new Map([
 
['myKey', true],
 
['mySecondKey', false ],
]);
myMap
.forEach( ( myValue, myKey, originalMap ) => {
    console
.log( myValue, myKey, originalMap  );
});
> true "myKey" Map { myKey true, mySecondKey false }
> false "mySecondKey" Map { myKey true, mySecondKey false }

Set.forEach 回调包含类似的参数。由于 Set 不包含 索引或键与值不同,那么第二个参数会改为提供 多余、可忽略的值,严格地保证语法与 其他 forEach 方法。

const mySet = new Set([ true, false ]);
mySet
.forEach( ( myValue, myKey, originalSet ) => {
  console
.log( myValue, myKey, originalSet  );
});
> true true Set [ true, false ]
> false false Set [ true, false ]

迭代器

可迭代对象是指由单个元素构成的任何数据结构,可迭代 并对其进行了反复改进。迭代器是 可迭代对象,这需要遵循迭代器协议,这意味着它必须实现 一个 next() 方法,该方法逐个遍历其中包含的元素, 每次调用该方法时,系统会为每个序列 特定格式的元素。

JavaScript 内置的可迭代数据结构(如 ArrayMapSet)不属于和/或 但它们都继承了 iterator 方法,您可以使用 @@iterator 知名符号, 该函数会返回根据可迭代数据结构创建的迭代器对象:

const myIterable = [ 1, 2, 3 ];
const myIterator = myIterable[ Symbol.iterator ]();

myIterable
;
> (3) [1, 2, 3]

myIterator
;
> Array Iterator {}

对迭代器调用 next() 方法可逐步遍历迭代器中的元素。 每次都包含一个 ,且每次调用返回一个对象,该对象包含两个 属性:value(包含当前元素的值)和 done,这是一个布尔值,指明迭代器是否已通过 数据结构。仅当调用 next() 时,done 的值为 true 会试图访问 迭代器。

const myIterable = [ 1, 2, 3 ];
const myIterator = myIterable[ Symbol.iterator ]();

myIterator
.next();
> Object { value: 1, done: false }

myIterator
.next();
> Object { value: 2, done: false }

myIterator
.next();
> Object { value: 3, done: false }

myIterator
.next();
> Object { value: undefined, done: true }

生成器函数

使用 function* 关键字(请注意星号)来声明生成器 函数或定义生成器函数表达式:

function* myGeneratorFunction() { };

迭代器一样,生成器函数也会维护状态。调用 generator 函数返回一个新的 Generator 对象,但不会立即 在函数正文中执行代码:

function* myGeneratorFunction() {
  console
.log( "Generator function body ")
};
const myGeneratorObject = myGeneratorFunction();

myGeneratorObject
;
> Generator {  }

typeof myGeneratorObject;
> "object"

生成器对象遵循迭代器协议。每次调用 生成器函数返回的 next()yield 表达式确定, 该函数会暂停生成器函数的执行,并返回 包含 yield 关键字的表达式。之后对 next() 的调用 继续执行该函数,在下一个 yield 表达式处暂停, 返回关联值。

function* myGeneratorFunction() {
 
yield "My first yielded value.";
 
yield "My second yielded value.";
};
const myGeneratorObject = myGeneratorFunction();

myGeneratorObject
.next();
> Object { value: "My first yielded value.", done: false }

myGeneratorObject
.next();
> Object { value: "My second yielded value.", done: false }

如果在未使用 yield 指定其他值后调用 next()returnthrow(如果发生错误),则该函数的其余部分为 body 执行,返回的对象具有 valueundefineddone true 的属性:


function* myGeneratorFunction() {
    console
.log( "Start of the generator function." );
   
yield "First";
    console
.log( "Second part of the generator function."  );
   
yield "Second";
    console
.log( "Third part of the generator function." );
   
yield "Third";
};
const myGeneratorObject = myGeneratorFunction();

myGeneratorObject
.next();
> "Start of the generator function."
> Object { value: "First", done: false }

myGeneratorObject
.next();
> "Second part of the generator function."
> Object { value: "Second", done: false }

myGeneratorObject
.next();
> "Third part of the generator function."
> Object { value: "Third", done: false }

myGeneratorObject
.next();
> Object { value: undefined, done: true }

请仅对生成器函数返回的对象使用 next(),而不是 生成器函数本身。否则,每次调用生成器函数 用于创建新的生成器对象:

function* myGeneratorFunction() {
 
yield "First";
 
yield "Second";
};

myGeneratorFunction
().next();
> Object { value: "First", done: false }

myGeneratorFunction
().next();
> Object { value: "First", done: false }

与任何函数一样,生成器函数在遇到 return 时停止运行 关键字。然后,它会向调用上下文返回一个对象,其中包含 以及值为 truedone 属性。

function* myGeneratorFunction() {
 
yield 1;
 
yield 2;
 
return 3;
};
const myGeneratorObject = myGeneratorFunction();

myGeneratorObject
.next().done;
> Object { value: 1, done: false }

myGeneratorObject
.next().done;
> Object { value: 2, done: false }

myGeneratorObject
.next();
> Object { value: 3, done: true }

yield 表达式可以采用标识符的某些语义, 它允许双向“通信”然后返回该 Pod 的暂停部分 生成器函数。当一个值传递到生成器的 next() 方法时,如下所示 参数,则它会替换与上一个处于暂停状态的 yield 表达式:

function* myGeneratorFunction() {
   
const firstYield = yield;
   
yield firstYield + 10;
};
const myGeneratorObject = myGeneratorFunction();

myGeneratorObject
.next();
> Object { value: undefined, done: false }

myGeneratorObject
.next( 5 );
> Object { value: 15, done: false }

请注意,这会替换与 之前的 yield,而不只是将前一个 yield 的值重新分配给 在 next() 中指定的值:

function* myGeneratorFunction() {
   
const firstYield = yield;
   
const secondYield = yield firstYield + 100;
   
yield secondYield + 10;
};
const myGeneratorObject = myGeneratorFunction();

myGeneratorObject
.next();
> Object { value: undefined, done: false }

myGeneratorObject
.next( 10 ); // Can be thought of as changing the value of the `firstYield` variable to `10
> Object { value: 110, done: false }

myGeneratorObject
.next( 20 ); // Can be thought of as changing the value of the `secondYield` variable to `20`, _not_ `20 + 100;`
> Object { value: 30, done: false }

传递到第一次调用 next() 的任何参数都会被忽略,因为没有 之前的 yield 表达式,以接受该值。与其他任何函数一样 传递给初始生成器函数调用的参数在 生成器函数正文的作用域:

function* myGeneratorFunction( startingValue ) {
    let newValue
= yield startingValue + 1;
    newValue
= yield newValue + 10;
   
yield startingValue + 20;
};
const myGeneratorObject = myGeneratorFunction( 2 );

myGeneratorObject
.next( 1 );
> Object { value: 3, done: false }

myGeneratorObject
.next( 5 );
> Object { value: 15, done: false }

myGeneratorObject
.next( 10 );
Object { value: 22, done: false }

yield*(注意星号)运算符与可迭代对象一起使用,例如 另一个生成器函数,用于迭代并生成每个值的操作数 返回:

function* mySecondaryGenerator() {
 
yield 2;
 
yield 3;
}

function* myGenerator() {
 
yield 1;
 
yield* mySecondaryGenerator();
 
yield 4;
 
return 5;
}

const myIterator = myGenerator();

myIterator
.next();
> Object { value: 1, done: false }

myIterator
.next();
> Object { value: 2, done: false }

myIterator
.next();
> Object { value: 3, done: false }

myIterator
.next();
> Object { value: 4, done: false }

myIterator
.next();
> Object { value: 5, done: true }

异步 JavaScript

虽然 JavaScript 从根本上说是同步的 有一些机制可让开发者利用 事件循环 异步任务

promise

Promise 是 promise 中未知的值的占位符 创建。它是一个指定异步操作的容器 操作成功或失败时,要执行的操作 以及产生的值。

使用带有内置 Promisenew 运算符创建 Promise 实例 构造函数。此构造函数接受名为“executor”的函数 作为参数。该执行器函数通常用于执行一个或多个 异步操作,并规定 Promise 遵守 视为已成功履单或遭拒。Promise 定义为 pending 在执行器函数运行时运行Executor 完成后,Promise 如果满足以下条件,则会被视为已执行(或在某些文档来源中为已解决) 执行器函数及其所执行的异步操作 成功执行;如果执行器函数遇到错误,则会遭拒;或者 所执行的异步操作失败。在 Promise 执行后或 则相应交易会被视为已结清

const myPromise = new Promise( () => { });

构造函数会使用两个参数调用执行器函数。参数 是用于手动执行或拒绝 Promise 的函数:

const  myPromise = new Promise( ( fulfill, reject ) => { });

用于执行或拒绝 Promise 的函数通过调用 Promise 值作为参数(通常是表示拒绝的错误):

const myPromise = new Promise( ( fulfill, reject ) => {
 
const myResult = true;
  setTimeout
(() => {
   
if( myResult === true ) {
        fulfill
( "This Promise was successful." );    
   
} else {
        reject
( new Error( "This Promise has been rejected." ) );
   
}
 
}, 10000);
});

myPromise
;
> Promise { <state>: "pending" }

myPromise
;
> Promise { <state>: "fulfilled", <value>: "This Promise was successful." }

Promise 链

您可以使用 then()catch() 和 从 Promise 构造函数继承的 finally() 方法。以上每一个 方法会返回一个 Promise,您可以立即使用 then() 对其执行操作。 catch()finally(),使您能够链接生成的 Promise。

then() 提供两个回调函数作为参数。使用第一个实现 生成的 Promise,以及另一个用于拒绝它的 Promise。这两种方法都接受 实参,以便为生成的 Promise 赋予其值。

const myPromise = new Promise( ( fulfill, reject ) => {
 
const myResult = true;
  setTimeout
(() => {
   
if( myResult === true ) {
        fulfill
( "This Promise was fulfilled." );    
   
} else {
        reject
( new Error( "This Promise has been rejected." ) );
   
}
 
}, 100);
});

myPromise
.then( successfulResult => console.log( successfulResult ), failedResult => console.error( failedResult ) );
> "This Promise was successful."

您还可以使用 then() 仅处理已履行状态,使用 catch 来 处理拒绝状态。使用包含catch 值:

const myPromise = new Promise( ( fulfill, reject ) => {
 
const myResult = false;
  setTimeout
(() => {
   
if( myResult === true ) {
        fulfill
( "This Promise was fulfilled." );    
   
} else {
        reject
( new Error( "This Promise has been rejected." ) );
   
}
 
}, 100);
});

myPromise
 
.then( fulfilledResult => console.log(fulfilledResult ) )
 
.catch( rejectedResult => console.log( rejectedResult ) )
 
.finally( () => console.log( "The Promise has settled." ) );
> "Error: This Promise has been rejected."
> "The Promise has settled."

thencatch 不同,后两者允许处理程序函数在发生 Promise 时运行 实现或拒绝时,作为参数传递给 finally 的函数 无论 Promise 是已执行还是被拒绝,系统都会调用该方法。 调用处理程序函数时不带任何参数, 与从 Promise 传递的值配合使用,仅在 Promise 已完成。

并发

Promise 构造函数提供了四种方法,用于处理多个相关的 promise,使用包含 Promise 对象的 iterable。这些 每个方法都会返回一个 Promise,后者会根据状态 与传递给它的 Promise 相关联。例如,Promise.all() 会创建一个 仅当传递给该方法的每个 Promise 都执行时,系统才会执行该方法:

const firstPromise  = new Promise( ( fulfill, reject ) => fulfill( "Successful. ") );
const secondPromise = new Promise( ( fulfill, reject ) => fulfill( "Successful. ") );
const thirdPromise  = new Promise( ( fulfill, reject ) => fulfill( "Successful. ") );
const failedPromise = new Promise( ( fulfill, reject ) => reject( "Failed.") );
const successfulPromises = [ firstPromise, secondPromise, thirdPromise ];
const oneFailedPromise = [ failedPromise, ...successfulPromises ];

Promise.all( successfulPromises )
 
.then( ( allValues ) => {
    console
.log( allValues );
 
})
 
.catch( ( failValue ) => {
    console
.error( failValue );
 
});
> Array(3) [ "Successful. ", "Successful. ", "Successful. " ]

Promise.all( oneFailedPromise  )
   
.then( ( allValues ) => {
      console
.log( allValues );
   
})
   
.catch( ( failValue ) => {
     console
.error( failValue );
   
});
> "Failed."

Promise 并发方法如下所示:

Promise.all()
仅在提供的所有 Promise 都执行时才执行。
Promise.any()
所提供的任一 Promise 执行,且仅拒绝执行 如果所有 Promise 都被拒绝,则会发生此错误。
Promise.allSettled()
在 promise 完成后,无论其结果如何,都会执行。
Promise.race()
根据第一个 promise 的结果而拒绝或履行,以解决结算问题; 并忽略稍后解决的所有 Promise。

async/await

函数声明前面使用 async 关键字时 或函数表达式、 函数返回的值将作为已满足的 Promise 返回,其中包含 值。这样,您就可以使用同一个 同步开发。

async function myFunction() {
 
return "This is my returned value.";
}

myFunction
().then( myReturnedValue => console.log( myReturnedValue ) );
> "This is my returned value."

await 表达式用于暂停异步函数的执行,而 关联的 promise 已结清。promise 得到解决后, await 表达式是 Promise 的已执行或拒绝值。

async function myFunction() {
 
const myPromise  = new Promise( ( fulfill, reject ) => { setTimeout( () => fulfill( "Successful. "), 5000 ); });
 
const myPromisedResult = await myPromise;
 
return myPromisedResult;
}

myFunction
()
 
.then( myResult => console.log( myResult ) )
 
.catch( myFailedResult => console.error( myFailedResult ) );
> "Successful."

await 表达式中包含的任何非 Promise 值都会作为 已实现的 Promise:

async function myFunction() {
 
const myPromisedResult = await "String value.";
 
return myPromisedResult;
}

myFunction
()
 
.then( myResult => console.log( myResult ) )
 
.catch( myFailedResult => console.error( myFailedResult ) );
> "String value."

检查您的理解情况

您会使用哪种循环来迭代已知数量?

for
do...while
while