JavaScript语法相关知识汇总(三)

第一章、模块化

        在JavaScript中,模块化是一种将代码分割成多个独立文件或模块的编程风格。这种编程风格旨在提高代码可维护性、可扩展性和重用性。

        node编程中最重要的思想之一就是模块,而正是这个思想,让JavaScript的大规模工程成为可能。模块化编程在js界流行,也是基于此,随后在浏览器端,requirejs和seajs之类的工具包也出现了,可以说在对应规范下,commonjs统治了ES6之前的所有模块化编程,即使现在,在ES6 module被完全实现之前,还是这样。node的module遵循CommonJS规范,requirejs遵循AMD,seajs遵循CMD,虽各有不同,但总之还是希望保持较为统一的代码风格,ES6之后可以使用import 导入export导出,但还是得看当前的node版本是否支持该功能。

  • CommonJS => NodeJS、Browserify
  • AMD => requireJS
  • CMD => seaJS

模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。

模块化的好处

  • 防止命名冲突
  • 代码复用
  • 高维护性

一、模块化的产品

1、CommonJS

        CommonJS是Node.js采用的模块化方案,它采用require和module.exports来导入和导出模块。

        CommonJS是一种同步加载的模块化方案,模块的导入和导出是在运行时进行的。

// 导出一个变量
module.exports.name = 'John';

// 导出一个函数
module.exports.greet = function(name) {
  console.log(`Hello, ${name}!`);
}
// 导入一个模块
const module = require('./module.js');

2、ES6模块

1、ES6模块是JavaScript标准库提供的模块化方案,它采用import和export来导入和导出模块。

2、ES6模块是一种静态加载的模块化方案,模块的导入和导出是在编译时进行的,能够实现更好的性能和可靠性。

// 导出一个变量
export const name = 'John';

// 导出一个函数
export function greet(name) {
  console.log(`Hello, ${name}!`);
}
// 导入一个模块
import { name, greet } from './module.js';

3、AMD

1、AMD(Asynchronous Module Definition)是一种异步加载的模块化方案,它采用define和require来定义和加载模块。

2、AMD通常用于在浏览器端异步加载模块,适用于Web应用开发。

// 定义一个模块
define('module', [], function() {
  return {
    name: 'John',
    greet: function(name) {
      console.log(`Hello, ${name}!`);
    }
  };
});
// 加载一个模块
require(['module'], function(module) {
  console.log(module.name);
  module.greet('Jack');
});

4、CMD

1、CMD(Common Module Definition)是SeaJS采用的模块化方案,它也是一种异步加载的模块化方案,采用define和require来定义和加载模块。

2、CMD与AMD类似,也适用于在浏览器端异步加载模块,但相比于AMD更注重模块的延迟执行。

// 定义一个模块
define(function(require, exports, module) {
  exports.name = 'John';
  exports.greet = function(name) {
    console.log(`Hello, ${name}!`);
  };
});
// 加载一个模块
require(['module'], function(module) {
  console.log(module.name);
  module.greet('Jack');
});

二、ES6模块语法

        ES6模块是JavaScript中的一种模块化系统,它是在ECMAScript 6(ES6)中引入的,并且是一种被现代浏览器和Node.js支持的标准模块语法。

模块功能主要由两个命令构成:export 和 import。

  • export 命令用于规定模块的对外接口
  • import 命令用于输入其它模块提供的功能

1、模块化的导出

1.1、分别导出

        分别导出(Named Export)是指将模块中的多个变量或函数分别导出,以便其他模块可以使用它们。分别导出使用 export 关键字实现。

// module.js

export const name = 'ChatGPT';

export function greet() {
  console.log(`Hello, ${name}!`);
}

        上面代码中,我们通过 export 关键字将 namegreet 分别导出。其他模块可以通过 import 关键字导入这两个变量或函数:

1.2、统一导出

        统一导出(Export)是指将模块中的多个变量或函数统一导出,以便其他模块可以一次性导入它们。统一导出使用 export 关键字和花括号({})实现。

        当一个模块需要导出的内容非常多时,可以使用统一导出的方式一次性导出多个变量或函数。此外,统一导出还可以和分别导出结合使用,从而实现更灵活的导入和导出。

const foo = 1;
const bar = 2;

function greet() {
  console.log('Hello, world!');
}

export { foo, bar, greet };

       上面代码中,我们通过 export 关键字和花括号将 foobargreet 统一导出。其他模块可以通过 import 关键字导入这些变量或函数:

1.3、默认导出

        默认导出提供了一种方便的方式,可以将一个值作为默认导出,其他模块可以直接导入默认值,而不需要使用花括号和变量名。同时,默认导出可以和其他导出方式结合使用,从而实现更灵活的导入和导出。

        需要注意的是,默认导出只能导出一个值,而不能导出多个值。如果需要导出多个值,可以使用分别导出或统一导出的方式。

        另外,需要注意的是,虽然默认导出可以使用任意的变量名来导入默认值,但是通常情况下我们会使用与默认导出的名称相同的变量名来导入默认值,这样可以使代码更加清晰易懂。

第一种方式默认导出变量:

// module.js

export default value;

        上面代码中,我们使用 export default 关键字将 value 作为默认导出。默认导出的值可以是任意类型,包括对象、函数、字符串、数值等。

        其他模块可以通过 import 关键字导入默认导出的值

第二种方式默认导出函数:

// module.js

function add(a, b) {
  return a + b;
}

export default add;

第三种方式默认导出普通对象:

// module.js

const person = {
  name: 'John',
  age: 30,
  sayHello() {
    console.log(`Hello, my name is ${this.name}, and I'm ${this.age} years old.`);
  }
};

export default person;

第四种方式默认导出类

// module.js

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  sayHello() {
    console.log(`Hello, my name is ${this.name}, and I'm ${this.age} years old.`);
  }
}

export default Person;

2、模块化的导入(静态导入)

JS报错SyntaxError Cannot use import statement outside a module解决方案

        静态导入是指在代码编译时就已经确定导入的模块和模块中导出的内容,可以使用import语句进行静态导入 

2.1、全部导入语法

语法:

import * as 别名 from '引入模块路径.js';     一定要加js后缀,别名可选

2.2、解构赋值导入语法

语法:

import {对象名1 as 别名1 , 对象名2 as 别名2} from '引入模块路径.js'; 

一定要加js后缀,别名可选

2.3、分别导出对应的导入

// app.js

import { name, greet } from './module.js';

console.log(name); // 'ChatGPT'

greet(); // 'Hello, ChatGPT!'

上面代码中,我们通过 import 关键字将 namegreet 分别导入。

2.4、统一导出对应的导入

// app.js

import { foo, bar, greet } from './module.js';

console.log(foo, bar); // 1, 2

greet(); // 'Hello, world!'

2.5、默认导出对应的导入

第一种方式导入变量:

// app.js

import defaultValue from './module.js';

上面代码中,我们使用 import 关键字将默认导出的值导入到 defaultValue 变量中。

第二种方式导入函数:

// app.js

import add from './module.js';

console.log(add(1, 2)); // 3

第三种方式导入普通对象

// app.js

import person from './module.js';

console.log(person.name); // 'John'
console.log(person.age); // 30

person.sayHello(); // 'Hello, my name is John, and I'm 30 years old.'

第四种方式导入类

// app.js

import Person from './module.js';

const person = new Person('John', 30);

console.log(person.name); // 'John'
console.log(person.age); // 30

person.sayHello(); // 'Hello, my name is John, and I'm 30 years old.'

3、动态导入

        动态导入是指在代码运行时才会确定导入的模块和模块中导出的内容,可以使用import()函数进行动态导入

import('./module.js')
  .then(module => {
    const { foo } = module;
    // ...
  })
  .catch(error => {
    // ...
  });

        静态导入可以直接在代码中使用,而动态导入则需要使用Promise来异步加载模块。动态导入的主要用途是在需要延迟加载模块时使用,可以提高应用程序的性能和响应速度。 

4、组合导出与导入

4.1、案例一

分别导出部分内容,统一导出其余内容

// module.js

const foo = 1;
const bar = 2;

function greet() {
  console.log('Hello, world!');
}

function sayGoodbye() {
  console.log('Goodbye, world!');
}

export { foo, bar };
export { greet as hello };
export default sayGoodbye;

上面代码中,我们通过 export 关键字和花括号将 foobar 分别导出,将 greet 通过别名 hello 分别导出,将 sayGoodbye 默认导出。其他模块可以通过 import 关键字导入这些变量或函数:

// app.js

import { foo, bar, hello } from './module.js';
import goodbye from './module.js';

console.log(foo, bar); // 1, 2

hello(); // 'Hello, world!'

goodbye(); // 'Goodbye, world!'

        上面代码中,我们通过 import 关键字将 foobar 分别导入,将 greet 通过别名 hello 分别导入,将 sayGoodbye 默认导入。

4.2、案例二

m1.js

//分别暴露
export let school = '沈阳建筑大学';

export function study (){
    console.log('I like study!');
}

m2.js

//统一暴露
let school = '沈阳建筑大学';

function findjob(){
    console.log('我要找工作!');
}

export {school,findjob};

m3.js

//方式三:默认暴露
export default {
    school: '沈阳建筑大学',
    change(){
        console.log('我要改变自己!');
    }
}

默认导出要加default使用

index.js

//引入m1.js模块内容
import * as m1 from './m1.js';

//引入m2.js模块内容
import * as m2 from './m2.js';

//引入m3.js模块内容
import * as m3 from './m3.js';

console.log('使用m1的内容=============');
console.log(m1.school);
m1.study();

console.log('使用m2的内容=============');
console.log(m2.school);
m2.findjob();

console.log('使用m3的内容=============');
console.log(m3.default.school);
m3.default.change();
m3.default.aaa();

index.js

//引入m1.js模块内容
import {school,study} from './m1.js';

//引入m2.js模块内容
import {school as s,findjob} from './m2.js';

//引入m3.js模块内容
// import {default as m3} from './m3.js';
import m3 from './m3.js';

console.log('使用m1的内容=============');
console.log(school);
study();

console.log('使用m2的内容=============');
console.log(s);
findjob();

console.log('使用m3的内容=============');
console.log(m3.school);
m3.change();
m3.aaa();

注意:针对默认暴露还可以直接 import m3 from "./m3.js" 

三、commonJS模块语法

        JavaScript 默认采用 CommonJS 模块语法,CommonJS 模块语法是一种后端(服务器端)的模块加载机制,通常在 Node.js 等服务器端 JavaScript 环境中被广泛采用。如果你在浏览器端使用 JavaScript,可以使用 ES6 模块语法或者其他模块加载器(例如 RequireJS)来组织和管理你的代码

        CommonJS 是一种用于 JavaScript 的模块化标准,主要用于在 Node.js 中实现模块化。CommonJS 规范定义了如何创建、导入和导出模块,以及如何管理模块之间的依赖关系。在 CommonJS 中,每个模块都是一个独立的文件,拥有自己的作用域和命名空间,可以在需要时被导入到其他模块中使用。

        CommonJS是一种Node.js中广泛使用的模块系统,它是一种同步加载模块的方式,主要用于服务器端的JavaScript编程。CommonJS定义了一些规范,包括模块定义、模块加载、模块标识符等。

        在CommonJS模块系统中,每个文件都是一个模块,模块内部的变量和函数默认都是私有的,即只有在模块内部才能访问。如果想要在模块外部访问模块内部的变量和函数,需要使用 module.exportsexports 来导出模块中的成员,然后使用 require 来加载模块。

        CommonJS模块系统是同步加载模块的,也就是说,在加载模块时会阻塞后面的代码,直到模块加载完毕(这意味着当一个模块被导入时,它的代码会被立即执行,并且模块中的所有导出都会被初始化。这也意味着,在模块中定义的任何变量或函数都只能在该模块内部使用,并不能被其他模块访问)。这样会影响性能,因此在浏览器端通常不使用CommonJS模块系统,而使用其他模块系统,例如ES6模块系统。

1、语法详解

require的基本使用,理解module.exports与exports_Robin_Hood_的博客-CSDN博客

JS语法之:require_js require()_王同学LM的博客-CSDN博客

CommonJS规范 -- JavaScript 标准参考教程(alpha)

1.1、CommonJS模块规范及规范化内容

         CommonJS 是一套代码规范, 目的是为了构建 JavaScript 在浏览器之外的生态系统 (服务器端, 桌面端).,通过该规范使JavaScript具备开发复杂应用、跨平台的能力
        
    CommonJS模块规范化的内容

  • (1)导出模块:moudle.exports 导出模块
  • (2)导入模块:require('模块名称')

1.2、moudle对象

每个导出的模块都有一个moudle对象,该对象包含的属性有:

  • moudle.exports:表示当前模块对外输出的接口,其他模块引用的是moudle.exports导出的变量  。
  • exports变量:指向moudle.exports,为了操作的方便,它不能直接指向一个值

这等同在每个模块头部,有一行这样的命令。

 var exports = module.exports;

2、moudle.exports导出和exports导出详解

        在Node.js中,moduleexports都是内置的特殊对象,用于控制模块的导入和导出。它们都是Node.js中的全局变量。但是,它们的本质是不同的。

   module.exports是真正的导出对象,而exports只是对module.exports的一个全局引用。默认情况下,exports指向module.exports的同一个对象。也就是说,如果你给exports赋一个新的对象,它只会在当前模块中起作用,而不会影响到其他模块。如果你想导出一个新的对象,你必须直接修改module.exports,而不能修改exports

可理解为:

1、module是一个对象,module.exports是module对象中的子对象,exports相当于直接把module.exports对象从module对象中拿出来,新建了一个对象

2、导出的实际上是module.exports或exports对象本身

1、module.exports 是真正的导出对象,而 exports 只是 module.exports 的一个别名。因此,当使用 module.exports 导出一个变量或函数时,相当于覆盖了 exports 对象中相应的属性。

例如,下面这个例子中,使用 module.exports 导出一个函数 foo

module.exports = {
  foo: function() {
    console.log('foo');
  }
}

相当于:

exports.foo = function() {
  console.log('foo');
}

2、如果一个模块同时使用了 module.exportsexports 导出变量或函数,那么最终导出的是 module.exports 所指向的对象,而 exports 对象中的属性会被忽略。这是因为 exports 只是 module.exports 的一个引用,而不是真正的导出对象。

例如,下面这个例子中,使用 module.exports 导出一个对象 obj1,并使用 exports 导出一个对象 obj2

module.exports = {
  foo: function() {
    console.log('foo');
  }
}

exports.obj2 = {
  bar: function() {
    console.log('bar');
  }
}

最终导出的是 obj1 对象,obj2 对象中的属性会被忽略。

3、exports 只能导出属性,而不能直接导出变量或函数。如果需要直接导出一个变量或函数,只能使用 module.exports

例如,下面这个例子中,使用 module.exports 直接导出一个函数 foo

module.exports = function() {
  console.log('foo');
}

而使用 exports 导出一个属性 foo

exports.foo = function() {
  console.log('foo');
}

4、如果一个模块既使用了 module.exports 导出变量或函数,又使用了 exports 导出属性,那么只有 module.exports 所指向的对象中包含的属性才会被导出。

例如,下面这个例子中,使用 module.exports 导出一个函数 foo,并使用 exports 导出一个属性 bar

module.exports = {
  foo: function() {
    console.log('foo');
  }
}

exports.bar = function() {
  console.log('bar');
}

最终导出的只有 foo 函数,bar 属性会被忽略。

3、使用"module.exports "语法

3.1、导出导入单个对象

导出

//test.js
function hello(){
  console.log('I am jiazhuoqun');
}

module.exports = hello;

应用

//index.js
let hello = require("./test.js")
hello();  

3.2、导出导入多个对象

导出

//test.js
function hello(){
  console.log('I am jiazhuoqun');
}
var aa = function(){
  console.log("123456789");
}

module.exports.ccc = hello;
module.exports.ddd=aa;

应用

//index.js
let req = require("./test.js")
req.ccc();  
req.ddd();

注意:

若是module.exports后不加变量名,则只能导出一个对象

若是module.exports后加变量名,则是将等号右边的对象赋值给该变量名,应用时也用该变量名

3.3、导出导入变量或函数

// math.js
const add = (a, b) => a + b;
const subtract = (a, b) => a - b;
module.exports = {
  add,
  subtract
};

// main.js
const math = require('./math');
console.log(math.add(2, 3)); // 5
console.log(math.subtract(5, 2)); // 3

        在上面的示例中,我们定义了一个 math 模块,其中导出了 addsubtract 两个函数。在 main.js 中,我们使用 require 导入了 math 模块,并调用了其中的两个函数。

3.4、导出导入一个类

// person.js
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  greet() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
  }
}
module.exports = Person;

// main.js
const Person = require('./person');
const john = new Person('John', 30);
john.greet(); // Hello, my name is John and I am 30 years old.

        在上面的示例中,我们定义了一个 Person 类,并将其作为模块的默认导出。在 main.js 中,我们使用 require 导入了 Person 类,并创建了一个 john 实例,并调用了 greet 方法。

3.5、导出导入一个函数

// greet.js
const greet = name => console.log(`Hello, ${name}!`);
module.exports = greet;

// main.js
const greet = require('./greet');
greet('John'); // Hello, John!

        在上面的示例中,我们定义了一个 greet 函数,并将其作为模块的默认导出。在 main.js 中,我们使用 require 导入了 greet 函数,并调用了它。

4、使用“exports”语法

4.1、解构赋值语法

        当使用 require 导入模块时,你需要从返回的对象中获取导出的变量或函数。通常,你可以使用解构语法来获取需要的导出,例如:

const { someExport } = require('some-module');

        这将从 some-module 模块中获取导出为 someExport 的变量或函数,并将其赋值给 someExport 变量。

4.2、导出导入单个对象

导出:m1.js

exports.myObj = {
    foo: 'bar',
    baz: function () {
        console.log('baz');
    }
};

导入:m2.js

第一种方式:

const myObj = require('./m1');

console.log(myObj.myObj.foo);

第二种方式:

const {myObj} = require('./m1');

console.log(myObj.foo);

4.3、案例

下面是一个示例,展示了如何使用CommonJS模块语法:

// module.js

const add = (a, b) => a + b;
const multiply = (a, b) => a * b;

module.exports = {
  add,
  multiply
};

在上面的示例中,我们定义了两个函数 addmultiply,并使用 module.exports 导出这两个函数。这样,其他模块就可以使用 require 来加载该模块,并访问其中的成员。

// app.js

const { add, multiply } = require('./module.js');

console.log(add(2, 3)); // 5
console.log(multiply(2, 3)); // 6

在上面的示例中,我们使用 require 加载了 module.js 模块,并通过解构赋值的方式分别导入了 addmultiply 函数,然后在 app.js 中使用这些函数。

四、命名空间

        在 JavaScript 中,命名空间是一种将全局对象中的函数、变量和对象组织到一个名字空间中的技术。这样可以减少全局命名冲突的风险,提高代码的可读性和可维护性。

        JavaScript 中没有本地作用域,变量和函数都可以在全局范围内访问。这就意味着,如果在一个页面中使用了多个 JavaScript 文件,它们中的变量和函数可能会相互影响,导致命名冲突。通过使用命名空间,可以将这些变量和函数分组,避免它们之间的冲突。

下面是一个使用命名空间的示例:

// 定义命名空间
var myNamespace = {
  counter: 0,
  incrementCounter: function() {
    this.counter++;
  },
  resetCounter: function() {
    this.counter = 0;
  }
};

// 使用命名空间中的成员
console.log(myNamespace.counter); // 0
myNamespace.incrementCounter();
console.log(myNamespace.counter); // 1
myNamespace.resetCounter();
console.log(myNamespace.counter); // 0

        在上面的示例中,我们定义了一个名为 myNamespace 的命名空间,其中包含了一个计数器 counter 和两个方法 incrementCounterresetCounter。我们可以通过 myNamespace 命名空间来访问这些成员,并避免和全局作用域中的变量和函数产生冲突。

需要注意的是,JavaScript 中并没有原生的命名空间机制,因此需要手动创建命名空间。通常情况下,命名空间的实现方式包括对象字面量、模块模式、立即执行函数等。

五、模块导入、导出原理

        在 JavaScript/TypeScript 中,模块的导入和导出是通过模块系统来实现的,模块系统的主要功能包括:

  • 识别模块:将模块文件标识为可导入的模块。
  • 导入模块:将导入语句转换为引用模块的代码。
  • 导出模块:将导出语句转换为使模块成为导入模块的对象的代码。

        当一个模块被导入到另一个模块中时,该模块中的所有顶层语句都会被执行,包括变量和函数的声明和赋值语句、函数调用、console.log() 等语句。

        这是因为在 JavaScript/TypeScript 中,每个模块都是一个独立的文件作用域,所有在文件作用域中声明的变量、函数等都只在该作用域内可见,不能被其他模块访问。因此,在导入模块时,需要执行该模块的代码来初始化该模块中的变量、函数等,并将需要导出的内容放在导出对象中以供其他模块使用。

        总之,当一个模块被导入时,该模块中的所有顶层语句都会被执行,以初始化模块的变量、函数等,并将需要导出的内容放在导出对象中以供其他模块使用。

如果不想执行某些语句,可将这些语句放到一个函数中保护起来,函数不被调用,这些语句就不会执行

第二章、异步与同步

JS中的异步详解_大牛旭旭的博客-CSDN博客_js异步是什么意思

一、同步

        同步代码按照从上到下的顺序执行,每当代码执行到一个阻塞操作时,它会停止并等待操作完成,然后才会继续执行下一行代码。这意味着在执行同步代码时,程序会一直停留在当前行,直到该行代码执行完成后才会执行下一行代码。

案例一:

function a (){
  console.log("阻塞")
}

function b (){
  console.log("开始!");
  a();
  console.log("结束!");
}

b();

案例二:

// 同步示例
console.log("同步start");

for(let i=0; i<5; i++){
  console.log(i);
}

console.log("同步end");

1、同步回调

一般情况下自定义的函数(不论几个形参),若是传入回调函数(不论是直接传入回调函数函数表达式,还是提前定要好回调函数,传入函数名)必是同步回调,该自定义的函数为同步函数,也是同步任务

        在JavaScript中,同步回调是一种回调函数的类型,该函数在同步代码执行期间立即执行。它是在函数调用期间传递给另一个函数的函数,以便在后者完成后执行某些操作。

例如,考虑以下代码片段:

function foo(callback) {
  console.log("foo");
  callback();
}

function bar() {
  console.log("bar");
}

foo(bar);
console.log("end");

        在这个例子中,foo 函数接受一个回调函数 callback 作为参数,它在 foo 函数中被调用。在这个例子中,回调函数是 bar。当 foo 函数被调用时,它将打印 "foo",然后立即调用回调函数 bar。因为 bar 函数是同步回调,它会在 foo 函数执行期间被立即执行。最后,foo 函数结束并打印 "end"。

        总的来说,同步回调是一种简单而常见的技术,用于在JavaScript中传递代码以便在特定的时间点执行。

2、同步请求

        在JavaScript中,同步请求是指当代码发出请求时,程序会一直等待服务器返回响应后才能执行后续代码。在这种情况下,程序将被阻塞,直到响应被接收到或超时发生。

        同步请求通常使用XMLHttpRequest(XHR)对象或fetch API进行。这些请求被称为同步,因为它们在代码执行期间阻塞了线程,直到服务器返回响应或超时发生。

下面是使用XHR对象进行同步请求的示例代码:

const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com/api/data', false);
xhr.send();

if (xhr.status === 200) {
  console.log(xhr.responseText);
} else {
  console.error('Request failed.');
}

        在这个例子中,XHR对象被用于发送一个同步GET请求,它的第三个参数被设置为false,表示这是一个同步请求。如果请求成功,就会在控制台输出响应文本;否则,就会输出错误信息。

        尽管同步请求在某些情况下可能很有用,但它们通常被认为是一种不良的实践,因为它们会阻塞浏览器的UI线程,使得用户不能与页面交互,造成页面假死等问题。因此,建议使用异步请求来避免这些问题。

3、同步加载

        在JavaScript中,同步加载是指在代码执行期间加载脚本或资源时,程序会一直等待资源加载完成后才能执行后续代码。在这种情况下,程序将被阻塞,直到资源加载完成或发生超时。

在Web开发中,同步加载通常发生在使用<script>标签加载JavaScript脚本时。例如:

<body>
  <script src="app.js"></script>
  <p>Hello World!</p>
</body>

        在这个例子中,当浏览器解析到<script>标签时,它会停止页面的渲染,直到app.js文件加载和执行完毕。因此,如果app.js文件很大或加载速度很慢,用户可能会看到一个空白页面,直到app.js文件加载完成。

        尽管同步加载可以确保代码在正确的上下文中执行,但它们通常被认为是一种不良的实践,因为它们会阻塞浏览器的UI线程,使得用户不能与页面交互,造成页面假死等问题。因此,建议使用异步加载来避免这些问题。

二、异步(Promise机制)

        JS内置异步编程机制,有异步编程模型,要想使用异步,必须借助异步编程模型,不能自己定义异步。

        异步代码则是不按照从上到下的顺序执行的。当异步操作开始时,代码会继续往下执行,而不会等待操作完成。一旦异步操作完成,它会将结果返回给程序,并且程序将在适当的时间继续执行。

        异步模式:每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。

        通俗的理解异步任务,即就是当轮到某个任务执行时,这个任务可能比较复杂,或者比较耗时间,所以就先不执行这个任务,直接执行下一个任务,等结束后执行这个任务,这个任务结束后会立即执行自己的回调函数。

// 异步示例
console.log("异步start");

setTimeout(function(){
  console.log("async task complete");
}, 2000);

console.log("异步end");

1、异步回调

一般情况下内置的函数(至少有两个形参),若是传入回调函数(不论是直接传入回调函数函数表达式,还是提前定要好回调函数,传入函数名)必是异步回调,该内置的函数也是异步函数,为异步任务。

        在JavaScript中,异步回调是一种回调函数的类型,该函数将在异步操作完成后执行。它是在函数调用期间传递给另一个函数的函数,以便在后者完成后执行某些操作。

        异步回调通常用于处理需要一定时间才能完成的操作,例如从服务器获取数据,读取本地文件等等。在这些情况下,程序不能等待操作完成,而是需要立即返回并继续执行后续代码。当操作完成后,回调函数将被执行以便处理操作结果。

以下是使用异步回调处理从服务器获取数据的示例代码:

function getData(callback) {
  fetch('https://example.com/api/data')
    .then(response => response.json())
    .then(data => callback(data))
    .catch(error => console.error(error));
}

function processData(data) {
  console.log(data);
}

getData(processData);
console.log('end');

        在这个例子中,getData函数用于从服务器获取数据,并将结果传递给回调函数callback。在这里,callback函数是processData。当数据从服务器返回后,它被解析为JSON格式,并被传递给回调函数callback以处理数据。在这里,回调函数打印数据。最后,控制台会输出 "end"。

        总的来说,异步回调是一种常用的技术,用于处理需要一定时间才能完成的操作,并确保代码不会被阻塞。

2、异步请求

        在JavaScript中,异步请求是一种与服务器交互的方式,它允许代码在等待响应时继续执行,而不必等待响应返回。异步请求通常用于执行需要一定时间才能完成的操作,例如从服务器获取数据、上传文件、更新数据等等。

        异步请求可以使用XMLHttpRequest对象、fetch函数或第三方库等方式实现。这些方法都允许您发送请求并指定回调函数,以在请求完成后执行某些操作。

以下是使用XMLHttpRequest对象执行异步请求的示例代码:

const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com/api/data', true);
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4 && xhr.status === 200) {
    const data = JSON.parse(xhr.responseText);
    console.log(data);
  }
};
xhr.send();

        在这个例子中,我们创建了一个XMLHttpRequest对象,并使用open方法指定请求的URL和请求类型。第三个参数true表示该请求是异步的。我们还定义了一个onreadystatechange回调函数,它将在请求状态发生改变时执行。在这里,我们检查请求状态和响应状态,并将响应文本解析为JSON对象。最后,我们将数据打印到控制台上。

        总的来说,异步请求是一种非常有用的技术,它使代码能够与服务器交互,并在请求返回之前继续执行其他操作,从而提高了应用程序的响应性和性能。

3、异步加载

        在JavaScript中,异步加载是指在页面加载过程中,通过动态加载资源的方式,实现某些资源的并行加载,从而提高页面的性能和用户体验。异步加载可以是脚本、样式、图像、音频、视频等资源的加载。

        常见的异步加载技术包括使用JavaScript动态创建<script><link>元素,以及使用XMLHttpRequest对象和Fetch API等方法动态加载资源。

以下是一个使用<script>元素动态加载JavaScript文件的示例代码:

const script = document.createElement('script');
script.src = 'https://example.com/my-script.js';
script.async = true;
document.head.appendChild(script);

        在这个例子中,我们使用document.createElement方法创建了一个<script>元素,并指定了src属性为JavaScript文件的URL。我们还设置了async属性为true,表示该文件是异步加载的。最后,我们将该元素添加到文档头部。

        异步加载的优点是,它可以加快页面的加载速度,因为浏览器可以并行加载多个资源。此外,它还可以提高页面的响应性,因为浏览器可以在后台加载资源,而不必等待所有资源都加载完毕才显示页面。这对于需要加载大量资源的页面或应用程序特别有用。

三、异步操作和同步操作

同步操作:本身是同步的,要转换为异步

异步操作:本身是异步的,要转换为同步

1、不管是同步模式还是异步模式,代码都是从上而下依次执行,代码本身执行顺序不是异步

2、同步模式会阻塞线程,异步模式不会阻塞线程

3、异步操作通常是通过    回调函数、Promise 或者 async/await   来实现的

        JavaScript 中的同步操作是指在执行代码时,程序会一步一步按照代码顺序执行,直到当前操作完成后才会进行下一步操作。这意味着,在执行一个同步操作期间,程序会一直阻塞等待操作完成,直到操作完成后才会进行下一步操作。
        而异步操作则是指在执行代码时,程序不会一直等待操作完成,而是继续执行后续代码,同时在操作完成后会执行一个回调函数或者Promise.resolve()。
        在 JavaScript 中,异步操作通常是通过回调函数、Promise 或者 async/await 来实现的。异步操作可以提高程序的响应性能和处理大量请求的能力。例如,当需要从远程服务器获取数据时,使用异步操作可以避免程序因为等待数据返回而被阻塞,提高程序的效率。
总结一下:

  • 同步操作:
    • 程序一步一步按照代码顺序执行;
    • 执行期间会一直阻塞等待操作完成;
    • 操作完成后才会进行下一步操作。
  • 异步操作:
    • 程序执行时,不会一直等待操作完成;
      • 在操作完成后执行回调函数或者 Promise.resolve();
    • 可以提高程序的响应性能和处理大量请求的能力。

四、异步函数

1、定义

        异步函数(Asynchronous Function)指的是一种不会立即返回结果的函数,它会先返回一个 Promise 对象,然后在后台继续执行任务,当任务完成后再将结果返回给 Promise 对象。

        在 JavaScript 中,异步函数通常用于处理一些需要等待的任务,比如读取文件、发起网络请求、处理动画效果等等。这些任务需要等待一定时间才能完成,如果在任务执行期间阻塞了 JavaScript 主线程,就会导致整个页面失去响应。为了避免这种情况,JavaScript 提供了异步函数的机制,可以让任务在后台执行,等到完成后再返回结果。

2、执行原理

        异步函数的执行原理是基于事件循环(Event Loop)机制的。在 JavaScript 中,所有的异步任务都会被放入任务队列(Task Queue)中,然后等待主线程空闲时被执行。当主线程空闲时,它会从任务队列中取出一个任务,执行它的回调函数,然后将结果返回给 Promise 对象。

事件循环的执行顺序如下:

  1. 执行当前的同步任务,直到遇到异步任务或者任务队列为空为止。
  2. 将异步任务放入对应的任务队列中,等待主线程空闲时执行。
  3. 当主线程空闲时,从任务队列中取出一个任务执行它的回调函数。
  4. 将任务的结果返回给 Promise 对象,并根据结果更新 Promise 对象的状态。
  5. 如果任务队列中还有任务,重复步骤 3-4,直到任务队列为空为止。

        在这个过程中,主线程会不断地从任务队列中取出任务执行,所以异步任务并不会阻塞 JavaScript 的执行。同时,Promise 对象也提供了一种方便的机制来处理异步任务的结果,可以通过 then()、catch()、finally() 等方法来处理异步任务的状态变化。

3、同步任务,异步任务

        在 JavaScript 中,任务可以分为同步任务和异步任务。

        同步任务(Synchronous Task)是指按照顺序一步一步地执行的任务,如果当前任务没有执行完成,后面的任务就必须等待它执行完成才能继续执行。同步任务通常是指 JavaScript 主线程上的任务,它们会阻塞 JavaScript 的执行,如果执行时间过长,就会导致页面失去响应。

一般情况下自定义的函数(不论几个形参),若是传入回调函数(不论是直接传入回调函数函数表达式,还是提前定要好回调函数,传入函数名)必是同步回调,该自定义的函数为同步函数,也是同步任务

        异步任务(Asynchronous Task)是指不会阻塞 JavaScript 主线程执行的任务,它们通常会在后台执行,并在执行完成后将结果返回给 JavaScript 主线程。异步任务通常包括读取文件、发起网络请求、处理动画效果等,它们的执行时间不确定,无法按照顺序执行,因此需要使用回调函数、Promise、async/await 等机制来处理它们的结果。

一般情况下内置的函数(至少有两个形参),若是传入回调函数(不论是直接传入回调函数函数表达式,还是提前定要好回调函数,传入函数名)必是异步回调,该内置的函数也是异步函数,为异步任务。

        JavaScript 中的异步任务通常都是通过事件循环(Event Loop)机制来实现的,即将异步任务放入任务队列中,等待主线程空闲时执行。在执行异步任务时,JavaScript 主线程会继续执行后续的同步任务,不会阻塞程序的执行。这种机制可以有效地提高 JavaScript 的执行效率,保证页面的流畅性。

4、setTimeout异步函数

  setTimeout 是 JavaScript 中的一个内置函数,用于在指定的时间后执行一次回调函数。它的语法如下:

setTimeout(callback, delay, arg1, arg2, ...);

其中

  • callback 表示要执行的回调函数,
  • delay 表示延迟的时间(以毫秒为单位),
  • arg1arg2 等表示传递给回调函数的参数(可选)。

  setTimeout 的执行过程是异步的,即它会将回调函数放入任务队列中,等待主线程空闲时执行。因此,调用 setTimeout 后,主线程会继续执行后续的同步任务,不会阻塞程序的执行。

举个例子,下面的代码使用 setTimeout 实现了一个简单的倒计时功能:

function countdown(seconds) {
  console.log(seconds);
  if (seconds > 0) {
    setTimeout(() => {
      countdown(seconds - 1);
    }, 1000);
  } else {
    console.log('Done!');
  }
}

countdown(5);

        这个例子中,countdown 函数接受一个数字参数 seconds,用于指定倒计时的时间。在函数内部,先输出当前的倒计时时间,然后使用 setTimeout 实现了递归调用,每隔 1 秒执行一次回调函数。当倒计时时间为 0 时,输出 'Done!' 表示倒计时结束。

        需要注意的是,虽然 setTimeout 的延迟时间是以毫秒为单位的,但是实际上回调函数并不会在精确的时间点执行,而是在指定时间后尽快执行,具体的时间由 JavaScript 引擎的调度决定。

第三章、回调(callback)与回调函数(callback_function)

一、回调

        回调是一种常见的编程模式,回调(Callback)通常指的是将一个函数作为参数传递给另一个函数,并在该函数执行完后执行该函数的过程。回调可以是任何可执行的代码,包括匿名函数和箭头函数等。用于处理异步操作和事件处理程序等情况。它允许您将代码组织成更可读、可维护的结构,并提高代码的可重用性。

二、回调函数

1、回调函数是一个函数,只是在特殊情况下作为另一个函数的参数而已

2、回调函数(Callback function)是指被传递给另一个函数作为参数的函数。回调函数通常用于异步编程中,当异步操作完成时,回调函数将被调用以处理结果。

3、回调函数的执行过程:直接将函数定义表达式或(提前定义好,将函数名)作为参数传递给另一个函数,并在其父函数完成后执行。

4、可理解为(JS内部异步编程机制):执行父函数获取数据等操作,将结果返回给子函数(callback)进行处理,只是一个形参而已。

5、回调函数就是子函数,被传入的函数就是父函数

6、传入回调函数就是别人提前已经定义好的,我们直接传参调用即可

1、回调函数解析

test.js
//回调函数:
//1、回调函数都是子函数,在父函数执行完成之后执行
//2、回调函数就是把函数当作参数传入另一个函数,该函数就是子函数,被传入的函数就是父函数

//案例一:
//定义函数
// function print(string,callback){
//     let wait = Math.random()*10;
//     setTimeout(()=>{
//         console.log(string);
//         callback();
//     },wait);
// }

//调用函数
// print('a',()=>{
//     print('b',()=>{
//         print('c',()=>{})
//     })
// })

//案例二:
//定义函数
function print(string,callback){
    console.log(string);
    console.log('after log');
    callback();
};
//调用函数1:
// print('a',function(){
//     print('b',function(){
//         print('c',function(){
//             console.log('finished!');
//         });
//     });
// });

//调用函数2:
print('a',()=>{
    print('b',()=>{
        print('c',()=>{
            console.log('finished!');
        });
    });
});

第四章、Promise对象

一、promise机制

        JavaScript中的Promise机制是一种异步编程解决方案,用于管理异步操作。它使得异步操作能够更加容易地处理和组织,从而减少了回调地狱(Callback Hell)的问题。

二、promise对象

        Promise对象是Promise机制的核心,它是一种用于管理异步操作的解决方案。在JavaScript中,Promise机制可以通过创建和使用Promise对象来实现。Promise对象封装了异步操作,并提供了一种可靠的方式来处理异步操作的结果(成功或失败)。通过使用Promise对象,我们可以避免回调地狱(Callback Hell)的问题,使异步操作的处理和组织更加容易和清晰。因此,可以说Promise对象是Promise机制的核心和基础。

1、promise原理

为了异步操作得到数据,首先布置一件任务,然后返回一个promise对象,该promise对象承诺一定给我一个结果,要是是任务成功将结果返回给我,要么就是任务执行失败,返回一个异常信息。

2、组成部分

2.1、状态(state)

        Promise 对象的状态指的是它的执行状态,有三种可能性:pending(等待态)、fulfilled(成功态)和rejected(失败态)。

2.2、成功值(value)

        当 Promise 对象的状态变为 fulfilled(成功态)时,它会返回一个成功的值。这个成功的值可以是任何 JavaScript 的合法数据类型,如字符串、数字、对象等等

2.3、错误值(reason)

        当 Promise 对象的状态变为 rejected(失败态)时,它会返回一个错误的值。这个错误的值通常是一个 Error 对象,其中包含了发生错误的具体信息。

        总之,Promise 对象的状态、成功值和错误值是构成它的基本要素。当我们使用 Promise 对象时,可以通过各种方法和函数来获取和处理这些值,以实现异步编程的各种需求。

三、promise对象的作用

主要作用可理解为:将任务布置的代码和任务结果的处理代码进行了分离

        Promise对象的作用是用于管理异步操作,它提供了一种可靠的方式来处理异步操作的结果(成功或失败)。在JavaScript中,由于异步操作的执行顺序是不确定的,因此可能会出现回调地狱(Callback Hell)的问题,这会导致代码难以阅读和维护。使用Promise对象可以避免回调地狱的问题,从而使异步操作的处理和组织更加容易和清晰。

具体来说,Promise对象的作用如下:

1、封装异步操作

        Promise对象可以封装异步操作,使其具有更加可控和可预测的特性。通过创建Promise对象,我们可以在异步操作完成时获取其结果,而不需要通过回调函数来处理异步操作的结果。

2、处理异步操作结果

        Promise对象提供了一种可靠的方式来处理异步操作的结果。通过调用Promise对象的then()方法,可以指定异步操作成功时的回调函数,通过catch()方法可以指定异步操作失败时的回调函数。

3、简化代码

        使用Promise对象可以避免回调地狱的问题,从而使异步操作的处理和组织更加容易和清晰。在处理异步操作时,Promise对象提供了一种链式调用的方式,使得代码更加简洁和易于理解。

4、处理多个异步操作

        通过使用Promise.all()方法可以同时处理多个异步操作,等到所有异步操作都完成后再执行后续的代码。这对于需要同时处理多个异步操作的情况非常有用。

总之,Promise对象的作用是使异步操作更加可控和可预测,从而使代码更加清晰和易于维护。

四、创建promise对象

创建promise对象时,需要传入resolve和reject两个函数当作参数,创建成功时用resolve函数,创建失败用reject函数 

1、改变promise对象状态方法

1、resolve方法

  • resolve 表示 Promise 的状态变为“已完成”,当 Promise 状态变为“已完成”时,会执行 then 方法中注册的回调函数,
  • 在调用 resolve 函数时,可以将一个值作为参数传递给它,这个值会被传递给 then 方法中的回调函数作为参数;

2、reject方法

  • reject 表示 Promise 的状态变为“已失败”。当 Promise 状态变为“已失败”时,会执行 catch 方法中注册的回调函数。
  • 在调用 reject 函数时,可以将一个错误对象作为参数传递给它,这个错误对象会被传递给 catch 方法中的回调函数作为参数。

3、异同点

  •  resolve和reject方法函数默认最多只能传一个参数(JS中任意类型数据)或者不传参数,需要传入多个参数 详见此处
  • resolvereject 函数都没有返回值,它们仅仅是 Promise 的两种状态之一
  • resolvereject 函数的返回值不会影响 Promise 的状态变化,它们仅仅是用来传递 Promise 的执行结果(或错误信息)给 thencatch 方法中的回调函数。

2、使用构造函数

declare var Promise: PromiseConstructor;

1、构造函数介绍

        Promise构造函数是JavaScript的标准库,而不是Node.js的内置函数。在Node.js环境中,可以直接使用Promise构造函数,也可以通过引入其他库(如bluebird)来使用Promise。

        Promise构造函数的主要作用是用于异步编程,它将异步操作封装成Promise对象,使得异步操作的结果可以通过Promise对象的状态(fulfilled或rejected)来获取,并且可以通过链式调用的方式来处理异步操作的结果。

2、构造函数参数说明

        在创建Promise对象时,需要传入一个回调函数(也称为执行器函数),该函数在Promise对象的状态发生变化时被调用。回调函数有两个参数,分别是resolve和reject。

2.1、resolve回调函数(无返回值)

        当异步操作成功时,调用resolve函数将Promise对象的状态设置为fulfilled,同时将异步操作的结果作为resolve函数的参数传递给Promise对象;

2.2、reject回调函数(无返回值)

        当异步操作失败时,调用reject函数将Promise对象的状态设置为rejected,同时将错误信息作为reject函数的参数传递给Promise对象。

3、构造函数执行原理

        Promise构造函数的运行原理是,当创建Promise对象时,该对象的状态是pending(即未完成),执行器函数开始执行,如果异步操作成功,调用resolve函数将Promise对象的状态改为fulfilled,如果异步操作失败,调用reject函数将Promise对象的状态改为rejected。当状态改变后,Promise对象的then或catch方法将被触发,进而执行相应的回调函数。

4、案例一

const promise = new Promise((resolve, reject) => {
  // 异步操作
  if (/* 异步操作成功 */) {
    resolve('操作成功');
  } else {
    reject('操作失败');
  }
});

promise.then(result => {
  console.log(result); // 打印'操作成功'
}).catch(error => {
  console.log(error); // 打印'操作失败'
});

        在这个例子中,我们创建了一个Promise对象,通过传入一个回调函数来处理异步操作的结果。如果异步操作成功,我们调用resolve函数将Promise对象的状态改为fulfilled,并将结果作为resolve函数的参数传递给Promise对象;如果异步操作失败,我们调用reject函数将Promise对象的状态改为rejected,并将错误信息作为reject函数的参数传递给Promise对象。最后,我们通过Promise对象的then方法来处理成功的结果,通过catch方法来处理失败的结果。

5、案例二

//实例化 Promise 对象
const p = new Promise(function (resolve, reject) {
  setTimeout(function () {

    // 失败调用reject()处理 
    let err = "数据读取失败";
    reject(err);  //直接将reject函数的参数传递给then方法中的failCallback方法(第二个方法)

    // 成功调用resolve()处理
    let data = "数据读取成功";
    resolve(data); //直接将resolve函数的参数传递给then方法中的doneCallback方法(第一个方法)



  }, 1000);
});

//调用 promise 对象的 then 方法
p.then(function (value) {
  console.log(value);
  console.log("doneCallback");
}, function (reason) {
  console.error(reason);
  console.log("failCallback");
});

3、使用Promise.resolve()方法

1、运行原理

        Promise.resolve()方法是用来创建一个已经成功(fulfilled)的Promise对象,其运行原理是返回一个Promise对象,该对象的状态为fulfilled,并且结果值为传入的参数。如果传入的参数本身就是一个Promise对象,则直接返回该对象。

例如,我们可以使用Promise.resolve()方法来创建一个已经成功的Promise对象:

const myPromise = Promise.resolve('已经完成');

        在这个例子中,我们调用了Promise.resolve()方法,传入一个字符串作为参数。因为传入的参数是一个字符串,而不是一个Promise对象,所以Promise.resolve()方法将创建一个新的Promise对象,该对象的状态为fulfilled,结果值为传入的字符串'已经完成'。可以将这个Promise对象看作是下面这个代码的简写:

const myPromise = new Promise((resolve) => {
  resolve('已经完成');
});

2、传入一个Promise对象,Promise.resolve()方法将直接返回该对象

const myPromise1 = Promise.resolve(somePromise);
const myPromise2 = Promise.resolve(Promise.resolve('已经完成'));

        在这个例子中,我们分别传入了一个Promise对象和一个返回Promise对象的Promise.resolve()方法作为参数。由于传入的参数都是Promise对象,因此Promise.resolve()方法将直接返回这些对象,而不会创建新的Promise对象。

3、案例

const myPromise = Promise.resolve('已经完成');

myPromise.then(text => console.log("doneCallback: "+text)).catch(err => console.log("catch: "+err));

4、使用Promise.reject()方法

1、运行原理

        Promise.reject()方法是用来创建一个已经失败(rejected)的Promise对象,其运行原理是返回一个Promise对象,该对象的状态为rejected,并且结果值为传入的参数(通常是一个错误对象)。

例如,我们可以使用Promise.reject()方法来创建一个已经失败的Promise对象:

const myPromise = Promise.reject(new Error('发生错误'));

        在这个例子中,我们调用了Promise.reject()方法,传入一个Error对象作为参数。因为传入的参数是一个Error对象,Promise.reject()方法将创建一个新的Promise对象,该对象的状态为rejected,结果值为传入的Error对象。可以将这个Promise对象看作是下面这个代码的简写:

const myPromise = new Promise((resolve, reject) => {
  reject(new Error('发生错误'));
});

2、传入一个Promise对象,Promise.reject()方法也将直接返回该对象

const myPromise1 = Promise.reject(somePromise);
const myPromise2 = Promise.reject(Promise.resolve('已经完成'));

        在这个例子中,我们分别传入了一个Promise对象和一个返回Promise对象的Promise.resolve()方法作为参数。由于传入的参数都是Promise对象,因此Promise.reject()方法将直接返回这些对象,而不会创建新的Promise对象。

3、案例

const myPromise = Promise.reject(new Error("发生错误"));

myPromise.then(text => console.log("doneCallback: "+text)).catch(err => console.log("catch: "+err));

5、使用Promise.all() 方法

1、运行原理

        Promise.all() 方法用于将多个 Promise 对象包装成一个 Promise 对象,当所有 Promise 对象都变为 resolved 状态时,Promise.all() 方法返回的 Promise 对象也会变为 resolved 状态,并且它的值为所有 Promise 对象的结果组成的数组;如果其中任意一个 Promise 对象变为 rejected 状态,则 Promise.all() 方法返回的 Promise 对象会变为 rejected 状态,并且它的值为第一个变为 rejected 状态的 Promise 对象的错误信息。

2、语法

Promise.all(iterable);

3、案例一

const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('Promise 1 resolved');
    }, 1000);
  });
  
  const promise2 = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('Promise 2 resolved');
    }, 500);
  });
  
  Promise.all([promise1, promise2])
  .then((values) => {
    console.log(values); // ['Promise 1 resolved', 'Promise 2 resolved']
    console.log("doneCallback" )
  })
  .catch((values) => {
    console.log(values);
    console.log("failCallback")
  });

        在上面的示例中,创建了两个 Promise 对象 promise1 和 promise2,并将它们作为参数传递给 Promise.all() 方法。当两个 Promise 对象都变为 resolved 状态时,Promise.all() 方法返回一个新的 Promise 对象,它的状态也会变为 resolved,values 数组中存储了两个 Promise 对象的结果。

4、案例二

const api1 = 'https://api.example.com/data1';
const api2 = 'https://api.example.com/data2';
const api3 = 'https://api.example.com/data3';

const promise1 = fetch(api1);
const promise2 = fetch(api2);
const promise3 = fetch(api3);

Promise.all([promise1, promise2, promise3]).then((responses) => {
  const [response1, response2, response3] = responses;
  console.log(response1.json());
  console.log(response2.json());
  console.log(response3.json());
}).catch((error) => {
  console.log(error);
});

        在上面的示例中,使用 fetch() 方法请求了三个不同的 API,将它们的 Promise 对象分别存储在 promise1、promise2 和 promise3 变量中,然后将它们作为参数传递给 Promise.all() 方法。当三个 Promise 对象都变为 resolved 状态时,Promise.all() 方法返回的 Promise 对象也会变为 resolved 状态,它的值为所有 Promise 对象的结果组成的数组 responses。通过解构赋值可以将 responses 数组中的每个元素都赋值给一个变量,然后分别调用 response.json() 方法来解析每个 API 返回的 JSON 数据。如果其中任意一个 API 请求返回错误,则 Promise.all() 方法返回的 Promise 对象会变为 rejected 状态,并且抛出错误信息。

6、使用Promise.allSettled()方法

1、运行原理

        Promise.allSettled() 方法返回一个 Promise 对象,该对象在所有 Promise 对象都已经 settled (即已经fulfilled或rejected)之后才会返回,返回的结果为一个数组,数组中包含了所有 Promise 对象的结果或错误信息,每个结果都是一个对象,对象的格式为:

{
    status: "fulfilled"/"rejected",
    value: //如果 Promise 对象状态为 fulfilled,则该属性包含 Promise 对象的返回值,否则为 undefined
    reason: //如果 Promise 对象状态为 rejected,则该属性包含 Promise 对象的错误信息,否则为 undefined
}

2、案例一

使用 Promise.allSettled() 处理多个 Promise 对象,输出所有 Promise 对象的状态和结果

Promise.resolve(1);
const p2 = Promise.reject("error");
const p3 = new Promise((resolve, reject) => {
    setTimeout(resolve, 1000, 'delayed success');
});

Promise.allSettled([p1, p2, p3])
  .then(results => results.forEach(result => console.log(result.status, result.value, result.reason)));
  
// 输出:
// fulfilled 1 undefined
// rejected undefined error
// fulfilled delayed success undefined

        在上面的示例中,我们定义了三个 Promise 对象:p1 和 p3 是成功状态的 Promise 对象,而 p2 是失败状态的 Promise 对象。然后我们将这三个 Promise 对象作为参数传入了 Promise.allSettled() 方法中,并使用 then() 方法处理返回的结果。由于三个 Promise 对象的状态不同,因此返回的结果中包含了三个对象,分别对应这三个 Promise 对象的状态和结果。

const p3 = new Promise((resolve, reject) => {
    setTimeout(resolve, 1000, 'delayed success');
});

语法解释:

这是一种使用 setTimeout 函数的语法,它通过 Promise 对象来包装 setTimeout 函数,使其返回一个 Promise 对象。这种用法的语法为:

const promise = new Promise((resolve, reject) => {
    setTimeout(resolve, delay, ...args);
});

        其中,resolve 表示 Promise 对象被 fulfilled 的回调函数,delay 表示延迟的时间,args 表示需要传递给回调函数的参数。

        在上述代码中,p3 是一个 Promise 对象,它的状态会在 1 秒后变为 fulfilled。setTimeout 函数的第一个参数是 resolve,表示在 1 秒后调用 resolve 函数,将 Promise 对象的状态设置为 fulfilled,第二个参数是 1000,表示延迟的时间是 1 秒,第三个参数是 'delayed success',表示在 1 秒后调用 resolve 函数时,将其参数设置为 'delayed success'。最终,p3 的状态会变为 fulfilled,并返回一个值为 'delayed success' 的结果。

        需要注意的是,这种语法只适用于具有回调函数的异步函数,比如 setTimeout、setInterval、XMLHttpRequest 等,它可以将这些异步函数封装成 Promise 对象,从而更方便地处理异步任务。

3、案例二

使用 Promise.allSettled() 处理空数组,返回一个空数组

Promise.allSettled([])
  .then(results => console.log(results));
  
// 输出:[]

        在上面的示例中,我们将一个空数组作为参数传入了 Promise.allSettled() 方法中,并使用 then() 方法处理返回的结果。由于没有 Promise 对象需要处理,因此返回的结果是一个空数组。

4、案例三

使用 Promise.allSettled() 处理只有一个 Promise 对象的数组

Promise.allSettled([Promise.resolve('success')])
  .then(results => console.log(results));
  
// 输出:[{status: "fulfilled", value: "success"}]

        在上面的示例中,我们将只包含一个 Promise 对象的数组作为参数传入了 Promise.allSettled() 方法中,并使用 then() 方法处理返回的结果。由于只有一个 Promise 对象需要处理,因此返回的结果是一个只包含一个对象的数组。

5、Promise.allSettled()方法和Promise.all()方法区别

  Promise.all() 方法接受一个 Promise 实例数组作为参数,返回一个新的 Promise 对象,只有当所有的 Promise 实例都变成 fulfilled 状态时,这个新的 Promise 对象才会变成 fulfilled 状态,并将所有 Promise 实例的返回值组成一个数组返回。如果其中任何一个 Promise 实例变成 rejected 状态,这个新的 Promise 对象就会变成 rejected 状态,并返回第一个变成 rejected 状态的 Promise 实例的错误信息。

  Promise.allSettled() 方法也接受一个 Promise 实例数组作为参数,返回一个新的 Promise 对象,当所有的 Promise 实例都变成 settled 状态(即 fulfilled 或 rejected 状态)时,这个新的 Promise 对象才会变成 fulfilled 状态,并将所有 Promise 实例的返回值组成一个数组返回,返回值中每个元素是一个对象,包含每个 Promise 实例的状态和结果或错误信息。

  Promise.all()Promise.allSettled() 的主要区别在于对 rejected 状态的处理。Promise.all() 会在任何一个 Promise 实例变成 rejected 状态时立即返回,并且不会等待其他 Promise 实例的执行结果;而 Promise.allSettled() 会等待所有 Promise 实例都执行完毕,不管是 fulfilled 还是 rejected 状态,并将每个 Promise 实例的状态和结果或错误信息都返回。

        举个例子,假设有两个 Promise 实例 p1 和 p2,p1 立即变成 fulfilled 状态并返回结果,p2 在 1 秒后变成 rejected 状态,那么使用 Promise.all()Promise.allSettled() 的结果分别如下:

// 使用 Promise.all()
Promise.all([p1, p2]).then(([result1, result2]) => {
  console.log(result1); // fulfilled 结果
  console.log(result2); // rejected 错误信息
}).catch((error) => {
  console.log(error); // rejected 错误信息
});

// 使用 Promise.allSettled()
Promise.allSettled([p1, p2]).then(([result1, result2]) => {
  console.log(result1.status); // fulfilled
  console.log(result1.value); // fulfilled 结果
  console.log(result2.status); // rejected
  console.log(result2.reason); // rejected 错误信息
}).catch((error) => {
  console.log(error); // 不会执行到这里
});

        从上面的例子可以看出,使用 Promise.all() 时,一旦有 Promise 实例变成 rejected 状态,就会立即返回并抛出错误信息;而使用 Promise.allSettled() 时,会等待所有 Promise 实例都执行完毕,并将每个 Promise 实例的状态和结果或错误信息都返回,不会抛出异常。

7、使用q模块

q模块的集中常用方法处理异步和同步

javascript - 使用 q.js,q.all 是否执行即时函数? - IT工具网

nodejs q模块_qeesung的博客-CSDN博客_node q

都是为了处理nodejs中的异步和回调机制 

1、q模块介绍

        q 是一个流行的 Promise 库,可以方便地创建和管理 Promise 对象,q 模块不是 Node.js 的内置模块,它是一个第三方模块,需要使用 npm 安装才能在 Node.js 中使用。可以使用以下命令安装 q 模块:

npm install q --save-dev

2、Q.defer()方法

jQuery deferred.resolve() 方法

JavaScript中deferred对象浅析_deferred js_eden_in_my_heart的博客-CSDN博客

1、Q.defer():创建一个 Deferred 对象,用于控制 Promise 对象的状态和值。

2、可以理解为:Q.defer()对返回的promise预先进行一次处理,nodejs进行一次操作都默认会返回一个promise

语法:

const Q = require('q');
const deferred = Q.defer();

        其中,Q 是 q 模块提供的对象,用于创建 Promise 对象;deferred 是一个 Deferred 对象,它是 q 模块提供的一个工具类,用于控制 Promise 对象的状态和值。

案例一:

const Q = require('q');

function getUser(id) {
  const deferred = Q.defer();
  setTimeout(() => {
    const user = {id: id, name: 'John Doe'};
    deferred.resolve(user);
  }, 1000);
  return deferred.promise;
}

getUser(123).then((user) => {
  console.log(user); // {id: 123, name: 'John Doe'}
}).catch((error) => {
  console.log(error);
});

        在上面的示例中,定义了一个名为 getUser 的函数,它接收一个 id 参数,返回一个 Promise 对象。在函数体内部,使用 Q.defer() 方法创建一个 deferred 对象,并将它的 promise 属性返回作为 getUser 函数的返回值。然后使用 setTimeout() 方法模拟异步操作,1 秒后将包含用户信息的对象 user 传递给 deferred.resolve() 方法,从而将 Promise 对象的状态从 pending 变为 resolved,并且它的值为 user 对象。

        使用 q 模块创建 Promise 对象时,通常会将 Promise 对象的创建和状态控制逻辑封装在一个函数内部,然后将 Promise 对象返回给调用方。调用方可以通过 then() 和 catch() 方法来获取 Promise 对象的值和错误信息。

案例二:

我们首先创建一个promise,然后在合适的时候将promise设置成为fulfilled或者rejected状态的

任何返回的promise只有两种状态,成功状态(false)和失败状态(true),且只返回到回调函数的第一个参数上

var Q = require('q');
var FS = require('fs');

function myReadFile(){
    var deferred = Q.defer();
    FS.readFile("foo.txt", "utf-8", function (aaa, text) {  //回调函数,将父函数执行结果返回此处
        if (aaa) {   //因为此处进行判断error,所以aaa属性为error
            deferred.reject(new Error(aaa));  //以异常error来创建一个promise
        } else {
            deferred.resolve(text);  //以text来创建了promise
        }
    });
    return deferred.promise; // 这里返回一个承诺
}

myReadFile().then(function(data){   //调用上述函数返回的promise传为回调函数进行处理
    console.log("get the data : "+data);
},function(err){
    console.log(err);
}); //then里面有两个函数,进行判断
  • var deferred = Q.defer();  实例化一个延迟对象,预先处理promise
  • deferfed.resolve(): 解决Deferred(延迟)对象,并根据给定的args参数调用任何完成回调函数(doneCallbacks),
  • 当延迟对象被 resolved 时,任何通过 deferred.then 或 deferred.done 添加的 doneCallbacks,都会被调用。回调函数按它们被添加时的顺序执行。传递给 deferred.resolve() 的 args 参数, 会传给每个回调函数。当延迟对象进入 resolved 状态后,任何 doneCallbacks 被添加时,就会被立刻执行,并带上传入给 .resolve() 的参数。
  • deferfed.reject(): 拒绝Deferred(延迟)对象,并根据给定的args参数调用任何失败回调函数(failCallbacks)。
var Q = require('q');
var FS = require('fs');
 
function myReadFile(){
    var deferred = Q.defer();
    FS.readFile("foo.txt", "utf-8", function (promise_boolean, promise_content) {  //以参数a为准,成功为false,失败为true
        if (promise_boolean) {   
            deferred.reject(true);  
        } else {
            deferred.resolve(promise_content);  
        }
    });
    return deferred.promise; // 这里返回一个承诺
}

myReadFile().then(function(donecallback){   
    console.log("get the data : "+donecallback);
},function(failcallback){
    console.log("get the error :"+failcallback);
}); 

3、Q.promise()方法

创建一个 Promise 对象,并返回它。

案例一:

const Q = require('q');

const promise = Q.promise((resolve, reject) => {
    setTimeout(() => {
      const value = Math.random() * 10;
      if (value < 5) {
        resolve(value);
      } else {
        reject(new Error('Value is too big'));
      }
    }, 1000);
  });
  
  promise.then((value) => {
    console.log(value);
  }).catch((error) => {
    console.log(error);
  });

 案例二:

var Q = require('q');

//Q对象中传入参数,就是调用了Q.promise对象
var outputPromise = Q('jiazhuoqun').then(function(data){  //data的属性和Q里面传的参数相关
   console.log(data);
//    return data;
   return '1'; 
   throw new Error("haha ,error!");  //return或throw后面的函数都不执行
});

outputPromise.then(function(data){  //此data的属性和outputPromise返回的value的属性相关
    console.log("FULFILLED : "+data);
},function(err){
    console.log("REJECTED : "+err);  //在此处分别进行判断
})

代码解析:

1、代码中引入了 Q 模块,并使用 Q() 方法创建一个 Promise 对象,并传入了一个参数 'jiazhuoqun',该参数会被传递到 then() 方法中的回调函数的参数中,这种方式默认是成功状态的promise

2、在 then() 方法中,添加了一个回调函数,该函数会在 Promise 对象的状态变为 fulfilled 时执行。在此回调函数中,使用 console.log() 打印出传入的参数 data,并返回了一个字符串 '1'。

3、使用 then() 方法添加了另一个回调函数,该函数会在 Promise 对象的状态变为 fulfilled 时执行。在此回调函数中,使用 console.log() 打印出参数 data,即上一个回调函数返回的值 '1'。

4、最后,使用 catch() 方法添加了一个回调函数,该函数会在 Promise 对象的状态变为 rejected 时执行。在此回调函数中,使用 console.log() 打印出错误信息。

4、Q.all()方法

        将多个 Promise 对象组合成一个新的 Promise 对象,等待所有 Promise 对象都变成 resolved 状态后返回一个包含所有 Promise 对象的值的数组,如果其中任何一个 Promise 对象变成 rejected 状态,则返回第一个 rejected 状态的 Promise 对象的错误信息。

const Q = require('q');

const promise1 = Promise.resolve("成功1");
const promise2 = Promise.resolve("成功2");

Q.all([promise1,promise2])
.then((values) => {
    console.log(values); //[ '成功1', '成功2' ]
    console.log("doneCallback"); //doneCallback
})
.catch((values) => {
    console.log(values);
    console.log("failCallback");
})

 五、promise对象用法

定义:Promise实例对象是异步编程的抽象。它是一个对象,代理返回值或由必须进行异步处理的函数抛出的异常

1、promise基本使用

1、setTimeout函数是异步函数,也是异步任务,其回调函数是同步函数,同步任务

2、回调函数中的resolve和reject方法,哪个在前(先执行),则在then方法中执行哪个

//实例化 Promise 对象
const p = new Promise(function (resolve, reject) {
    setTimeout(function () {

        // 成功调用resolve()处理
        let data = "数据读取成功";
        resolve(data);

        // 失败调用reject()处理
        let err = "数据读取失败";
        reject(err);

    }, 1000);
});

//调用 promise 对象的 then 方法
p.then(function (value) {
    console.log(value);
}, function (reason) {
    console.error(reason);
});

2、promise对象的常用方法

1、resolve()方法的参数将会传递给后续promise对象方法中的doneCallback函数,成为doneCallback函数的实参

2、reject()方法的参数将会传递给后续promise对象方法中的failCallback函数,成为failCallback函数的实参

3、resolve和reject方法只能执行一个,哪个在前那个执行,因为promise对象只能有一种状态

(1)、Promise.prototype.then()

添加 fulfilledrejected 状态的回调函数。 

1、参数分析

Promise.prototype.then()函数是 Promise 对象原型上的一个方法,它接受两个回调函数作为参数,一个用于处理 Promise 对象 resolved 的结果,一个用于处理 rejected 的结果,然后返回一个新的 Promise 对象。它的语法如下:

promise.then(onResolved[, onRejected]);

then函数传入两个函数对象:

  • promisefulfilled(满足)的时候执行的函数
  • promiserejected(拒绝)的时候执行的函数
  • 永远默认then函数的第一个回调函数为完成执行函数(doneCallbacks),第二个回调函数为失败执行函数(failCallbacks)

        其中,onResolvedonRejected 都是可选参数,如果它们不是函数,将会被忽略。如果 onResolved 是一个函数,则在 Promise 对象 resolved 后将会调用它,并将 Promise 对象的结果作为参数传递进去;如果 onRejected 是一个函数,则在 Promise 对象 rejected 后将会调用它,并将 Promise 对象的 rejection 值作为参数传递进去。如果 onResolvedonRejected 抛出异常,则返回的 Promise 对象将会 rejected,且 rejection 的值为抛出的异常。

      每次只有一个函数可能被执行,因为返回的promise只可能存在一个状态,要么被promise被解决了,要么promise没有被解决。

2、作用分析

        当得到一个promise以后,我们需要指定对应的处理函数,也就是then函数来指定对应的处理函数。

3、返回值分析

最终then函数会返回一个新的promise

返回的新 Promise 对象有以下几种情况:

  1. 如果 onResolved 返回一个值(或返回一个新的 Promise 对象),则新 Promise 对象将会 resolved,并以该值为结果。
  2. 如果 onRejected 抛出一个异常,则新 Promise 对象将会 rejected,且 rejection 的值为抛出的异常。
  3. 如果 onResolvedonRejected 没有返回值,则新 Promise 对象将会 resolved,并以原 Promise 对象的结果为结果。

理解为

1、then函数是承接前面的promise对象,

  • then函数自身有回调函数
    • (如果有返回值的话)才会真正返回一个新的有状态,有数据的promise对象,
    • (如果没有返回值)则由then函数返回一个resolved状态,值为undefined的promise对象
  • then函数自身没有任何回调函数
    • 则承接前面的promise对象直接返回,不做处理

2、每次执行完一个promise对象后,就会执行下一个promise对象,如果该promise对象一直未被执行,则一直往后传递,直到遇到可执行该promise对象的回调函数。是以promise对象为基本执行单位的

3、 如果你只关心任务的成功或者失败状态,上面的fulfilled_function或者rejected_function可以设置为空。

var Q = require('q');

//Q对象中传入参数,就是调用了Q.promise对象
var outputPromise = Q('jiazhuoqun').then(function(data){  //data的属性和Q里面传的参数相关
   console.log(data);  //由then函数返回一个resolved状态,值为undefined的promise对象
}).then(function(data){
    console.log("doneCallback: " + data); //执行这个
},function(error){
    console.log('failCallback: ' + error) //不执行这个
});
const promise = new Promise((resolve, reject) => {
  resolve('resolved value');
});

promise.then(
  value => console.log('onResolved:', value), // 'onResolved: resolved value'
  reason => console.log('onRejected:', reason) // 不会执行,因为 Promise 被 resolved 了
);

        在这个例子中,我们创建了一个 Promise 对象,并且在构造函数的回调函数中调用了 resolve 方法,将 Promise 对象的状态设置为 resolved,同时将一个值作为 Promise 对象的结果。接着,我们调用了 then 方法,并传递了两个回调函数作为参数。因为 Promise 对象已经 resolved 了,所以第一个回调函数会被执行,并将 Promise 对象的结果作为参数传递进去。这里我们的回调函数只是简单地将结果打印到控制台上。因为 Promise 对象没有被 rejected,所以第二个回调函数不会被执行。

4、流式操作

1、then函数最后会返回一个新的promise,这样我们就可以将多个promise串联起来,完成一系列的串行操作,可以通过 .then() 方法的链式调用,来对 Promise 对象进行流式操作。

const myPromise = new Promise((resolve,reject) => {
    setTimeout(() => {
        reject("hello!");
    },1000)
})

myPromise.then((result) => {
    console.log(result);
    return result + 'world!';
}).then((result) => {
    console.log(result);
    throw new Error("不好意思,出错了!");
}).catch((error) => {
    console.error("catch: " + error);
});

        在上面的代码中,我们通过 .then() 方法的链式调用,将一个回调函数的返回值作为另一个回调函数的参数,实现了对 Promise 对象的连续处理。如果中间出现了错误,我们可以使用 .catch() 方法来捕获它,并在控制台输出错误信息。

5、组合操作

有多个任务需要一起并行操作,然后所有任务操作结束后,或者其中一个任务失败后就直接返回,q模块的中的all函数就是用来解决这个问题的。

组合操作即将多个异步操作组合在一起。

        组合操作的思想是将一个 Promise 对象作为另一个 Promise 对象的输入,将它们链接在一起,以便一个 Promise 对象在另一个 Promise 对象成功或失败之后执行。这个过程可以通过在 then() 方法中返回一个 Promise 对象来实现。具体来说,then() 方法可以返回一个新的 Promise 对象,以便在 Promise 对象成功或失败时执行另一个异步操作。

下面是一个示例,展示了如何使用 Promise.prototype.then() 方法进行组合操作:

function fetchJSON(url) {
  return fetch(url).then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    } else {
      return response.json();
    }
  });
}

fetchJSON('/api/data')
  .then(data => {
    // 处理第一个异步操作的成功值
    return fetchJSON(`/api/more-data/${data.id}`);
  })
  .then(moreData => {
    // 处理第二个异步操作的成功值
    console.log(moreData);
  })
  .catch(error => {
    // 处理任何一个异步操作的错误值
    console.error(error);
  });

        在上面的代码中,fetchJSON() 函数返回一个 Promise 对象,表示异步操作的结果。第一个 then() 方法将在第一个异步操作成功后调用,将其成功值作为参数传递给它。它返回一个新的 Promise 对象,以便在第二个异步操作成功或失败时执行。

        第二个 then() 方法将在第二个异步操作成功后调用,将其成功值作为参数传递给它。在这里,我们只是将其打印到控制台,但是你可以使用该值执行其他操作。

        如果任何一个异步操作失败,Promise 链将在 catch() 方法中捕获错误,并将错误传递给它。在这里,我们只是将错误打印到控制台,但是你可以使用错误执行其他操作。

(2)、Promise.prototype.catch()

1、如果只想处理错误状态,我们可以使用 catch 方法

2、catch() 方法是 Promise 对象原型链上的方法,用于捕获 Promise 中的异常。当 Promise 被 rejected 时,catch() 方法会被调用,传入被拒绝的原因,并返回一个新的 Promise 对象,该 Promise 对象是 resolved 的,其值为 catch() 方法的回调函数的返回值。

3、添加 rejected 状态的回调函数,相当于 .then(null, rejection) 的别名。可以方便地处理 Promise 对象的异常。

例如:

const promise = new Promise((resolve, reject) => {
  reject(new Error('Something went wrong'));
});

promise.catch((error) => {
  console.log(error.message); // 打印 'Something went wrong'
});

当 Promise 被 rejected 时,catch() 回调函数会被调用,传入被拒绝的原因并打印错误信息。

catch() 方法还可以与 then() 方法组合使用,实现链式操作。例如:

const promise = new Promise((resolve, reject) => {
  reject(new Error('Something went wrong'));
});

promise
  .then((result) => {
    // 正常情况下执行的操作
    console.log(result);
  })
  .catch((error) => {
    // 异常情况下执行的操作
    console.log(error.message);
  });

        在这个例子中,then() 方法只会在 Promise 对象 resolved 的情况下被调用。如果 Promise 被 rejected,catch() 方法会被调用并打印错误信息。

        注意,catch() 方法只能捕获当前 Promise 对象所在的执行环境中的异常,不能捕获其他 Promise 对象的异常。如果要捕获多个 Promise 对象中的异常,可以使用 Promise.all() 方法。

(3)、Promise.prototype.finally()

1、添加不论状态如何都会执行的回调函数。

  Promise.prototype.finally() 是一个用于添加在 Promise 链式操作的末尾的回调函数。该回调函数将在 Promise 状态变为 resolved 或 rejected 时都会被调用,无论其状态如何。finally() 方法在不需要修改 Promise 返回值的情况下,用于执行一些清理工作,例如释放资源、关闭文件等。

该方法的语法如下:

promise.finally(onFinally)

其中,onFinally 是一个无论 Promise 状态如何都会被调用的回调函数。

下面是一个使用 finally() 方法的例子:

fetch('/api/data')
  .then(response => response.json())
  .then(data => {
    // 处理数据
  })
  .catch(error => {
    // 处理错误
  })
  .finally(() => {
    // 清理工作
  });

        在这个例子中,无论 fetch() 方法返回的 Promise 状态如何,都将执行 .finally() 中的清理工作。例如,如果网络请求成功并得到了数据,那么 .finally() 方法中的清理工作将在数据处理完毕后被执行。如果网络请求失败或中途出现了错误,也会执行 .finally() 中的清理工作。

        需要注意的是,finally() 方法会返回一个新的 Promise 对象,因此它也可以被链式调用。在这种情况下,后面的 .then() 方法将接收到 finally() 返回的 Promise 对象。如果 .finally() 中的回调函数返回了一个 Promise 对象,那么后面的 .then() 方法也将接收到该 Promise 对象。

下面是一个使用 finally() 方法的链式调用的例子:

fetch('/api/data')
  .then(response => response.json())
  .then(data => {
    // 处理数据
  })
  .catch(error => {
    // 处理错误
  })
  .finally(() => {
    return new Promise(resolve => {
      // 执行一些异步操作
      setTimeout(() => {
        console.log('清理工作已完成');
        resolve();
      }, 1000);
    });
  })
  .then(() => {
    console.log('操作已完成');
  });

        在这个例子中,.finally() 方法中的回调函数返回了一个 Promise 对象。这个 Promise 对象将在 1 秒钟后被 resolved,然后后面的 .then() 方法将接收到该 Promise 对象,继续进行链式操作。

(4)、自定义done方法

https://www.cnblogs.com/jimaww/p/10065592.html

        Promise 对象的回调链,不管以then方法或catch方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为 Promise 内部的错误不会冒泡到全局)。因此,我们可以提供一个done方法,总是处于回调链的尾端,保证抛出任何可能出现的错误。

        JavaScript 中没有 Promise.prototype.done 这个方法。这是因为 Promise.prototype.done 不是标准的 Promise 方法,而是某些库(例如 Q)提供的方法。

        虽然确实存在 Promise 内部错误不会冒泡到全局的情况,但是推荐的方式是在 Promise 链的最后添加 catch 方法来处理错误,而不是使用非标准的 done 方法。

        如果在 Promise 链中的某个位置抛出了异常,那么这个异常将会被传递给 Promise 链中最近的 catch 方法。因此,您可以在 Promise 链的最后添加一个 catch 方法,以确保任何异常都会被捕获并处理,而不需要使用 done 方法。

1、实现原理

Promise.prototype.done = function (onFulfilled, onRejected) {
    this.then(onFulfilled, onRejected)
        .catch(function (reason) {
        // 抛出一个全局错误
        setTimeout(() => { throw reason }, 0);
    });
};

        从上面代码可见,done方法的使用,可以像then方法那样用,提供fulfilledrejected状态的回调函数,也可以不提供任何参数。但不管怎样,done都会捕捉到任何可能出现的错误,并向全局抛出。

2、案例

Promise.prototype.done = function (onFulfilled, onRejected) {
    this.then(onFulfilled, onRejected)
        .catch(function (reason) {
        // 抛出一个全局错误
        setTimeout(() => { throw reason }, 0);
    });
};

const myPromise = new Promise((resolve,reject) => {
    setTimeout(() => {
        reject("hello!");
    },1000)
})

myPromise.then()
.then()
.done();

第五章、环境与模块

一、开发环境与生产环境

在软件开发中,通常有两个主要的环境:开发环境和生产环境。

1、开发环境

        开发环境是指在开发过程中使用的环境,它通常包括开发人员使用的计算机、编译器、编辑器、测试工具等。在开发环境中,开发人员可以创建、测试和调试软件。

        举个例子,假设你正在开发一个 Web 应用程序,你会在本地计算机上使用编辑器和浏览器来编写和测试代码。这就是开发环境。

2、生产环境

        生产环境是指部署软件的实际环境,例如生产服务器、Web服务器、移动设备等。在生产环境中,软件需要能够稳定、可靠地运行,以提供服务和满足用户需求。

        一旦你完成了开发,你需要将应用程序部署到生产服务器上,让用户能够访问它。在生产服务器上,应用程序需要以稳定、可靠的方式运行,并且需要处理大量的用户请求。这就是生产环境。

3、开发环境和生产环境的区别

        由于开发环境和生产环境之间存在差异,因此软件的部署需要注意这些差异。例如,在开发环境中,可能会使用不同的数据库或测试数据,而在生产环境中需要使用真实的数据库和数据。此外,在开发环境中可能会使用调试工具或打印调试信息,但在生产环境中需要删除这些信息以提高性能和安全性。

        在开发过程中,应该尽可能模拟生产环境,并在测试和部署之前进行彻底测试,以确保软件在生产环境中能够正常运行。

        在开发环境中,你可能会使用 mock 数据、虚拟的服务等方式进行开发和测试,而在生产环境中,你需要连接到真实的数据库、使用真实的服务器等。

        此外,在生产环境中,你需要确保应用程序的安全性、可靠性和性能,并且需要监控应用程序的运行情况。因此,在生产环境中需要采用一些额外的措施来确保应用程序的稳定和安全。

二、JS环境

JS环境包括Node.js环境和浏览器环境

1、Node.js环境

        Node.js是一个基于Chrome V8引擎的JavaScript运行环境,可以使JavaScript脱离浏览器运行在服务器端。在Node.js环境中,可以使用JavaScript编写各种类型的应用程序,例如Web应用程序、命令行工具、桌面应用程序等。Node.js提供了许多内置的模块和API,例如文件系统、网络通信、进程管理、数据库访问等,方便开发者进行各种操作。

Node.js简介_node.js是什么_s_kzn的博客-CSDN博客

        JavaScript是一种单线程的编程语言,它的执行是同步的。这意味着JavaScript代码中的每个语句都会按照它们在代码中出现的顺序依次执行,而且在执行完一个语句之前,不会执行下一个语句。这种执行方式被称为同步执行。但是,JavaScript也有异步执行的机制,例如通过setTimeout()函数来实现异步执行。setTimeout()函数可以让JavaScript在指定的时间之后执行一个函数,而不会阻塞后续代码的执行。这种执行方式被称为异步执行。

JS是同步的,但是用上回调函数后一般就是异步的

1.1、Node.js定义

Node.js是JavaScript语言的服务器运行环境。

    Node.js 就是运行在服务端的 JavaScript。
    Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。
    Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。

1.2、为什么要用node.js

        javaScript语言本身是完善的函数式语言,在前端开发时,开发人员往往写得比较随意,让人感觉JavaScript就是个“玩具语言”。无法像其他编程语言一样满足工程的需要。

        但是,在Node环境下,通过模块化的JavaScript代码,加上函数式编程,并且无需考虑浏览器兼容性问题,直接使用最新的ECMAScript 6标准,可以完全满足工程上的需求。

     Node.js 适合以下场景:

1、实时性应用,比如在线多人协作工具,网页聊天应用等。

2、以 I/O 为主的高并发应用,比如为客户端提供 API,读取数据库。

3、流式应用,比如客户端经常上传文件。

4、前后端分离。

1.3、npm定义

        npm(即 node package manager )是Node的包管理工具,能解决NodeJS代码部署上的很多问题npm里面有接近60万个别人已经写好的包,到目前为止,npm差不多收集了60万个别人写好的包,其实每个包就是一个功能,一个需求,npm里面有关于创建服务器的包,启动电脑摄像头的包,如果正好我们有这些需求,那么我们就没必要自己去写代码,完全可以用别人已经写好的包

        npm是随同Nodejs一起安装的包管理工具,能解决Nodejs代码部署上的很多问题,常见的使用场景有以下几种:

  •     允许用户从NPM服务器下载别人编写的第三方包到本地使用。
  •     允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。
  •     允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用

由于新版的nodejs已经集成了npm,所以之前npm也一并安装好了。同样可以通过输入

" npm -v " 来测试是否成功安装。

如果安装的是旧版本的 npm,可以很容易得通过 npm 命令来升级,命令如下:

     c:\ npm install npm -g

1.4、npm 命令安装模块

npm 的包安装分为本地安装(local)、全局安装(global)两种,

命令如下:

  • npm install express          //本地安装

  • npm install express -g         //全局安装

如果出现以下错误: 

npm err! Error: connect ECONNREFUSED 127.0.0.1:8087

解决办法为:

 $ npm config set proxy null

 (1)本地安装

    将安装包放在 ./node_modules 下(运行 npm 命令时所在的目录),如果没有 node_modules 目录,会在当前执行 npm 命令的目录下生成 node_modules 目录。
    可以通过 require() 来引入本地安装的包。

(2)全局安装

    将安装包放在 /usr/local 下或者你 node 的安装目录。
    可以直接在命令行里使用。

如果你希望具备两者功能,则需要在两个地方安装它或使用 npm link。

1.5、选用 cnpm

1、cnpm 的官方介绍是:cnpm是一个完整 npmjs.org 镜像,你可以用此代替官方版本(只读),同步频率目前为 10分钟 一次以保证尽量与官方服务同步。既然都一样,那么cnpm为什么要出现呢?

由于npmjs.org的服务器在国外(即在“墙”外),国(墙)内开发者做项目的时候,很多“包”的下载速度极慢,在这种环境下阿里巴巴为了众多开发者的便捷便挺身而出推出了淘宝镜像(即cnpm),它把npm官方的“包”全部搬到国内,供广大开发者使用。

官网地址为: http://npm.taobao.org

    使用淘宝镜像的命令:

    npm install -g cnpm --registry=https://registry.npmmirror.com

2、安装cnpm,使用命令 npm install cnpm -g --registry=https://registry.npm.taobao.org 安装完成后用 cnpm -v 查看是否安装成功

3、cnpm的使用方法和npm完全一致,只需要把npm改成cnpm即可

1.6、Node.js环境支持的模块导入语法

Node应用是由模块组成,默认遵循的是CommonJS模块规范

        Node.js环境中,可以使用CommonJS模块和ES6的动态导入(即使用import()语法)。

        在Node.js 14及以上版本中,也可以使用ES6的静态导入(即使用import语法)。但是,在使用ES6的静态导入时,需要设置"type": "module"选项,告诉Node.js该文件是ES6模块。同时,还需要注意,ES6的静态导入目前还不支持文件的相对路径,只能使用绝对路径。

        总之,Node.js环境对于模块的支持情况是随着版本的升级而不断变化的,可以根据自己的需求和实际情况选择合适的模块化方案。

1.7、Node.js环境进程架构

        在Node.js环境中,通常有一个主进程(也称为主线程或主事件循环),该进程负责管理其他所有进程和线程。在Node.js中,主进程通常称为Event Loop(事件循环),它是一个单线程的进程,用于调度和处理所有的异步I/O操作和事件。

除了主进程之外,Node.js环境还包括其他一些进程和线程,例如:

  • LibUV线程池:用于执行I/O操作和定时器等异步任务。
  • V8引擎线程:用于执行JavaScript代码。
  • 工作进程:在Node.js中使用child_process模块创建的子进程,用于执行独立的任务,例如运行外部命令或计算密集型任务。

1.8、Node.js环境运行JS代码

2、浏览器环境

        浏览器环境是指JavaScript在Web浏览器中运行的环境。浏览器环境和Node.js环境的主要区别在于,浏览器环境中可以访问DOM和BOM对象,例如window、document、location等,而Node.js环境中不能访问这些对象。另外,浏览器环境中也提供了许多Web API和事件机制,例如XMLHttpRequest、WebSocket、Canvas、事件处理器等,可以方便地操作Web页面和响应用户交互。
        浏览器环境中也有一些JavaScript框架和库,例如jQuery、React、Vue等,它们可以使开发者更加高效地开发复杂的Web应用程序。

2.1、浏览器环境进程架构

        与Node.js不同,浏览器环境通常使用多进程架构来实现。浏览器将所有的Web内容(HTML、CSS、JavaScript)都视为单独的文档,每个文档都在单独的进程中运行。

通常,浏览器的多进程架构包括以下几个进程:

  • 浏览器进程:负责管理和协调其他进程的工作,例如绘制浏览器窗口、处理用户输入、处理网络请求等。
  • 渲染进程:负责将HTML、CSS和JavaScript编译为可视化的网页,每个标签页通常对应一个渲染进程。
  • 插件进程:负责运行浏览器插件,例如Flash Player和Silverlight等。
  • GPU进程:负责处理浏览器中的3D图形和硬件加速相关的任务。
  • 视频解码进程:负责解码视频文件,以确保视频可以正常播放。

需要注意的是,不同的浏览器有不同的架构和实现方式,上述进程架构仅是一个通用的概述。

2.2、浏览器环境运行JS代码

三、安装模块命令

npm   install   模块名@版本号   【--save-dev】

1、@版本号 说明

如不指定版本号,则默认为最新版本

2、--save-dev  说明

        如果您在运行 npm install 命令时省略了 --save--save-dev 选项,那么安装的包将被添加到 dependencies 字段中。这意味着该包是生产环境所需的依赖项,将包含在最终的应用程序代码中。

        如果您要安装的包只是用于开发或构建目的,那么应该使用 --save-dev 选项。这将把该包添加到 devDependencies 字段中,表明该包是开发环境所需的依赖项,不会被包含在最终的应用程序代码中。

        如果您只是想在开发过程中使用某个包,但不希望它出现在生产环境中,那么建议使用 --save-dev 选项。这样可以确保您的生产环境代码只包含必需的依赖项,并减小最终应用程序的大小。

四、卸载模块命令

npm uninstall 模块名

在您运行该命令后,指定模块将从您的项目中删除。 

第六章、del模块

一、模块介绍

        Node.js中的第三方模块,需要安装。del是一个用于删除文件和目录的NPM包,它可以很方便地删除不需要的文件或者目录。

二、安装

  • Node版本:v18.12.1
  • del模块版本 del@6.0.0

        因为最新del库是使用es6模块,所以不能使用commonjs语法,为了方便起见安装6.0.0版本,此版本支持CommonJs语法

npm  install  del@6.0.0  --save-dev

三、使用方法

1、删除文件

        使用del模块可以很容易地删除指定的文件。以下是一个简单的示例,删除所有匹配build/目录下以.js结尾的文件:

const del = require('del');

del(['build/**/*.js']).then((deletedPaths) => {
  console.log('Deleted files:\n', deletedPaths.join('\n'));
});

        在上面的示例中,del方法接受一个文件路径的数组作为参数,使用通配符**匹配所有子目录,并使用.js作为文件名的匹配模式。当文件删除成功后,del方法将返回一个Promise对象,并且Promise的返回值是一个数组,其中包含已删除的所有文件的路径。

2、删除目录

        除了可以删除文件之外,del模块还可以删除指定的目录。以下是一个简单的示例,删除名为dist/的目录:

const del = require('del');

del(['dist/']).then(() => {
  console.log('Deleted directory: dist/');
});

        在上面的示例中,del方法接受一个目录路径的数组作为参数,当目录删除成功后,del方法将返回一个Promise对象,但是在这种情况下,Promise的返回值为空。

3、异步删除

        在上面的示例中,del方法返回的是一个Promise对象。这是因为del方法是异步执行的,它会在后台删除文件和目录,而不会阻塞代码执行。如果需要等待删除操作完成后再执行其他代码,可以使用async/await或者.then()方法处理Promise的返回值。

以下是使用async/await处理Promise的示例:

const del = require('del');

(async () => {
  const deletedPaths = await del(['build/**/*.js']);
  console.log('Deleted files:\n', deletedPaths.join('\n'));
})();

        在上面的示例中,我们使用了async/await语法来等待del方法返回的Promise。async/await语法可以让我们更清晰地处理异步代码。

四、总结

  del模块是一个用于删除文件和目录的NPM包。使用del模块可以很方便地删除指定的文件或者目录。del方法接受一个文件或目录路径的数组作为参数,并返回一个Promise对象。当文件或目录删除成功后,del方法将返回已删除文件或目录的路径。

第七章、request模块

一、模块介绍

  request 是 Node.js 中一个常用的第三方 HTTP 请求库,它可以用于向远程服务器发送 HTTP 请求,并获取服务器的响应结果。使用 request 可以方便地实现多种 HTTP 请求操作,例如 GET、POST、PUT、DELETE 等。

二、安装

  • request版本:request@2.88.2

npm  install  request@2.88.2  --save-dev

三、参数分析

1、options 参数

options 参数是一个对象,用于指定请求的详细信息

const options = {
  url: 'https://www.example.com',
  method: 'GET',
  headers: {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
  }
};

        上面的 options 对象指定了要请求的 URL、请求的方法是 GET、以及请求头中的 User-Agent 字段。

2、callback 参数

callback 参数是一个函数,用于处理请求的响应。它有三个参数:errorresponsebody

request(options, (error, response, body) => {
  if (error) {
    console.error(error);
    return;
  }
  console.log(`statusCode: ${response.statusCode}`);
  console.log(`body: ${body}`);
});

上面的代码发出了一个 GET 请求,并将响应的状态码和正文输出到控制台。

四、方法汇总

除了 request 方法,request 模块还提供了许多其他方法和功能,例如:

  • request.get(options, callback) 发出一个 GET 请求
  • request.post(options, callback) 发出一个 POST 请求
  • request.put(options, callback) 发出一个 PUT 请求
  • request.delete(options, callback) 发出一个 DELETE 请求
  • 支持 Promise API
  • 支持流式上传和下载
  • 支持 HTTPS 请求
  • 支持 cookie 管理

总之,request 模块是一个非常强大、灵活、易用的 HTTP 请求库,适用于各种场景。

1、request.post(options, callback)方法详解

javascript - 如何使用 Protractor 发送 POST - IT工具网

request.post() 方法的返回值是一个 Request 对象。这个对象可以用来在请求的过程中进行一些操作,例如中止请求、暂停请求等。

request.post() 方法的回调函数中包含三个参数:errresponsebody。其中,

  • err 是错误对象,如果请求出现了错误则为真,否则为假;
  • response 是 HTTP 响应对象,包含响应的头信息和状态码等;
  • body 是响应的主体数据,可以是字符串、二进制数据、JSON 等格式,具体取决于响应的内容类型。

request.post() 方法的回调函数中,你可以根据返回的值来判断请求是否成功,例如:

request.post(options, function (err, response, body) {
  if (err) {
    console.error('请求出错:', err);
  } else if (response.statusCode !== 200) {
    console.error('请求失败:', response.statusCode);
  } else {
    console.log('请求成功:', body);
  }
});

        在上面的例子中,如果请求出错,会输出错误信息;如果请求响应的状态码不是 200,会输出请求失败信息;如果请求成功,会输出响应的主体数据。 

第八章、fs模块

nodejs学习-fs模块_有爱的萌哥的博客-CSDN博客

一、模块介绍

  fs 模块是 Node.js 内置的核心模块之一,可以直接在代码中使用,不需要额外安装或导入。在 Node.js 的 require() 方法中引入 fs 模块即可:

const fs = require('fs');

二、方法介绍

        在 Node.js 中的很多异步 API 中都包含了一个回调函数,在执行异步操作完成后,该回调函数将被调用。这种回调函数的通用惯例是将第一个参数命名为 err,表示操作是否成功。如果操作成功,err 将为 nullundefined,否则 err 将包含一个错误对象,该对象包含了出错的原因。

1、fs.readFile() 方法

fs.readFile() 方法是 fs 模块中提供的一个读取文件内容的方法,其语法如下:

fs.readFile(path[, options], callback)

其中,

  • path 参数表示要读取的文件路径,可以是字符串或 Buffer 类型;
  • options 参数是一个对象,用于指定读取选项,比如指定编码格式等;
  • callback 参数是一个回调函数,用于在读取文件完成后处理文件内容。包含两个参数,分别为 errdata。下面是对这两个参数的解读:
    • err:表示读取文件时出现的错误,如果读取文件过程中没有出现错误,则 err 的值为 null。如果读取文件过程中出现错误,可以通过判断 err 是否为 null 来确定读取是否成功。如果 err 不为 null,则通常需要在代码中对错误进行处理,比如输出错误信息、记录错误日志等。
    • data:表示读取到的文件内容。如果读取文件成功,则 data 的值为一个字符串或一个 Buffer,具体取决于 options 参数指定的编码格式。如果读取文件失败,则 data 的值为 undefined
const fs = require('fs');

fs.readFile('example.txt', 'utf8', (err, data) => {
  if (err) {
    console.error(err);
    return;
  }
  console.log(data);
});

        在这个例子中,fs.readFile() 方法会异步地读取 example.txt 文件的内容,编码格式为 utf8,读取完成后将内容传递给回调函数。如果读取文件过程中出现错误,则会将错误信息传递给回调函数的第一个参数 err。否则,回调函数的第二个参数 data 中将包含读取的文件内容。

        需要注意的是,fs.readFile() 方法读取的是整个文件的内容,适用于文件比较小的情况。如果需要读取大文件,可以使用 fs.createReadStream() 方法分块读取。

2、fs.existsSync()方法

  fs.existsSync() 方法是 fs 模块中提供的一个同步方法,用于检查指定的路径是否存在。其语法如下:

fs.existsSync(path)

        其中,path 参数表示要检查的文件或目录的路径,可以是字符串或 Buffer 类型。如果指定的路径存在,则返回 true;如果不存在,则返回 false

        需要注意的是,fs.existsSync() 方法是同步方法,调用该方法时会阻塞当前线程,直到文件或目录的存在与否被确定。因此,如果要检查多个文件或目录的存在情况,最好使用异步方法,比如 fs.stat()fs.access() 方法。

以下是一个使用 fs.existsSync() 方法检查文件是否存在的例子:

const fs = require('fs');

if (fs.existsSync('example.txt')) {
  console.log('File exists');
} else {
  console.log('File does not exist');
}

        在这个例子中,如果当前目录下存在名为 example.txt 的文件,则输出 File exists;否则输出 File does not exist

3、fs.createReadStream( )方法

fs.createReadStream() 方法是 fs 模块中提供的一个可读流方法,用于从指定的文件创建一个可读流。其语法如下:

fs.createReadStream(path[, options])

其中,

  • path 参数表示要读取的文件路径,可以是字符串或 Buffer 类型;
  • options 参数是一个可选的配置对象,用于指定读取文件的一些选项,比如起始位置、结束位置、编码格式等。

   fs.createReadStream() 方法返回一个可读流对象。可读流是一个 EventEmitter,可以通过监听其 data 事件来获取数据,也可以监听其 end 事件来获取读取结束的通知。

        如果指定的文件不存在或不可读,则 fs.createReadStream() 方法会抛出一个异常,需要进行错误处理。因此,在使用可读流读取文件时,通常需要监听其 error 事件,以便在发生错误时进行相应的错误处理。

        需要注意的是,可读流的内部缓冲区大小默认是 64KB,可以通过设置 highWaterMark 选项来修改。同时,可读流也支持暂停(pause())和恢复(resume())读取操作,以便更好地控制数据流的速度。

fs.createReadStream() 方法创建的可读流对象可以通过监听 data 事件来获取文件的内容。以下是一个读取文件并将内容输出到控制台的例子:

const fs = require('fs');

const stream = fs.createReadStream('example.txt', 'utf8');
stream.on('data', (chunk) => {
  console.log(chunk);
});
stream.on('end', () => {
  console.log('Finished reading file');
});
stream.on('error', (err) => {
  console.error(err);
});

        在这个例子中,首先创建一个可读流对象 stream,然后监听其 data 事件,在事件回调函数中输出每个数据块(chunk)的内容。最后监听 end 事件,表示读取完成,输出一条信息;同时也监听了 error 事件,以便在读取文件出错时进行错误处理。

        需要注意的是,当使用可读流读取文件时,数据不一定会一次性全部加载到内存中,而是分成多个数据块逐步加载。因此,需要在 data 事件回调函数中处理每个数据块,而不能将所有数据都存储在一个字符串或 Buffer 对象中,以免占用过多的内存。

4、fs.writeFile() 方法

fs.writeFile() 方法是 Node.js 中的一个文件系统模块的 API,用于异步地写入数据到文件中。它的语法如下

fs.writeFile(file, data[, options], callback)

其中,参数解释如下:

  • file:一个字符串类型的参数,表示要写入数据的目标文件的路径,可以是绝对路径或相对路径。
  • data:一个字符串或 Buffer 类型的参数,表示要写入的数据。
  • options:一个对象类型的参数,表示可选的配置项,包括编码格式和文件模式等。
  • callback:一个回调函数,表示当写入操作完成时要执行的函数,它有一个参数 err 表示写入操作中的错误信息。
    • 回调函数的第一个参数 err 表示写入操作中的错误信息。如果写入操作成功,则 err 将为 nullundefined。如果写入操作失败,则 err 将包含一个错误对象,该对象包含了出错的原因。
    • 在使用回调函数时,一定要正确处理错误信息,否则可能会导致程序异常或崩溃。在 Node.js 中,可以使用 try-catch 语句或者 Promise 对象等机制来处理错误信息。

fs.writeFile() 方法是一个异步方法,它会将数据写入到指定的文件中。如果该文件不存在,fs.writeFile() 方法会自动创建一个文件,如果文件已经存在,它会将原来的文件内容清空,然后写入新的数据。

如果您需要在写入数据到文件中之前对其进行编码,请使用 options 参数指定编码格式,例如:

fs.writeFile('message.txt', 'Hello Node.js', { encoding: 'utf8' }, function (err) {
  if (err) throw err;
  console.log('The message has been saved!');
});

        此代码将字符串 "Hello Node.js" 写入到文件 message.txt 中,并将文件编码格式设置为 utf8。当写入完成时,将执行回调函数,并在控制台输出一条消息。

        需要注意的是,fs.writeFile() 方法会覆盖目标文件中的所有内容,如果您需要追加数据到文件中,而不是覆盖原有内容,请使用 fs.appendFile() 方法。

第九章、path模块

一、模块介绍

  path 模块是 Node.js 中一个核心模块,它提供了处理文件路径和目录路径的实用工具函数,可以跨平台地处理路径。

不需要额外安装或导入。在 Node.js 的 require() 方法中引入 path 模块即可:

const path = require('path');

二、特殊参数

1、__filename

指代当前文件的文件名

2、__dirname

指代当前文件的父目录名称

二、方法介绍

1、path.resolve()

1、返回一个绝对路径,默认从当前文件的父目录开始,往后添加路径

2、使用绝对路径是很重要的,因为它可以避免因为相对路径的变化而导致路径解析错误的问题。

path.resolve() 方法是 Node.js 中的一个核心模块 path 提供的方法,用于将一组路径或路径片段解析成一个绝对路径。它可以接受任意数量的参数,每个参数都会被视为一个路径片段,并依次拼接成一个绝对路径。

path.resolve() 方法的用法如下:

path.resolve([...paths])

        其中 paths 是一个可选的参数列表,可以传入多个路径或路径片段。下面是一些常见的用法和示例:

  1. 拼接路径

如果 path.resolve() 方法接受的参数只有一个,那么它会将这个参数解析成一个绝对路径并返回。例如:

const path = require('path');

const fullPath = path.resolve('dist');
console.log(fullPath); //E:\Cypress\TypeScript_demo1\js\src\dist

这里的 /foo/bar 是一个绝对路径,./baz 表示当前目录下的 baz 目录。

  1. 解析相对路径

如果传入的参数是多个路径片段,那么 path.resolve() 方法会将这些路径片段依次拼接起来,并解析成一个绝对路径。例如:

const path = require('path');

const fullPath = path.resolve('foo', 'bar', 'baz');
console.log(fullPath); // '/Users/john/foo/bar/baz'

这里的 foobarbaz 都是相对路径,但是最终解析出来的是一个绝对路径。

  1. 解析相对于根目录的路径

如果传入的路径以 / 开头,那么它会被解析成相对于根目录的绝对路径。例如:

const path = require('path');

const fullPath = path.resolve('/foo', '/bar', 'baz');
console.log(fullPath); // '/bar/baz'

这里的 /foo/bar 都是绝对路径,但是最终解析出来的是相对于根目录的路径。

  1. 解析多个绝对路径

如果传入的路径中有一个或多个绝对路径,那么最后的解析结果将只包含最后一个绝对路径和它之后的路径。例如:

const path = require('path');

const fullPath = path.resolve('/foo', '/bar', '/baz', 'qux');
console.log(fullPath); // '/baz/qux'

        在这个例子中,/foo/bar 都被忽略了,只有最后一个 /baz 被保留了下来。

        总之,path.resolve() 方法非常实用,可以将多个路径片段解析成一个绝对路径,避免因为路径格式不同而导致的解析错误。

第十章、Math内置对象

一、概述

在JavaScript中,Math是一个内置的数学对象,提供了许多常用的数学操作和函数。

这些属性和方法的具体实现是由JavaScript的运行环境(浏览器或Node.js)提供的。 

Math对象是JavaScript的内置实例对象,因此可以直接调用Math接口中的属性和方法

二、常用方法

1、Math.floor(x: number): number 返回小于或等于x的最大整数。

console.log(Math.floor(9.8)); //9
console.log(Math.floor(3.4)); //3

 2、Math.random(): number  返回一个0到1之间的伪随机浮点数

console.log(Math.random()); //0.7764969637679975

3、Math.max(...values: number[ ]): number  返回一组数中的最大值。

console.log(Math.max(1,4,9,2)); //9

4、Math.min(...values: number[ ]): number  返回一组数中的最小值

console.log(Math.min(1,4,9,2)); //1

 六、JavaScript拓展知识

1、严格模式

1.1 概述

        严格模式是在 ECMAScript5(ES5)中引入的,在严格模式下,JavaScript 对语法的要求会更加严格,一些在正常模式下能够运行的代码,在严格模式下将不能运行

添加严格模式,主要有以下几个目的:

  • 消除 JavaScript 语法中一些不合理、不严谨的地方;
  • 消除代码中一些不安全的地方,保证代码的安全运行;
  • 提高 JavaScript 程序的运行效率;
  • 为以后新版本的 JavaScript 做好铺垫。

1.2 启用严格模式

在整个脚本第一行或者函数内部第一行添加"use strict";'use strict'指令

1.3 严格模式中的变化

'use strict';  严格模式

【JS ES6】use strict 严格模式_坚毅的小解同志的博客-CSDN博客2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值