【10】ES6:Promise 对象
一、同步和异步
1、JS 是单线程语言
JavaScript 是一门单线程的语言
,因此同一个时间只能做一件事情,这意味着所有任务都需要排队,前一个任务执行完,才会执行下一个任务。但是,如果前一个任务的执行时间很长,那么后一个任务就需要一直等待,这样就会造成页面的渲染不连贯,严重影响用户体验。
因此,JavaScript 在设计的时候,就已经考虑到这个问题,主线程无需等待这些耗时任务执行完成(此时这些耗时任务正在执行中),先运行排在后面的任务,等到这些耗时任务有了结果后,再回过头执行他们,因此,所有任务可以分成两种,一种是同步任务,另一种是异步任务。(总结:因为是单线程,所以必须异步。)
2、同步任务和异步任务
同步任务
指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。
异步任务
指的是,不进入主线程,而进入”任务队列”(task queue)的任务,只有”任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
他们的区别就是在这条主线程上执行顺序的不同。全部同步任务执行完成后,才会去执行异步任务,遇到同步任务,如果他不执行,程序会卡在那,后面的任务无法执行,遇到异步任务,程序不会卡住,而是继续执行后面的任务。
总结:如果同步任务没有执行完,异步任务是不会执行的。
3、事件循环机制
JS 是单线程运行的,异步是基于回调函数实现的,事件循环机制(Event Loop)就是异步回调实现的原理。
JavaScript 运行机制,别名事件循环(EventLoop)
- 所有同步任务都在主线程上执行,行成一个执行栈。
- 主线程之外,还存在任务队列,只要异步任务有了结果,就会在任务队列中放置对应的任务(回调函数),等待执行。
- 一旦执行栈中的所有同步任务执行完毕,JS 引擎会判断任务队列中是否有任务可以执行,如果有,则该任务结束等待状态,进入执行栈,开始执行。
- 主线程不断的重复上面的第三步。
总结:只要主线程空了,就会去读取”任务队列”,这个过程是循环不断的,这就是 JavaScript 的异步执行机制,也叫事件循环。
注意点:
异步任务和异步任务的回调函数不一样,任务队列存放的是异步任务的回调函数。
放入任务队列的时机并不是一遇到异步任务就放入,若遇到异步任务,则将其放置到宿主环境中。如果在宿主环境中的异步任务达到了执行的条件,则将所要执行的回调函数放入任务队列中,比如说遇到了一个1s的定时器setTimeout,那就是1s后才将回调函数放到任务队列中。
栈是一种先出后出的数据结构,压在栈底的最慢被执行,队列是一种先进先出的数据结构,先去到队列的任务,优先被执行。
4、微任务和宏任务
异步任务分为 微任务 和 宏任务,也就是任务队列分为 微任务队列 和 宏任务队列。
微任务的触发时机是早于宏任务的,因为微任务是在 DOM 渲染前执行,宏任务是在 DOM 渲染后执行。
微任务: Promise,async,await,nextTick 等等
宏任务: setTimeout,setInterval,ajax,DOM事件 等等
二、Promise 基本了解
1、Promise 的含义
Promise 是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了 Promise 对象。
所谓 Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
Promise 对象的特点:
(1)对象的状态不受外界影响
。
Promise 对象代表一个异步操作,有三种状态:等待状态(pending)、成功状态(resolved)、失败状态(rejected)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果
。
Promise 对象的状态改变,只有两种可能:从 pending 变为 fulfilled 和从 pending 变为 rejected 。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
Promise 对象的优点:
(1)实现了将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。(回调地狱)
(2)提供统一的接口,使得控制异步操作更加容易。
Promise 对象的缺点:
(1)无法取消 Promise,一旦新建它就会立即执行,无法中途取消。
(2)如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
(3)当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
2、基本用法
ES6 规定,Promise 对象是一个构造函数,用来生成 Promise 实例。
Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 和 reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
参数 | 说明 |
---|---|
resolve 函数 | 将 Promise对象的状态从等待状态 -> 成功状态; 在异步操作成功时调用,并将异步操作的结果,作为参数传递出去; 会触发后续的 then 回调函数。 |
reject 函数 | 将 Promise对象的状态从等待状态 -> 失败状态; 在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去; 会触发 catch 回调函数。 |
// 这里的 resolve reject 只是一个形参,可以取任意名字,但是我们约定直接使用 resolve reject 。
const promise = new Promise(function(resolve, reject) {// ... some codeif (/* 异步操作成功 */){resolve(value)} else {reject(error)}
})
Promise 新建后就会立即执行。
let promise = new Promise(function(resolve, reject) {console.log('Promise')resolve()
})promise.then(function() {console.log('resolved.')
})console.log('Hi!')// Promise
// Hi!
// resolved
上面代码中,Promise 新建后立即执行,所以首先输出的是 Promise。然后,then 方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以 resolved 最后输出。
调用 resolve 或 reject 并不会终结 Promise 的参数函数的执行。
new Promise((resolve, reject) => {resolve(1)console.log(2)
}).then(r => {console.log(r)
})
// 2
// 1
上面代码中,调用 resolve(1) 以后,后面的 console.log(2) 还是会执行,并且会首先打印出来。这是因为立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。
一般来说,调用 resolve 或 reject 以后,Promise 的使命就完成了,后继操作应该放到 then 方法里面,而不应该直接写在 resolve 或 reject 的后面。所以,最好在它们前面加上 return 语句,这样就不会有意外。
new Promise((resolve, reject) => {return resolve(1)// 后面的语句不会执行console.log(2)
})
三、Promise.prototype.then()
Promise 实例具有 then 方法,也就是说,then 方法是定义在原型对象 Promise.prototype 上的。它的作用是为 Promise 实例添加状态改变时的回调函数。
then 方法的第一个参数是 resolved 状态的回调函数,第二个参数是 rejected 状态的回调函数,它们都是可选的。
1、then 方法的两个回调函数什么时候执行。
pending -> resolved 时,执行 then 的第一个回调函数;
pending -> rejected 时,执行 then 的第二个回调函数。
2、then 方法执行后的返回值。
then 方法执行后默认自动返回一个新的 Promise 对象。
3、then 方法返回的 Promise 对象的状态改变。
then 方法其实默认返回的是 undefined,即:return undefined,但是 ES6 的机制规定:当 then 返回 undefined 时,那么会将这个 undefined 包装成一个 Promise,并且这个 Promise 默认调用了 resolve() 方法(成功态),并且把 undefined 作为了 resolve() 的参数,相当于:
const p = new Promise((resolve, reject) => {resolve()
})
p.then(() => {// 默认会执行这一条// return undefined
}, () => {
})// 实际上,return 会包装为一个 Promise 对象,同时默认执行 resolve(),并把 return 的值作为 resolve() 的参数
/*
return new Promise(resolve => {resolve(undefined);
})
*/// -----------------------------
// 如果我们在这个返回的 Promise 上继续调用 then 方法,并接收参数的话,可以发现 then 中成功接收到了被 Promise 包装后的参数
const p2 = new Promise((resolve, reject) => {resolve()
})
p2.then(() => {// 默认会执行这一条// return undefined
}).then(data => {console.log(data) // 打印 undefinedreturn 24 // 手动 return 一个值// 相当于:return new Promise(resolve => {resolve(24)})
}).then((data) => {console.log(data) // 打印 24
})
如果我们要让 then 返回一个失败状态的 Promise,那么我们可以手动 return 一个 Promise 并执行 reject() 方法。
const p3 = new Promise((resolve, reject) => {resolve()
})
p3.then(() => {// 手动返回一个调用了 reject 的 Promisereturn new Promise((resolve, reject) => {reject('失败')})
}).then(() => {}, errData => {console.log(errData) // 失败
})
总结:
- Promise 是一个构造函数,需要 new 才能使用。在 new Promise() 的时候需要传递一个匿名回调函数作为 Promise() 唯一的参数,这个回调函数有两个参数 resolve reject,这两个参数也是函数,当回调函数执行第一个 resolve 函数后 Promise 便变为了成功状态,反之回调函数执行了 reject 后 Promise 便变为了失败状态,且每个 Promise 只能要么执行 resolve,要么执行 reject,不能同时执行!
- 当 Promise 被 new 之后就会有一个 then 方法,该方法默认接收两个匿名回调函数作为参数,其中第一个回调函数是在 Promise 为成功状态时自动调用的,反之第二个回调函数是在 Promise 为失败状态时自动调用的,并且这两个回调函数是可以接收参数的,参数就来自于 resolve 或 reject 调用时传递的实参!
- 在 then 方法执行后会默认返回 undefined(在没有指定返回值的情况下),ES6 会将其包装为一个新的成功态的 Promise,该 Promise 会自动执行 resolve 函数,该函数的参数来自于 then 方法的返回值(如果没有返回值那么默认就返回 undefined)。如果需要返回一个失败态的 Promise,那么需要在 then 中手动指定返回值:return new Promise((resolve, reject) => { reject(参数) }
四、Promise.prototype.catch()
Promise.prototype.catch() 方法是 .then(null, rejection) 或 .then(undefined, rejection) 的别名,用于指定发生错误时的回调函数。
new Promise((resolve, reject) => {reject('失败')
}).then(res => {console.log(res)
}).catch(err => {console.log(err) // 失败
})// -------------------------------------
// 上面的代码本质上等同于
new Promise((resolve, reject) => {reject('失败')
}).then(res => {console.log(res)
}).then(null, err => {console.log(err) // 失败
})
在 Promise 中,一但出现了错误状态,那么这个错误是不会消失的,会一直向下传递,直到遇到可以处理错误的函数。
由于 catch 是 then 的特例,所以 catch 依旧返回的是一个 Promise 对象,我们可以在 catch 后继续调用 then。
new Promise((resolve, reject) => {reject('失败')
}).then(res => {console.log(res)
}).catch(err => {console.log(err) // 失败return '测试'
}).then(res => {console.log(res) // 测试
})
一般总是建议,Promise 对象后面要跟一个或多个 catch 方法,这样可以处理 Promise 内部发生的错误!
五、Promise.prototype.finally()
Promise.prototype.finally() 方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
finally 方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是 fulfilled 还是 rejected 。这表明,finally 方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。
finally 本质上是 then 方法的特例。
// 不管 promise 最后的状态,在执行完 then 或 catch 指定的回调函数以后,都会执行 finally 方法指定的回调函数。
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···})
promise
.finally(() => {// 语句
})// 等同于
promise
.then(result => {// 语句return result},error => {// 语句throw error}
)
finally:主要是用来处理一些必做操作,比如在操作数据库之后(无论成功与否)都要关闭数据库连接。
server.listen(port).then(function () {// ...}).finally(server.stop)
六、Promise.all()
Promise.all() 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
Promise.all() 方法接受一个数组作为参数,p1、p2、p3 都是 Promise 实例,如果不是,就会先调用 Promise.resolve 方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all() 方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。
const p = Promise.all([p1, p2, p3])
p 的状态由 p1、p2、p3 决定,分成两种情况。
(1)只有 p1、p2、p3 的状态都变成 fulfilled,p 的状态才会变成 fulfilled,此时 p1、p2、p3 的返回值组成一个数组,传递给 p 的回调函数。
(2)只要 p1、p2、p3 之中有一个被 rejected,p 的状态就变成 rejected,此时第一个被 reject 的实例的返回值,会传递给 p 的回调函数。
const p1 = new Promise((resolve,reject)=>{resolve('请求成功')
})
const p2 = new Promise((resolve,reject)=>{resolve('上传成功')
})
const p3 = Promise.reject('error')Promise.all([p1, p2]).then(data => {console.log(data) // data为一个数组 ['请求成功', '上传成功']
}).catch(err => {console.log(err)
})Promise.all([p1, p2, p3]).then(data => {console.log(data)
}).catch(err => {console.log(err) // 失败 打印结果为 'error'
})
七、Promise.race()
Promise.race() 方法将多个 Promise 实例,包装成一个新的 Promise 实例。
Promise.race() 方法的参数与 Promise.all() 方法一样,如果不是 Promise 实例,就会先调用 Promise.resolve() 方法,将参数转为 Promise 实例,再进一步处理。
const p = Promise.race([p1, p2, p3])
p 的状态由 p1、p2、p3 决定。
p1、p2、p3之中有一个实例率先改变状态,p 的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给 p 的回调函数。
const p = Promise.race([fetch('/resource-that-may-take-a-while'),new Promise(function (resolve, reject) {setTimeout(() => reject(new Error('request timeout')), 5000)})
])p
.then(console.log)
.catch(console.error)
上面代码中,如果 5 秒之内 fetch 方法无法返回结果,变量 p 的状态就会变为 rejected,从而触发 catch 方法指定的回调函数。
八、Promise.allSettled()
Promise.allSettled() 方法,用来确定一组异步操作是否都结束了(不管成功或失败)。
Promise.allSettled() 方法接受一个数组作为参数,数组的每个成员都是一个 Promise 对象,并返回一个新的 Promise 对象。只有等到参数数组的所有 Promise 对象都发生状态变更(不管是 fulfilled 还是 rejected),返回的 Promise 对象才会发生状态变更。
const promises = [fetch('/api-1'),fetch('/api-2'),fetch('/api-3')
]await Promise.allSettled(promises)
removeLoadingIndicator()
上面示例中,数组 promises 包含了三个请求,只有等到这三个请求都结束了(不管请求成功还是失败),removeLoadingIndicator() 才会执行。
该方法返回的新的 Promise 实例,一旦发生状态变更,状态总是 fulfilled,不会变成 rejected。 状态变成 fulfilled 后,它的回调函数会接收到一个数组作为参数,该数组的每个成员对应前面数组的每个 Promise 对象。
const resolved = Promise.resolve(42)
const rejected = Promise.reject(-1)const allSettledPromise = Promise.allSettled([resolved, rejected])allSettledPromise.then(function (results) {console.log(results)
})
// [
// { status: 'fulfilled', value: 42 },
// { status: 'rejected', reason: -1 }
// ]
上面代码中,Promise.allSettled() 的返回值 allSettledPromise,状态只可能变成 fulfilled。它的回调函数接收到的参数是数组 results。该数组的每个成员都是一个对象,对应传入 Promise.allSettled() 的数组里面的两个 Promise 对象。
results 的每个成员是一个对象,对象的格式是固定的,对应异步操作的结果。
// 异步操作成功时
{ status: 'fulfilled', value: value }// 异步操作失败时
{ status: 'rejected', reason: reason }
成员对象的 status 属性的值只可能是字符串 fulfilled 或字符串 rejected,用来区分异步操作是成功还是失败。如果是成功(fulfilled),对象会有 value 属性,如果是失败(rejected),会有 reason 属性,对应两种状态时前面异步操作的返回值。
下面是返回值的用法例子。
const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ]
const results = await Promise.allSettled(promises)// 过滤出成功的请求
const successfulPromises = results.filter(p => p.status === 'fulfilled')// 过滤出失败的请求,并输出原因
const errors = results.filter(p => p.status === 'rejected').map(p => p.reason)
九、Promise.any()
ES2021 引入了 Promise.any() 方法。该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。
只要参数实例有一个变成 fulfilled 状态,包装实例就会变成 fulfilled 状态;如果所有参数实例都变成 rejected 状态,包装实例就会变成 rejected 状态。
Promise.any([fetch('https://v8.dev/').then(() => 'home'),fetch('https://v8.dev/blog').then(() => 'blog'),fetch('https://v8.dev/docs').then(() => 'docs')
]).then((first) => { // 只要有一个 fetch() 请求成功console.log(first)
}).catch((error) => { // 所有三个 fetch() 全部请求失败console.log(error)
})
Promise.any() 跟 Promise.race() 方法很像,只有一点不同,就是 Promise.any() 不会因为某个 Promise 变成rejected 状态而结束,必须等到所有参数 Promise 变成 rejected 状态才会结束。
下面是 Promise() 与 await 命令结合使用的例子。
const promises = [fetch('/endpoint-a').then(() => 'a'),fetch('/endpoint-b').then(() => 'b'),fetch('/endpoint-c').then(() => 'c'),
]try {const success = await Promise.any(promises)console.log(success)
} catch (error) {console.log(error)
}
上面代码中,Promise.any() 方法的参数数组包含三个 Promise 操作。其中只要有一个变成 fulfilled,Promise.any() 返回的 Promise 对象就变成 fulfilled。如果所有三个操作都变成 rejected,那么 await 命令就会抛出错误。
Promise.any() 抛出的错误是一个 AggregateError 实例,这个 AggregateError 实例对象的 errors 属性是一个数组,包含了所有成员的错误。
下面是一个例子。
var resolved = Promise.resolve(42)
var rejected = Promise.reject(-1)
var alsoRejected = Promise.reject(Infinity)Promise.any([resolved, rejected, alsoRejected]).then(function (result) {console.log(result); // 42
})Promise.any([rejected, alsoRejected]).catch(function (results) {console.log(results instanceof AggregateError) // trueconsole.log(results.errors) // [-1, Infinity]
})
十、Promise.resolve()
Promise.resolve() 方法,用于将现有对象转为 Promise 对象。
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
Promise.resolve() 方法的参数:
(1)参数为一般参数
如果参数是一个原始值,或者是一个不具有 then() 方法的对象,则 Promise.resolve() 方法返回一个新的 Promise 对象,状态为 resolved。
const p = Promise.resolve('Hello')p.then(function (s) {console.log(s)
})
// Hello
(2)参数是一个 Promise 实例
如果参数是 Promise 实例,那么 Promise.resolve 将不做任何修改、原封不动地返回这个实例。
(3)不带有任何参数
Promise.resolve() 方法允许调用时不带参数,直接返回一个 resolved 状态的 Promise 对象。
所以,如果希望得到一个 Promise 对象,比较方便的方法就是直接调用 Promise.resolve() 方法。
const p = Promise.resolve()p.then(function () {// ...
})
(4)参数是一个 thenable 对象(了解即可)
当接收一个含 then 方法的对象时,Promise.resolve() 会直接调用 then 方法。
thenable 对象指的是具有 then 方法的对象。
const thenable = {then() {console.log('then')}
}
Promise.resolve(thenable).then(res => console.log('res ' + res),err => console.log('err ' + err)
)console.log(Promise.resolve(thenable))
// Promise { <pending> }
// then// 为什么不会执行 then 中的两个回调函数呢?
// 可见,当接收一个含 then 方法的对象时,默认返回一个 Promise 并且是等待状态的,没有状态的变化,那么就不可能会执行 then 的回调函数// 如果我们要改变这个返回的 Promise 对象的状态,并让 then 的回调对应处理的话,ES6 规定了以下写法:
const thenable02 = {then(resolve, reject) {console.log('then')resolve('then')}
}
Promise.resolve(thenable02).then(res => console.log('res ' + res),err => console.log('err ' + err)
)
// then
// res then
十一、Promise.reject()
Promise.reject(reason) 方法也会返回一个新的 Promise 实例,该实例的状态为 rejected。
const p = Promise.reject('出错了')
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))p.then(null, function (s) {console.log(s)
})
// 出错了
上面代码生成一个 Promise 对象的实例 p,状态为 rejected,回调函数会立即执行。
Promise.reject() 方法的参数,会原封不动地作为 reject 的理由,变成后续方法的参数。
Promise.reject('出错了')
.catch(e => {console.log(e === '出错了')
})
// true
十二、Promise.try()
实际开发中,经常遇到一种情况:不知道或者不想区分,函数 f 是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管 f 是否包含异步操作,都用 then 方法指定下一步流程,用 catch 方法处理 f 抛出的错误。一般就会采用下面的写法。
Promise.resolve().then(f)
上面的写法有一个缺点,就是如果 f 是同步函数,那么它会在本轮事件循环的末尾执行。
// 函数 f 是同步的,但是用 Promise 包装了以后,就变成异步执行了。
const f = () => console.log('now')
Promise.resolve().then(f)
console.log('next')
// next
// now
Promise.try 方法让同步函数同步执行,异步函数异步执行,并且让它们具有统一的 API 。
const f = () => console.log('now')
Promise.try(f)
console.log('next')
// now
// next
database.users.get() 返回一个 Promise 对象,如果抛出异步错误,可以用 catch 方法捕获。但是 database.users.get() 可能还会抛出同步错误(比如数据库连接错误,具体要看实现方法),这时你就不得不用 try…catch 去捕获。
事实上,Promise.try 就是模拟 try 代码块,就像 promise.catch 模拟的是 catch 代码块。
// 统一用 promise.catch() 捕获所有同步和异步的错误。
Promise.try(() => database.users.get({id: userId})).then(...).catch(...)// ----------------------
// 等同于try {database.users.get({id: userId}).then(...).catch(...)
} catch (e) {// ...
}
十三、Promise 的应用
异步加载:也称为图片的预加载。利用 js 代码提前加载图片,用户需要时可以直接从本地缓存获取,但是会增加服务器前端的压力。这样做可以提高用户的体验,因为同步加载大图片的时候,图片会一层一层的显示处理,但是经过预加载后,直接显示出整张图片。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"/><title>Promise 的应用</title><style>#img {width: 24%;padding: 24px;}</style>
</head>
<body>
<!-- 一般加载图片方式 -->
<!-- <img src="https://scpic.chinaz.net/files/pic/pic9/202009/apic27858.jpg" /> -->
<img src="" alt="" id="img"><script>// 异步加载图片函数(参数:图片路径)const loadImgAsync = url => {return new Promise((resolve, reject) => {const img = new Image() // 创建一个图片对象img.onload = () => { // 图片成功加载触发事件resolve(img)}img.onerror = () => { // 图片加载失败触发事件reject(new Error(`Could not load image at ${url}`))}// 这个放在 onload 与 onerror 之后// 一但给 img.src 赋值,那么便立马开始发送请求加载图片(在后台加载,页面上不会显示)// 注意:这里的 src 是 img 对象的属性,与 html 中 img 的 src 无关img.src = url})}const imgDOM = document.getElementById('img')loadImgAsync('https://scpic.chinaz.net/files/pic/pic9/202009/apic27858.jpg').then(img => {// 如果加载成功,那么把后台缓存的图片显示到页面上imgDOM.src = img.src}).catch(err => {console.log(err)})
</script>
</body>
</html>
相关文章:

【10】ES6:Promise 对象
一、同步和异步 1、JS 是单线程语言 JavaScript 是一门单线程的语言,因此同一个时间只能做一件事情,这意味着所有任务都需要排队,前一个任务执行完,才会执行下一个任务。但是,如果前一个任务的执行时间很长ÿ…...

Hive和Spark生产集群搭建(spark on doris)
1.环境准备 1.1 版本选择 序号bigdata-001bigdata-002bigdata-003bigdata-004bigdata-005MySQL-8.0.31mysqlDataxDataxDataxDataxDataxDataxSpark-3.3.1SparkSparkSparkSparkSparkHive-3.1.3HiveHive 1.2 主要组件官网 hive官网: https://hive.apache.org/ hive…...

VuePress、VuePress-theme-hope 搭建个人博客 1【快速上手】 —— 防止踩坑篇
vuePress官网地址 👉 首页 | VuePress 手动安装 这一章节会帮助你从头搭建一个简单的 VuePress 文档网站。如果你想在一个现有项目中使用 VuePress 管理文档,从步骤 3 开始。 步骤 1: 创建并进入一个新目录 mkdir vuepress-starter cd vuepress-star…...

【PostgreSQL】从零开始:(三十一)数据类型-复合类型
复合类型 复合类型是一种由其他类型组成的类型。它可以是数组、结构体、联合体或指向这些类型的指针。复合类型允许将多个值组合成单个实体,以便更方便地处理和使用。复合类型在C语言中非常常见,用于表示复杂的数据结构和组织数据的方式。 数组是一种由…...

基于鸿蒙OS开发一个前端应用
创建JS工程:做鸿蒙应用开发到底学习些啥? 若首次打开DevEco Studio,请点击Create Project创建工程。如果已经打开了一个工程,请在菜单栏选择File > New > Create Project来创建一个新工程。选择HarmonyOS模板库,…...

PIC单片机项目(7)——基于PIC16F877A的智能灯光设计
1.功能设计 使用PIC16F877A单片机,检测环境关照,当光照比阈值低的时候,开灯。光照阈值可以通过按键进行设置,同时阈值可以保存在EEPROM中,断电不丢失。使用LCD1602进行显示,第一行显示测到的实时光照强度&a…...

Mysql For Navicate (老韩)
Navicate创建数据库 先创建一个数据库;然后在数据库中创建一张表;在表格当中填入相应的属性字段;打开表, 然后填入相应的实例字段; – 使用数据库图形化App和使用指令来进行操作各有各的好处和利弊; 数据库的三层结构(破除MySQL神秘) 所谓安装Mysql数据库, 就是在主机安装一…...

设计模式之-建造者模式通俗易懂理解,以及建造者模式的使用场景和示列代码
系列文章目录 设计模式之-6大设计原则简单易懂的理解以及它们的适用场景和代码示列 设计模式之-单列设计模式,5种单例设计模式使用场景以及它们的优缺点 设计模式之-3种常见的工厂模式简单工厂模式、工厂方法模式和抽象工厂模式,每一种模式的概念、使用…...

Redis分布式锁进阶源码分析
Redis分布式锁进阶源码分析 1、如何写一个商品秒杀代码?2、加上Java锁3、使用redis setnx命令获取锁4、增加try和finally5、给锁设置过期时间6、增长过期时间,并setnx增加唯一value7、使用redisson8、源码分析a、RedissonLock.tryLockInnerAsyncb、Redis…...

lag-llama源码解读(Lag-Llama: Towards Foundation Models for Time Series Forecasting)
Lag-Llama: Towards Foundation Models for Time Series Forecasting 文章内容: 时间序列预测任务,单变量预测单变量,基于Llama大模型,在zero-shot场景下模型表现优异。创新点,引入滞后特征作为协变量来进行预测。 获得…...

Three.js基础入门介绍——Three.js学习三【借助控制器操作相机】
在Three.js基础入门介绍——Three.js学习二【极简入门】中介绍了如何搭建Three.js开发环境并实现一个包含旋转立方体的场景示例,以此为前提,本篇将引进一个控制器的概念并使用”轨道控制器”(OrbitControls)来达到从不同方向展示场…...

【日志系列】什么是分布式日志系统?
✔️什么是分布式日志系统? 现在,很多应用都是集群部署的,一次请求会因为负载均衡而被路由到不同的服务器上面,这就导致一个应用的日志会分散在不同的服务器上面。 当我们要向通过日志做数据分析,问题排查的时候&#…...

[卷积神经网络]FCOS--仅使用卷积的Anchor Free目标检测
项目源码: FCOShttps://github.com/tianzhi0549/FCOS/ 一、概述 作为一种Anchor Free的目标检测网络,FCOS并不依赖锚框,这点类似于YOLOx和CenterNet,但CenterNet的思路是寻找目标的中心点,而FCOS则是寻找每个像素点&…...

Ubuntu fcitx Install
ubuntu经常出现键盘失灵的问题 查询资料得知应该是Ibus框架的问题 于是需要安装fcitx框架和搜狗拼音 sudo apt update sudo apt install fcitx 设置fcitx开机自启动(建议) sudo cp /usr/share/applications/fcitx.desktop /etc/xdg/autostart/ 然后…...

【Makefile/GNU Make】知识总结
文章目录 1. 总体认识2. 编写Makefile2.1. Makefile的组成2.2. Makefile文件名2.3. 包含其他Makefile 3. 编写规则4. 编写规则中的构建命令5. 如何使用变量6. 条件判断7. 转换文本的函数8. 如何运行make9. 使用模糊规则10. 使用make来更新存档文件11. 扩展GNU make12. 集成GNU …...

腾讯云轻量服务器和云服务器CVM该怎么选?区别一览
腾讯云轻量服务器和云服务器CVM该怎么选?不差钱选云服务器CVM,追求性价比选择轻量应用服务器,轻量真优惠呀,活动 https://curl.qcloud.com/oRMoSucP 轻量应用服务器2核2G3M价格62元一年、2核2G4M价格118元一年,540元三…...

MySQL定时备份实现
一、备份数据库 –all-databases 备份所有数据库 /opt/mysqlcopy/all_$(date “%Y-%m-%d %H:%M:%S”).sql 备份地址 docker exec -it 容器名称 sh -c "mysqldump -u root -ppassword --all-databases > /opt/mysqlcopy/all_$(date "%Y-%m-%d %H:%M:%S").sq…...

Nginx 不同源Https请求Http 报strict-origin-when-cross-origin
原因: nginx代理配置url指向只开放了/* 而我/*/*多了一层路径 成功:...

openGauss学习笔记-175 openGauss 数据库运维-备份与恢复-导入数据-管理并发写入操作示例
文章目录 openGauss学习笔记-175 openGauss 数据库运维-备份与恢复-导入数据-管理并发写入操作示例175.1 相同表的INSERT和DELETE并发175.2 相同表的并发INSERT175.3 相同表的并发UPDATE175.4 数据导入和查询的并发 openGauss学习笔记-175 openGauss 数据库运维-备份与恢复-导入…...

pnpm、npm、yarn是什么?怎么选择?
pnpm、npm、yarn三者是前端常用的包管理器,那么他们有什么区别呢? 1. npm (Node Package Manager) npm是Node.js的默认包管理器。自Node.js发布以来,npm就一直作为它的一个组成部分存在,因此,安装Node.js时也会自动安…...

MySQL8 一键部署
#!/bin/bash ### 定义变量 mysql_download_urlhttps://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.33-linux-glibc2.12-x86_64.tar.xz mysql_package_namemysql-8.0.33-linux-glibc2.12-x86_64.tar.xz mysql_dec_namemysql-8.0.33-linux-glibc2.12-x86_64 mysql_download_…...

12 UVM Driver
目录 12.1 uvm_driver class hierarchy 12.2 How to write driver code? 12.3 UVM Driver example 12.4 How to get sequence items from the sequencer? 12.5 UVM driver methods 12.5.1 Using get_next_item/ try_next_item and item_done methods 12.5.2 Using get…...

“暂存”校验逻辑探讨
1、背景 在业务中可能会遇到这种场景,前端页面元素多且复杂,一次性填完提交耗时很长,中间中断面临着丢失数据的风险。针对这个问题,“暂存”应运而生。 那“暂存”的时候,是否需要对数据校验,如何进行校验…...

探究element-ui 2.15.8中<el-input>的keydown事件无效问题
一、问题描述 今天看到一个问题,在用Vue2element-ui 2.15.8开发时,使用input组件绑定keydown事件没有任何效果。 <template><div id"app"><el-input v-model"content" placeholder"请输入" keydown&quo…...

Unity 代码控制Text自适应文本高度
在使用代码给Text赋值时,且文本有多段,并需要根据实际文本高度适配Text组件的高度时,可以使用以下方法: //Text文本 public TextMeshProUGUI text;void Start() {//代码赋值文本text.text "好!\n很好!\n非常好!";//获…...

TiDB 7.1 多租户在中泰证券中的应用
本文详细介绍了中泰证券在系统国产化改造项目中采用 TiDB 多租户技术的实施过程。文章分析了中泰证券数据库系统现状以及引入 TiDB 资源管控技术的必要性,探讨了 TiDB 多租户的关键特性,并阐述了在实际应用中的具体操作步骤。通过该技术的应用࿰…...

嵌入式-stm32-SR04超声波测距介绍及实战
一:超声波传感器介绍 1.1、SR04超声波测距硬件模块 1.2、SR04的四个IO口 vcc:提供电源5V gnd:接地 Trig:是**发送**声波信号的触发器 Echo:是**接收**回波信号的引脚 当TRIG信号被触发时,传感器会发送一定频率的声波信号,该信号被反射后&am…...

智能优化算法应用:基于白鲸算法3D无线传感器网络(WSN)覆盖优化 - 附代码
智能优化算法应用:基于白鲸算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用:基于白鲸算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.白鲸算法4.实验参数设定5.算法结果6.参考文献7.MA…...

mac m1芯片 pytorch安装及gpu性能测试
pytorch 使用mac的m1芯片进行模型训练。 #小结:在数据量小和模型参数少,batch_size小时,cpu训练更快(原因:每次训练时数据需要放入GPU中,由于batch_size小。数据放入gpu比模型计算时间还长) 在…...

go 使用 - sync.WaitGroup
使用 - sync.WaitGroup 简介使用注意点 简介 waitgroup 是等待一组并发操作完成得方法。Goroutines对Go来说是独一无二的(尽管其他一些语言有类似的并发原语)。它们不是操作系统线程,它们不完全是绿色的线程(由语言运行时管理的线程)&#x…...