异步 进程 Promise规范及应用

news2025/1/22 6:00:15

异步 

两个或多个事件不同时存在或发生,区别于同步,同步是顺序执行从上到下,而异步不需要顺序执行,且不依赖于前面的事情是否已完成。 

举例: 

//异步执行
let count = 1;
let timer = setTimeout(function () {
    count++;
    console.log('in', count)
}, 1000)
 
console.log('out', count)

 执行代码会发现,out必in先输出。

再举个例子:

//循环执行
let count = 1
let timer = setInterval(function () {
    count++
    console.log('in', count)
}, 1000)
 
setTimeout(function () {
    clearInterval(timer)
    console.log('clear')
}, 5000)

上面代码,每秒执行count++,且输出一次count的值,5秒后清空定时器。

在异步中最常见的方法setTimeout和setInterval

在第一个例子中,我们发现,代码并不是从上往下执行的,setTimeout方法打印的内容,是在out输出后打印的,这是为什么呢?

这是因为定时器方法被放到一个看不见的队列中,等待执行指令。

进程 & 线程

概念 & 区别 

在计算机组成原理中,进程是CPU资源分配的最小单位,线程是CPU调度的最小单位。

  • 进程内部有独立资源,进程与进程之间的资源是互不干扰的。
  • 线程是共用一个资源

可以用下面这个图理解: 

思考:浏览器chrome新开一个窗口,是进程还是线程?

答案:从上面我们了解到,进程与进程之间的资源是互不干扰的,而线程会共用一个资源。

假如我们在页面上执行了死循环,页面卡死。此时我们打开一个新窗口,还可以继续执行。如果窗口是线程,共用资源,那我们页面卡死之后,我们每打开一个页面都会卡死,因为资源是共用的,占用内存过多,导致所有页面都会卡死,这就很可怕。

所以窗口是进程,每个页面之间资源互不干扰,哪怕我们其中一个页面卡死,也可以打开新窗口,把卡死的窗口关掉。

 浏览器原理

刚刚提到浏览器窗口是一个进程,那么进程下面就会有多个线程。在浏览器中,有以下这些线程,支持页面的渲染:

1、GUI渲染线程

GUI渲染线程用来解析HTML、CSS、构建DOM树。

注意:与JS引擎线程互斥,当执行JS的时候GUI渲染会被挂起

2、JS引擎线程

JS引擎线程用来处理JS,解析执行脚本。会阻塞GUI线程。

为什么GUI渲染线程和JS引擎线程互斥呢?是为了防止先后顺序错乱,导致页面渲染不一致。

3、定时器线程

用来处理和执行异步定时器,接收JS引擎分配的定时任务并执行,处理完成后交给事件触发线程。

4、异步HTTP请求线程

异步执行网络操作,接收JS引擎分配的网络请求任务并执行,处理完成后交给事件触发线程。

5、事件触发线程

接收所有的时间,把接收到的事件按顺序排序,并交给JS引擎线程

总结:GUI渲染线程和JS引擎线程互斥,为了防止先后顺序错乱。当JS引擎线程执行到定时器任务时,会把任务分配给定时器线程。当执行到网络请求会把任务分配给异步HTTP请求线程。当定时器线程、异步HTTP请求线程处理完后,会交给事件触发线程。事件触发线程会把接收到的定时器任务、异步HTTP请求任务进行排队,然后给到JS引擎执行。 

事件循环机制

执行栈中存放同步任务,事件队列中存放异步任务(把线程处理好的异步任务放到队列中等待执行) 

执行栈中的任务执行完后,会到事件队列中查看是否有需要执行的任务。事件队列任务执行完成后,又会回到执行栈继续执行。这就是事件循环机制。 

PS:如果执行栈中任务没执行完,需要等待执行栈中的任务执行完毕,才会去执行事件队列中的内容,导致定时任务执行时间并不准确,最小执行时间为4ms。  

事件队列中又分为宏任务和微任务

  • 宏任务:script、setTimeout、setInterval、I/O
  • 微任务:promise、defineProperty、proxy

PS:微任务紧迫性更高。微任务执行完后,或者没有微任务,才会去执行下一个宏任务。

总结:

JS单线程 => 同步 + 异步 => 执行栈 + 任务队列(任务优先级)

举例:

setTimeout(() =>{
    console.log('Timeout')
}, 0)
 
new Promise(resolve => {
    console.log('new Promise')
    resolve();
}).then(() =>{
    console.log('promise then')
}).then(() =>{
    console.log('promise then then')
})
 
console.log('hi')

可以暂停思考一下打印顺序。

new Promise中的内容属于同步,then执行的回调才是异步的,因此new Promise是第一个执行的任务,然后是最后一行hi是同步任务。then中的内容属于Promise宏任务中的微任务,顺序执行,因此,微任务1和2执行。最后执行setTimeout的宏任务。

关于Promise我们之前在ES6语法一文中已经做了初步的介绍。

异步模块中,我们对Promise做了基本介绍。这里就不再赘述了。

这里主要是对Promise的规范以及应用的展开。

Promise的整体语法结构 

这部分看不懂的,可以看ES6语法文章中,对Promise用法的拆解,这里是作为一个整体角度,把Promise中可以使用的方法以及回调函数,做了一个概述。 

new Promise((resolve, reject)=>{
    // 这里执行异步操作,然后将异步操作的结果,以及参数value/reason,通过resolve或者reject传入then的回调函数中
    resolve(value)
    // reject(reason)
}).then(res => {
    //此处res就是上面resolve传入的value,在then中可以返回一个新的promise对象用来处理下一次异步操作
    //此处可以对res进行处理
    return new Promise((resolve, reject)=>{
        //将异步操作的结果放入resolve/reject,传入then
        resolve(value)
        // reject(reason)
    })
}).then(res => {
    //如果不用进行异步操作,只是对数据进行简单处理,可以使用这种方式进行下一次then
    return Promise.resolve(res)
}).then(res => {
    //第三个promise的处理结果
}, err => {
    //此处err是reject传入的reason
    //其实then中可以传入两个回调函数参数,第一个参数是promise中使用的resolve后进入成功回调函数
    //第二个参数是promise中使用reject后进入的失败(或者说是错误)回调函数,在then中失败回调函数可以忽略,然后在最后通过下面的catch方法接收失败的结果
}).catch(err => {
    //此处err是reject传入的reason
    //此处可对err信息进行一些打印操作
})
  • Promise.all(iterable): 等待所有 Promise 完成,如果所有 Promise 都成功,则返回一个包含所有结果的数组;如果有任何一个 Promise 失败,则返回第一个失败的 Promise 的原因。
  • Promise.race(iterable):等待第一个完成的 Promise,并返回其结果或原因。

当需要多个异步操作处理完成,才能对数据进行处理可使用promise.all方法。里面传入一个promise数组,then中的result是一个数组。举例:

Promise.all([
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve({name: 'why', age: 18})
        }, 2000)
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve({name: 'kobe', age: 19})
        }, 1000)
    })
]).then(results => {
    console.log(results);
})

总结:then中的第二个失败回调,和catch方法一般是使用其中一个就可以了。如果多个回调,且每个回调需要处理不同的失败结果,可以使用then中的第二个回调。catch方法用来处理异步操作中其他不合理的代码逻辑。(catch可以处理的错误更多,then中第二个回调是专门用来处理回调失败的结果)

promise/A+规范

这个规范大家有兴趣可以看看:Promises/A+中文网

术语

  1. promise是一个有then方法的对象或者函数,行为遵循本规范
  2. thenable是一个有then方法的对象或者函数
  3. value是promise状态成功的值,也就是resolve的参数,包括各种数据类型,也包括undefined/thenable或者promise
  4. reason是promise状态失败时的值,也就是reject的参数,表示拒绝的原因
  5. exception是一个使用throw抛出的异常值

规范

promise有三种状态:pending、fulfilled、rejected

pending

  1. 初始化的状态,可改变
  2. 一个promise在resolve或者reject之前都处于这个状态
  3. 可以通过resolve改变promise状态为fulfilled
  4. 可以通过reject改变promise状态为rejected

fulfilled

  1. 最终态,不可变
  2. 一个promise被resolve后会变成这个状态
  3. 必须拥有一个value值

rejected

  1. 最终态,不可变
  2. 一个promise被reject后会变成这个状态
  3. 必须拥有一个reason值

总结:

pending → resolve(value) → fulfilled

pending → reject(reason) → rejected

then

Promise中应该提供一个then方法,用来访问最终的结果,无论是value还是reason。

Promise.then(onFulfilled, onRejected)

PS:一般以on开头的都是回调函数,比如说onClick、onChange

1、参数要求

(1)onFulfilled必须是函数类型,如果不是函数,应该被忽略

(2)onRejected必须是函数类型,如果不是函数,应该被忽略

说明then中两个参数都必须是函数,而把函数当做参数传递的,就叫回调函数。

2、onFulfilled特性

(1)在promise变成fulfilled时,应该调用onFulfilled,参数是value

(2)在promise变成fulfilled之前,不应该被调用【状态为fulfilled再调用then的第一个回调函数

(3)只能被调用一次【所以在实现的时候需要一个变量来限制执行次数】

3、onRejected特性

(1)在promise变成rejected时,应该调用onRejected,参数是reason

(2)在promise变成rejected之前,不应该被调用【状态为rejected调用then的第二个回调函数

(3)只能被调用一次【所以在实现的时候需要一个变量来限制执行次数】

4、onFulfilled和onRejected应该是微任务

这里用queueMicrotask来实现微任务的调用

5、then方法可以被调用多次

(1)promise状态变成fulfilled后,所有的onFulfilled回调都需要按照then的顺序执行,也就是按照注册顺序执行【所以在实现的时候需要一个数组来存放多个onFulfilled的回调】

(2)promise状态变成rejected后,所有的onRejected回调都需要按照then的顺序执行,也就是按照注册顺序执行【所以在实现的时候需要一个数组来存放多个onRejected的回调】

6、返回值

then应该返回一个promise

promise2 = promise1.then(onFulfilled, onRejected)

(1)onFullfiled或onRejected执行的结果为x,调用resolvePromise

(2)如果onFulfilled或者onRejected执行时抛出异常e,promise2需要被reject

(3)如果onFulfilled不是一个函数,promise2以promise1的value触发fulfilled

(4)如果onRejected不是一个函数,promise2以promise1的reason触发rejected

7、resolvePromise

resolvePromise(promise2, x, resolve, reject)

(1)如果promise2和x相等,那么reject TypeError

(2)如果x是一个promise

  • 如果x是pending状态,那么promise必须要在pending,直到x变成fulfilled or rejected
  • 如果x被fulfilled,fulfill promise with the same value
  • 如果x被rejected,reject promise with the same reason

(3)如果x是一个object或者是一个function

let then = x.then

  • 如果x.then这步出错,那么reject promise with e as the reason.【抛出异常】
  • 如果then是一个函数,then.call(x, resolvePromiseFn, rejectPromise)

resolvePromiseFn的入参是y,指向resolvePromise(promise2, y, resolve, reject);

rejectPromise的入参是r,reject promise with r.

  • 如果resolvePromise和rejectPromise都调用了,那么第一个调用优先,后面的调用忽略。
  • 如果调用then抛出异常e,reject promise with e as the reason.
  • 如果resolvePromise或rejectPromise已经被调用,那么忽略
  • 如果then不是一个function,fulfill promise with x

实现一个Promise

基本步骤 

先来看看Promise的基础版完整写法

new Promise((resolve, reject) => {
    //Promise大多用来处理异步操作,这里使用setTimeout模拟一个异步操作
    setTimeout(() => {
        resolve(value)
        // reject(reason)
    }, 1000)
}).then(value => {
    //第一个then
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(value)
            // reject(reason)
        }, 1000)
    })
}, reason => {
    
}).then(value => {
    //第二个then
    console.log(value)
}, reason => {
    
}).catch(reason => {})

从上面代码看出,一个完整的Promise链式调用大概可以分为以上形式,由此可以看出Promise中至少有resolve、reject、then、catch等函数,还有创建Promise实例的构造函数。这条链式调用的过程是:

  1. new Promise:调用Promise的构造函数,创建Promise实例,然后执行传入的(resolve, reject) => {}回调函数,执行回调里面的同步操作,setTimeout里面异步操作需要后面执行
  2. 通过1中的Promise实例调用then方法,then方法会返回一个Promise实例(不管你有没有与上面代码那样返回一个Promise),该方法需要传入成功和失败的回调函数,如果传入的不是函数,Promise会另外处理
  3. 整条链上的then是同步执行的,但传入then的成功回调和失败回调,只会在前一个resolve/reject执行之后才能执行(resolve和reject每次只能执行一个),如果执行then的时候前一个resolve/reject没有执行,其成功和失败的回调函数会先存起来,等到resolve/reject之后才会执行

实现过程

1、采用class实现Promise,首先定义三个常量用来表示Promise的状态(pending、fulfilled、rejected),然后实现一下构造函数,value和reason首先设置为null。

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MPromise {
    constructor() {
        //初始状态为pending
        this.status = PENDING;
        this.value = null;
        this.reason = null;
    }
}

2、接下来实现一下Promise中的resolve和reject方法,当我们调用resolve方法需要将Promise的状态改为fulfilled(成功),当调用reject时需要将Promise的状态改为rejected(失败)

resolve(value) {
    if (this.status === PENDING) {
        this.value = value;
        this.status = FULFILLED;
    }
}
reject(reason) {
    if (this.status === PENDING) {
        this.reason = reason;
        this.status = REJECTED;
    }
}

3、当我们new Promise((resolve, reject) => {})的时候我们是有传入一个回调函数的,所以我们的构造函数需要接收这个回调函数并执行这个回调函数,所以构造函数需要改为如下:

    constructor(fn) {
        //初始状态为pending
        this.status = PENDING;
        this.value = null;
        this.reason = null;
        try {
            //在此执行传入的回调函数,该回调函数的参数就是我们先前在类中定义的改变Promise状态的resolve和reject函数
            fn(this.resolve.bind(this), this.reject.bind(this));
        }catch (e) {
            this.reject(e)
        }
    }

4、所以目前实现Promise的代码应该是这样

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
 
class MPromise {
 
    constructor(fn) {
        // 初始状态为pending
        this.status = PENDING;
        this.value = null;
        this.reason = null;
        try {
            // 在此执行传入的回调函数,该回调函数的参数就是我们先前我们在类中定义的改变Promise状态的resolve和reject函数
            fn(this.resolve.bind(this), this.reject.bind(this));
        } catch (e) {
            this.reject(e);
        }
    }
 
    resolve(value) {
        // 当Promse为fulfilled和rejected状态时状态不再可变,只有pending状态时才能改变状态
        if (this.status === PENDING) {
            this.value = value;
            this.status = FULFILLED;
        }
    }
 
    reject(reason) {
        if (this.status === PENDING) {
            this.reason = reason;
            this.status = REJECTED;
        }
    }
}

5、接下来需要实现Promise的另一个方法then,then((value) => {}, (reason) => {})中需要传入当Promise变为fulfilled状态或者rejected状态之后的成功回调函数失败回调函数成功回调函数将在构造函数中调用resolve(value)的时候执行失败回调函数将在构造函数中调用reject(value)的时候执行。

//储存成功和失败回调函数的数组
FULFILLED_CALLBACK_LIST = [];
REJECTED_CALLBACK_LIST = [];

then(onFulfilled, onRejected) {
    //当传入的onFulfilled和onRejected不是函数的时候,我们需要将其转换成函数
    const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : value => {
        return value;
    }
    const realOnRejected = this.isFunction(onRejected) ? onRejected : reason => {
        return reason;
    }
    //then方法默认会返回一个Promise实例,使得我们能够一直执行链式调用
    const promise2 = new MPromise((resolve, reject) => {
        switch (this.status) {
            //当状态变为fulfilled表明已经执行resolve,this.value就是resolve(value)中传入的值
            case FULFILLED: {
                realOnFulfilled(this.value)
                break;
            }
            //当状态变为rejected表明已经执行reject,this.reason就是reject(reason)中传入的值
            case REJECTED: {
                realOnRejected(this.reason)
                break;
            }
            //当状态变为pending还未执行resolve或reject,此时暂时用数组将成功和失败的回调方法存储起来,所以需要在前面分别定义一个存储成功和失败的数组
            case PENDING: {
                this.FULFILLED_CALLBACK_LIST.push(realOnFulfilled)
                this.REJECTED_CALLBACK_LIST.push(realOnRejected)
            }
        }
    })
    return promise2
}

6、现在有一个问题,如果我们将成功和失败的回调函数存储在数组中,我们什么时候来调用这个回调呢?肯定是要等到Promise的状态改变了才能调用这个储存在数组中的回调函数,这时候我们就需要监听Promise状态的编号,我们在此可以用到ES6getter和setter,注意getter和setter必须同时存在。(可以在ES6语法的数组和对象的扩展中查看)

//此处使用另一个变量来存储状态是为了防止套娃,每当我们this.status的时候都会调用get status(),假设get status方法里面return this.status的话,就会陷入无限的死循环
_status = PENDING;

get status() {
    return this._status;
}

set status(newStatus) {
    this._status = newStatus;
    switch (newStatus) {
        //成功取出成功回调函数执行,this.value是执行了resolve之后的value
        case FULFILLED: {
            this.FULFILLED_CALLBACK_LIST.forEach(callback => {
                callback(this.value);
            });
            break;
        }
        //失败取出失败回调函数执行,this.reason是执行了reject之后的reason
        case REJECTED: {
            this.REJECTED_CALLBACK_LIST.forEach(callback => {
                callback(this.reason);
            });
            break;
        }
    }
}

7、前面说到每个then都会返回一个promise实例,使得我们可以进行如下链式使用then,但现在是有一个问题。第一个then中成功回调函数会执行,这是因为我们最开始创建Promise执行了resolve,但是第二个then的成功和失败回调是不会执行的,因为并没有执行对应的resolve和reject。

这里我们要搞清楚,then返回的Promise实例then成功回调函数返回的Promise实例并不是同一个对象,在then的成功回调函数调用resolve或者reject,我们给这个方法取名resolvePromise

8、需要对then进行修改一下,then的修改其实只有两处,就是switch中成功和失败状态那里,成功和失败的返回值用一个变量x接收,然后然后调用resolvePromise方法,该方法传入四个参数,分别是then将要返回的promise实例、x、promise实例的resolve和reject。

//储存成功和失败回调函数的数组
FULFILLED_CALLBACK_LIST = [];
REJECTED_CALLBACK_LIST = [];

then(onFulfilled, onRejected) {
    //当传入的onFulfilled和onRejected不是函数的时候,我们需要将其转换成函数
    const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : value => {
        return value;
    }
    const realOnRejected = this.isFunction(onRejected) ? onRejected : reason => {
        return reason;
    }
    //then方法默认会返回一个Promise实例,使得我们在能够一直执行链式调用
    const promise2 = new MPromise((resolve, reject) => {
        switch (this.status) {
            //当状态变为fulfilled表明已经执行resolve,this.value就是resolve(value)中传入的值
            case FULFILLED: {
                const x = realOnFulfilled(this.value);
                this.resolvePromise(promise2, x, resolve, reject);
                break;
            }
            //当状态变为rejected表明已经执行reject,this.reason就是reject(reason)中传入的值
            case REJECTED: {
                const x = realOnRejected(this.reason);
                this.resolvePromise(promise2, x, resolve, reject);
                break;
            }
            //当状态变为pending还未执行resolve或reject,此时暂时用数组将成功和失败的回调方法存储起来,所以需要在前面分别定义一个存储成功和失败的数组
            case PENDING: {
                this.FULFILLED_CALLBACK_LIST.push(realOnFulfilled)
                this.REJECTED_CALLBACK_LIST.push(realOnRejected)
            }
        }
    })
    return promise2
}

9、resolvePromise的实现,对于成功/失败回调函数的返回值x可能有很多种情况,可能返回一个Promise,也可能返回一个对象、一个函数、设置不返回任何值,在resolvePromise中我们需要考虑到这些情况并针对不同情况作出处理

resolvePromise(promise2, x, resolve, reject) {
    //如果newPromise和x指向同一对象,以TypeError为因,拒绝执行newPromise
    //这是为了防止死循环
    if(promise2 === x) {
        return reject(
            new TypeError("The promise and the return value are the same")
        );
    }

    if(x instanceof MPromise) {
        //如果x为promise,则使newPromise接收x的状态
        //也就是继续执行x,如果执行的时候拿到一个y,还要继续解析y
        //之前规范中提到成功和失败的回调函数应该是一个微任务,所以这里使用queueMicrotask进行包裹,先前的then函数中的成功和失败回调函数后续也会进行修改
        queueMicrotask(() => {
            x.then(y => {
                this.resolvePromise(promise2, y, resolve, reject);
            }, reject)
        });
    } else if (typeof x === "object" || this.isFunction(x)) {
        //如果x为对象或者函数,可能对象是第三方的promise
        if(x === null) {
            //null也会被判断为对象
            return resolve(x);
        }

        let then = null;

        try {
            //把x.then赋值给then
            then = x.then;
        }catch (error) {
            //如果取x.then的值时抛出错误e,则以e为因,拒绝promise
            return reject(error);
        }

        //如果then是函数
        if(this.isFunction(then)) {
            let called = false;
            //将x作为函数的作用域this调用
            //传递两个回调函数作为参数,第一个参数叫做resolvePromise,第二个参数叫做rejectPromise
            try {
                then.call(
                    x,
                    //如果resolvePromise以值y为参数被调用,则运行resolvePromise //此处y是上一个resolve传过来的值
                    y => {
                        //需要有一个变量called来保证只调用一次。 //为了防止第三方Promise同时调用resolve和reject
                        if(called) return;
                        called = true;
                        reject(r);
                    }
                );
            } catch (error) {
                //如果调用then方法抛出了异常e
                if(called) return;

                //否则以e为因拒绝promise
                reject(error);
            }
        } else {
            // 如果then不是函数,以x为参数执行promise
            resolve(x);
        }
    } else {
        //如果x不为对象或者函数,以x为参数执行promise
        resolve(x);
    }
}

10、promise的使用中,我们除了在then中进行失败的处理之外,promise还可以通过catch方法对失败进行处理,其原因还是在调用then。Promise除了在创建Promise实例的回调函数中使用resolve()/reject()之外,还可以直接通过对象直接使用,如Promise.resolve()/Promise.reject(),我们接下来实现一下这几个功能。

catch(onRejected) {
    return this.then(null, onRejected);
}

static resolve(value) {
    if(value instanceof MPromise) {
        return value;
    }
    
    return new MPromise(resolve => {
        resolve(value)
    });
}

static reject(reason) {
    return new MPromise((resolve, reject) => {
        reject(reason)
    });
}

Promise竞速的写法:

static race(promiseList) {
    return new MPromise((resolve, reject) => {
        const length = promiseList.length;
        
        if(length === 0) {
            return resolve();
        } else {
            for(let i = 0; i < length; i++) {
                MPromise.resolve(promiseList[i]).then(
                    value => {
                        return resolve(value);
                    },
                    reason => {
                        return reject(reason);
                    }
                );
            }
        }
    });
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2280198.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C#树图显示目录下所有文件以及文件大小

C#树图显示目录下所有文件以及文件大小 我们在打开某个目录属性时,可以查看到有大小信息.如下图 而一个目录(文件夹)System.IO.Directory是没有FileSize或者Length属性的. 目录(文件夹)的大小是指该目录下所有子目录和所有文件大小的累加,按字节为单位. 新建窗体应用程序Get…

力扣707题——设计链表

#题目 从零开始设计链表&#xff0c;我们拆分成两次任务&#xff0c;今天先看1 ,2 ,4 #代码

机器学习09-Pytorch功能拆解

机器学习09-Pytorch功能拆解 我个人是Java程序员&#xff0c;关于Python代码的使用过程中的相关代码事项&#xff0c;在此进行记录 文章目录 机器学习09-Pytorch功能拆解1-核心逻辑脉络2-个人备注3-Pytorch软件包拆解1-Python有参和无参构造构造方法的基本语法示例解释注意事项…

天津市开通首个万兆宽带:1秒钟下载1GB文件

快科技1月21日消息&#xff0c;华为光网宣布&#xff0c;天津联通携手华为&#xff0c;日前成功为天津市北辰区柴楼金园小区部署了天津市首个万兆宽带网络。 在现场测速环节中&#xff0c;该万兆宽带网络展现出了惊人的速度——高达9429Mbps的下载速率&#xff0c;几乎跑满带宽…

html全局遮罩,通过websocket来实现实时发布公告

1.index.html代码示例 <div id"websocket" style"display:none;position: absolute;color:red;background-color: black;width: 100%;height: 100%;z-index: 100; opacity: 0.9; padding-top: 30%;padding-left: 30%; padding-border:1px; "onclick&q…

数据结构(四) B树/跳表

目录 1. LRU 2. B树 3. 跳表 1. LRU: 1.1 概念: 最近最少使用算法, 就是cache缓存的算法. 因为cache(位于内存和cpu之间的存储设备)是一种容量有限的缓存, 有新的数据进入就需要将原本的数据进行排出. 1.2 LRU cache实现: #include <iostream> #include <list>…

Spring Boot 整合 ShedLock 处理定时任务重复执行的问题

&#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Micro麦可乐的博客 &#x1f425;《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程&#xff0c;入门到实战 &#x1f33a;《RabbitMQ》…

CSDN年度回顾:技术征途上的坚实步伐

嘿&#xff0c;时光过得可真快呀&#xff0c;就像那匹跑得飞快的白马&#xff0c;嗖的一下&#xff0c;2024 年的日历就这么悄无声息地翻到了最后一页。这会儿我回头看看在 CSDN 上度过的这一年&#xff0c;心里那叫一个感慨万千&#xff0c;满满的都是喜悦&#xff0c;就像心里…

解决 PostgreSQL 中创建 TimescaleDB 扩展的字符串错误

解决 PostgreSQL 中创建 TimescaleDB 扩展的字符串错误 在使用 PostgreSQL 数据库并尝试创建 TimescaleDB 扩展时&#xff0c;你可能会遇到一些棘手的错误。今天&#xff0c;我们就来探讨一个常见的错误信息及相应的解决方法&#xff1a; CREATE EXTENSION IF NOT EXISTS tim…

【语言处理和机器学习】概述篇(基础小白入门篇)

前言 自学笔记&#xff0c;分享给语言学/语言教育学方向的&#xff0c;但对语言数据处理感兴趣但是尚未入门&#xff0c;却需要在论文中用到的小伙伴&#xff0c;欢迎大佬们补充或绕道。ps&#xff1a;本文不涉及公式讲解&#xff08;文科生小白友好体质&#xff09;&#xff…

【ESP32】ESP32连接JY61P并通过WIFI发送给电脑

前言 手头上有个ESP32&#xff0c;发现有wifi功能&#xff0c;希望连接JY61P并通过WIFI把姿态数据发送给电脑 1.采用Arduino IDE编译器&#xff1b;需要安装ESP32的开发板管理器&#xff1b; 2.电脑接受数据是基于python的&#xff1b; 1. ESP32 连接手机WIFI #include <…

将 Docker 安装到 Windows 的 D 盘的方法

1.官网下载Docker Desktop Installer.exe&#xff0c;官网网址&#xff1a;Get Started | Docker 2.以管理员身份打开 Windows 终端 3.切换到 你Docker Desktop Installer.exe下载到的地址 4.在运行代码前&#xff0c;需要提前手动创建好D:\Program Files\Docker和D:\Program F…

DeepSeek-R1-GRPO理解

一、GRPO GRPO&#xff08;Group Relative Policy Optimization&#xff09;是一种强化学习&#xff08;Reinforcement Learning, RL&#xff09;算法&#xff0c;专门用于训练大型语言模型&#xff08;LLMs&#xff09;在复杂任务&#xff08;如数学推理、代码生成等&#xf…

Unreal Engine 5 C++ Advanced Action RPG 十章笔记

第十章 Survival Game Mode 2-Game Mode Test Map 设置游戏规则进行游戏玩法 生成敌人玩家是否死亡敌人死亡是否需要刷出更多 肯定:难度增加否定:玩家胜利 流程 新的游戏模式类游戏状态新的数据表来指定总共有多少波敌人生成逻辑UI告诉当前玩家的敌人波数 3-Survival Game M…

接口(1)

大家好&#xff0c;今天我们来看看接口的概念&#xff0c;接口跟类的使用是有一些区别的&#xff0c;接口中都是抽象方法&#xff0c;简单介绍一下后&#xff0c;我们正式来学习。 2、接口 接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用. 在java中,…

docker离线安装及部署各类中间件(x86系统架构)

前言&#xff1a;此文主要针对需要在x86内网服务器搭建系统的情况 一、docker离线安装 1、下载docker镜像 https://download.docker.com/linux/static/stable/x86_64/ 版本&#xff1a;docker-23.0.6.tgz 2、将docker-23.0.6.tgz 文件上传到服务器上面&#xff0c;这里放在…

debian中apt的配置与解析

引言 在系统使用过程中&#xff0c;我们可能会遭遇 apt update 操作出现问题&#xff0c;或者 apt upgrade 速度迟缓的情况。这往往是由于所使用软件源本身存在诸如服务器性能不佳、维护不及时等质量问题&#xff0c;同时&#xff0c;软件源服务器与我们所处地理位置的距离较远…

python创建一个httpServer网页上传文件到httpServer

一、代码 1.server.py import os from http.server import SimpleHTTPRequestHandler, HTTPServer import cgi # 自定义请求处理类 class MyRequestHandler(SimpleHTTPRequestHandler):# 处理GET请求def do_GET(self):if self.path /:# 响应200状态码self.send_response(2…

Git处理冲突详解

文章目录 Git处理冲突详解一、引言二、冲突产生的原因三、解决冲突的步骤1. 手动解决冲突1.1 查看冲突文件1.2 编辑冲突文件1.3 提交解决冲突 2. 使用合并工具解决冲突 四、使用示例五、总结 Git处理冲突详解 一、引言 在团队协作开发中&#xff0c;Git冲突是不可避免的。当多…

如何降低振荡器的相位噪音

相位噪音&#xff08;Phase Noise&#xff09;是振荡器设计中一个重要的性能指标&#xff0c;尤其在高精度和高频应用中。相位噪音不仅影响信号的质量&#xff0c;还可能对后续系统的性能造成显著影响。因此&#xff0c;如何有效降低振荡器的相位噪音成为了科研与工业界关注的热…