【JavaScript】异步函数(async和await详解)

异步函数

ES8 的 async/await 旨在解决利用异步结构组织代码的问题。为此,ECMAScript 对函数进行了扩展,为其增加了两个新关键字:async 和 await。

1. async

async 关键字用于声明异步函数。这个关键字可以用在函数声明、函数表达式、箭头函数和方法上:

async function foo() {} let bar = async function() {}; let baz = async () => {}; class Qux { async qux() {} 
} 

使用 async 关键字可以让函数具有异步特征,但总体上其代码仍然是同步求值的。而在参数或闭包方面,异步函数仍然具有普通 JavaScript 函数的正常行为。正如下面的例子所示,foo()函数仍然会在后面的指令之前被求值:

async function foo() { console.log(1); 
} foo(); 
console.log(2); // 1 
// 2 

不过,异步函数如果使用 return 关键字返回了值(如果没有 return 则会返回 undefined),这个值会被 Promise.resolve()包装成一个期约对象。异步函数始终返回期约对象。在函数外部调用这个函数可以得到它返回的期约:

async function foo() { console.log(1); return 3; 
} // 给返回的期约添加一个解决处理程序 
foo().then(console.log); console.log(2); // 1 
// 2 
// 3 

当然,直接返回一个期约对象也是一样的:

async function foo() { console.log(1); return Promise.resolve(3); 
} // 给返回的期约添加一个解决处理程序 
foo().then(console.log); console.log(2); // 1 
// 2 
// 3 

异步函数的返回值期待(但实际上并不要求)一个实现 thenable 接口的对象,但常规的值也可以。如果返回的是实现 thenable 接口的对象,则这个对象可以由提供给 then()的处理程序“解包”。如果不是,则返回值就被当作已经解决的期约。下面的代码演示了这些情况:

// 返回一个原始值 
async function foo() { return 'foo'; 
} 
foo().then(console.log); 
// foo // 返回一个没有实现 thenable 接口的对象 
async function bar() { return ['bar']; 
} 
bar().then(console.log); 
// ['bar'] // 返回一个实现了 thenable 接口的非期约对象 
async function baz() { const thenable = { then(callback) { callback('baz'); } }; return thenable; 
} 
baz().then(console.log); 
// baz 
// 返回一个期约 
async function qux() { return Promise.resolve('qux'); 
} 
qux().then(console.log); 
// qux 

与在期约处理程序中一样,在异步函数中抛出错误会返回拒绝的期约:

async function foo() { console.log(1); throw 3; 
} // 给返回的期约添加一个拒绝处理程序 
foo().catch(console.log); 
console.log(2); // 1 
// 2 
// 3 

不过,拒绝期约的错误不会被异步函数捕获:

async function foo() { console.log(1); Promise.reject(3); 
} // Attach a rejected handler to the returned promise 
foo().catch(console.log); 
console.log(2); // 1 
// 2 
// Uncaught (in promise): 3 

2. await

因为异步函数主要针对不会马上完成的任务,所以自然需要一种暂停和恢复执行的能力。使用 await关键字可以暂停异步函数代码的执行,等待期约解决。来看下面之前在Promise章节中出现过的例子:

let p = new Promise((resolve, reject) => setTimeout(resolve, 1000, 3)); p.then((x) => console.log(x)); // 3 

使用 async/await 可以写成这样:

async function foo() { let p = new Promise((resolve, reject) => setTimeout(resolve, 1000, 3)); console.log(await p); 
} foo(); 
// 3 

注意,await 关键字会暂停执行异步函数后面的代码,让出 JavaScript 运行时的执行线程。这个行为与生成器函数中的 yield 关键字是一样的。await 关键字同样是尝试“解包”对象的值,然后将这个值传给表达式,再异步恢复异步函数的执行。

await 关键字的用法与 JavaScript 的一元操作一样。它可以单独使用,也可以在表达式中使用,如下面的例子所示:

// 异步打印"foo" 
async function foo() { console.log(await Promise.resolve('foo')); 
} 
foo(); 
// foo // 异步打印"bar" 
async function bar() { return await Promise.resolve('bar'); 
} 
bar().then(console.log); 
// bar // 1000 毫秒后异步打印"baz" 
async function baz() { await new Promise((resolve, reject) => setTimeout(resolve, 1000)); console.log('baz'); 
} 
baz(); 
// baz(1000 毫秒后) 

await 关键字期待(但实际上并不要求)一个实现 thenable 接口的对象,但常规的值也可以。如果是实现 thenable 接口的对象,则这个对象可以由 await 来“解包”。如果不是,则这个值就被当作已经解决的期约。下面的代码演示了这些情况:

// 等待一个原始值 
async function foo() { console.log(await 'foo'); 
} 
foo(); 
// foo // 等待一个没有实现 thenable 接口的对象 
async function bar() { console.log(await ['bar']); 
} 
bar(); 
// ['bar'] // 等待一个实现了 thenable 接口的非期约对象 
async function baz() { const thenable = { then(callback) { callback('baz'); } }; console.log(await thenable); 
} 
baz(); 
// baz // 等待一个期约 
async function qux() { console.log(await Promise.resolve('qux')); 
} 
qux(); 
// qux 

等待会抛出错误的同步操作,会返回拒绝的期约:

async function foo() { console.log(1); await (() => { throw 3; })(); 
} // 给返回的期约添加一个拒绝处理程序 
foo().catch(console.log); 
console.log(2); // 1 
// 2 
// 3 

如前面的例子所示,单独的 Promise.reject()不会被异步函数捕获,而会抛出未捕获错误。不过,对拒绝的期约使用 await 则会释放(unwrap)错误值(将拒绝期约返回):

async function foo() { console.log(1); await Promise.reject(3); console.log(4); // 这行代码不会执行 
} // 给返回的期约添加一个拒绝处理程序 
foo().catch(console.log); 
console.log(2); // 1 
// 2 
// 3 

3. await 的限制

await 关键字必须在异步函数中使用,不能在顶级上下文如<script>标签或模块中使用。不过,定义并立即调用异步函数是没问题的。下面两段代码实际是相同的:

async function foo() { console.log(await Promise.resolve(3)); 
} 
foo(); 
// 3 // 立即调用的异步函数表达式 
(async function() { console.log(await Promise.resolve(3)); 
})(); 
// 3 

此外,异步函数的特质不会扩展到嵌套函数。因此,await 关键字也只能直接出现在异步函数的定义中。在同步函数内部使用 await 会抛出SyntaxError。

下面展示了一些会出错的例子:

// 不允许:await 出现在了箭头函数中 
function foo() { const syncFn = () => { return await Promise.resolve('foo'); }; console.log(syncFn());} // 不允许:await 出现在了同步函数声明中 
function bar() { function syncFn() { return await Promise.resolve('bar'); } console.log(syncFn()); 
} // 不允许:await 出现在了同步函数表达式中 
function baz() { const syncFn = function() { return await Promise.resolve('baz'); }; console.log(syncFn()); 
} // 不允许:IIFE 使用同步函数表达式或箭头函数 
function qux() { (function () { console.log(await Promise.resolve('qux')); })(); (() => console.log(await Promise.resolve('qux')))(); 
} 

停止和恢复执行

使用 await 关键字之后的区别其实比看上去的还要微妙一些。比如,下面的例子中按顺序调用了 3个函数,但它们的输出结果顺序是相反的:

async function foo() { console.log(await Promise.resolve('foo')); 
} async function bar() { console.log(await 'bar'); 
} async function baz() { console.log('baz'); 
} foo(); 
bar(); 
baz(); // baz 
// bar 
// foo 

async/await 中真正起作用的是 await。async 关键字,无论从哪方面来看,都不过是一个标识符。

毕竟,异步函数如果不包含 await 关键字,其执行基本上跟普通函数没有什么区别:

async function foo() { console.log(2); 
} console.log(1); 
foo(); 
console.log(3); 
// 1 
// 2 
// 3 

要完全理解 await 关键字,必须知道它并非只是等待一个值可用那么简单。JavaScript 运行时在碰到 await 关键字时,会记录在哪里暂停执行。等到 await 右边的值可用了,JavaScript 运行时会向消息队列中推送一个任务,这个任务会恢复异步函数的执行。

因此,即使 await 后面跟着一个立即可用的值,函数的其余部分也会被异步求值。下面的例子演示了这一点:

async function foo() { console.log(2); await null; console.log(4); 
} console.log(1); 
foo(); 
console.log(3); // 1 
// 2 
// 3 
// 4 

控制台中输出结果的顺序很好地解释了运行时的工作过程:
(1) 打印 1;
(2) 调用异步函数 foo();
(3)(在 foo()中)打印 2;
(4)(在 foo()中)await 关键字暂停执行,为立即可用的值 null 向消息队列中添加一个任务;
(5) foo()退出;
(6) 打印 3;
(7) 同步线程的代码执行完毕;
(8) JavaScript 运行时从消息队列中取出任务,恢复异步函数执行;
(9)(在 foo()中)恢复执行,await 取得 null 值(这里并没有使用);
(10)(在 foo()中)打印 4;
(11) foo()返回。
如果 await 后面是一个期约,则问题会稍微复杂一些。此时,为了执行异步函数,实际上会有两个任务被添加到消息队列并被异步求值。下面的例子虽然看起来很反直觉,但它演示了真正的执行顺序:(TC39 对 await 后面是期约的情况如何处理做过一次修改。修改后,本例中的Promise.resolve(8)只会生成一个异步任务。因此在新版浏览器中,这个示例的输出结果为 123458967。实际开发中,对于并行的异步操作我们通常更关注结果,而不依赖执行顺序。——译者注)

async function foo() { console.log(2); console.log(await Promise.resolve(8)); console.log(9); 
} async function bar() {console.log(4); console.log(await 6); console.log(7); 
} console.log(1); 
foo(); 
console.log(3); 
bar(); 
console.log(5); // 1 
// 2 
// 3 
// 4 
// 5 
// 6 
// 7 
// 8 
// 9 

运行时会像这样执行上面的例子:
(1) 打印 1;
(2) 调用异步函数 foo();
(3)(在 foo()中)打印 2;
(4)(在 foo()中)await 关键字暂停执行,向消息队列中添加一个期约在落定之后执行的任务;
(5) 期约立即落定,把给 await 提供值的任务添加到消息队列;
(6) foo()退出;
(7) 打印 3;
(8) 调用异步函数 bar();
(9)(在 bar()中)打印 4;
(10)(在 bar()中)await 关键字暂停执行,为立即可用的值 6 向消息队列中添加一个任务;
(11) bar()退出;
(12) 打印 5;
(13) 顶级线程执行完毕;
(14) JavaScript 运行时从消息队列中取出解决 await 期约的处理程序,并将解决的值 8 提供给它;
(15) JavaScript 运行时向消息队列中添加一个恢复执行 foo()函数的任务;
(16) JavaScript 运行时从消息队列中取出恢复执行 bar()的任务及值 6;
(17)(在 bar()中)恢复执行,await 取得值 6;
(18)(在 bar()中)打印 6;
(19)(在 bar()中)打印 7;
(20) bar()返回;
(21) 异步任务完成,JavaScript 从消息队列中取出恢复执行 foo()的任务及值 8;
(22)(在 foo()中)打印 8;
(23)(在 foo()中)打印 9;
(24) foo()返回。

异步函数策略

因为简单实用,所以异步函数很快成为 JavaScript 项目使用最广泛的特性之一。不过,在使用异步函数时,还是有些问题要注意。

1. 实现 sleep()

很多人在刚开始学习 JavaScript 时,想找到一个类似 Java 中Thread.sleep()之类的函数,好在程序中加入非阻塞的暂停。以前,这个需求基本上都通过 setTimeout()利用 JavaScript 运行时的行为来实现的。
有了异步函数之后,就不一样了。一个简单的箭头函数就可以实现sleep():

async function sleep(delay) { return new Promise((resolve) => setTimeout(resolve, delay)); 
} async function foo() { const t0 = Date.now(); await sleep(1500); // 暂停约 1500 毫秒 console.log(Date.now() - t0); 
} 
foo(); 
// 1502 

2. 利用平行执行

如果使用 await 时不留心,则很可能错过平行加速的机会。来看下面的例子,其中顺序等待了 5 个随机的超时:

async function randomDelay(id) { // 延迟 0~1000 毫秒 const delay = Math.random() * 1000; return new Promise((resolve) => setTimeout(() => { console.log(`${id} finished`); resolve(); }, delay)); 
} async function foo() { const t0 = Date.now(); await randomDelay(0); await randomDelay(1); await randomDelay(2); await randomDelay(3); await randomDelay(4); console.log(`${Date.now() - t0}ms elapsed`); 
} 
foo(); // 0 finished 
// 1 finished 
// 2 finished 
// 3 finished 
// 4 finished 
// 877ms elapsed 

用一个 for 循环重写,就是:

async function randomDelay(id) { // 延迟 0~1000 毫秒 const delay = Math.random() * 1000; return new Promise((resolve) => setTimeout(() => { console.log(`${id} finished`); resolve(); }, delay)); 
} async function foo() { const t0 = Date.now(); for (let i = 0; i < 5; ++i) { await randomDelay(i); } console.log(`${Date.now() - t0}ms elapsed`); 
} 
foo(); // 0 finished 
// 1 finished 
// 2 finished 
// 3 finished 
// 4 finished 
// 877ms elapsed 

就算这些期约之间没有依赖,异步函数也会依次暂停,等待每个超时完成。这样可以保证执行顺序,但总执行时间会变长。
如果顺序不是必需保证的,那么可以先一次性初始化所有期约,然后再分别等待它们的结果。比如:

async function randomDelay(id) { // 延迟 0~1000 毫秒 const delay = Math.random() * 1000; return new Promise((resolve) => setTimeout(() => { setTimeout(console.log, 0, `${id} finished`); resolve(); }, delay)); 
} async function foo() { const t0 = Date.now(); const p0 = randomDelay(0); const p1 = randomDelay(1); const p2 = randomDelay(2); const p3 = randomDelay(3); const p4 = randomDelay(4); await p0; await p1; await p2; await p3; await p4; setTimeout(console.log, 0, `${Date.now() - t0}ms elapsed`); 
} 
foo(); // 1 finished 
// 4 finished 
// 3 finished 
// 0 finished 
// 2 finished 
// 877ms elapsed 

用数组和 for 循环再包装一下就是:

async function randomDelay(id) {// 延迟 0~1000 毫秒const delay = Math.random() * 1000return new Promise((resolve) =>setTimeout(() => {console.log(`${id} finished`)resolve()}, delay))
}async function foo() {const t0 = Date.now()const promises = Array(5).fill(null).map((_, i) => randomDelay(i))for (const p of promises) {await p}console.log(`${Date.now() - t0}ms elapsed`)
}
foo()// 4 finished
// 2 finished
// 1 finished
// 0 finished
// 3 finished
// 877ms elapsed

注意,虽然期约没有按照顺序执行,但 await 按顺序收到了每个期约的值:

async function randomDelay(id) { // 延迟 0~1000 毫秒 const delay = Math.random() * 1000; return new Promise((resolve) => setTimeout(() => { console.log(`${id} finished`); resolve(id); }, delay)); 
} async function foo() { const t0 = Date.now(); const promises = Array(5).fill(null).map((_, i) => randomDelay(i)); for (const p of promises) { console.log(`awaited ${await p}`); } console.log(`${Date.now() - t0}ms elapsed`); 
} 
foo(); 
// 1 finished 
// 2 finished 
// 4 finished 
// 3 finished 
// 0 finished 
// awaited 0 
// awaited 1 
// awaited 2 
// awaited 3 
// awaited 4 
// 645ms elapsed 

3. 串行执行期约

在 11.2 节,我们讨论过如何串行执行期约并把值传给后续的期约。使用 async/await,期约连锁会变得很简单:

function addTwo(x) {return x + 2;} 
function addThree(x) {return x + 3;} 
function addFive(x) {return x + 5;} async function addTen(x) { for (const fn of [addTwo, addThree, addFive]) { x = await fn(x); } return x; 
} addTen(9).then(console.log); // 19 

这里,await 直接传递了每个函数的返回值,结果通过迭代产生。当然,这个例子并没有使用期约,如果要使用期约,则可以把所有函数都改成异步函数。这样它们就都返回期约了:

async function addTwo(x) {return x + 2;} 
async function addThree(x) {return x + 3;} 
async function addFive(x) {return x + 5;} async function addTen(x) { for (const fn of [addTwo, addThree, addFive]) { x = await fn(x); } return x; 
} addTen(9).then(console.log); // 19 

4. 栈追踪与内存管理

期约与异步函数的功能有相当程度的重叠,但它们在内存中的表示则差别很大。看看下面的例子,它展示了拒绝期约的栈追踪信息:

function fooPromiseExecutor(resolve, reject) { setTimeout(reject, 1000, 'bar'); 
} function foo() { new Promise(fooPromiseExecutor); 
} 
foo(); 
// Uncaught (in promise) bar 
// setTimeout 
// setTimeout (async) 
// fooPromiseExecutor 
// foo 

根据对期约的不同理解程度,以上栈追踪信息可能会让某些读者不解。栈追踪信息应该相当直接地表现 JavaScript 引擎当前栈内存中函数调用之间的嵌套关系。在超时处理程序执行时和拒绝期约时,我
们看到的错误信息包含嵌套函数的标识符,那是被调用以创建最初期约实例的函数。可是,我们知道这些函数已经返回了,因此栈追踪信息中不应该看到它们。

答案很简单,这是因为 JavaScript 引擎会在创建期约时尽可能保留完整的调用栈。在抛出错误时,调用栈可以由运行时的错误处理逻辑获取,因而就会出现在栈追踪信息中。当然,这意味着栈追踪信息
会占用内存,从而带来一些计算和存储成本。 如果在前面的例子中使用的是异步函数,那又会怎样呢?比如:

function fooPromiseExecutor(resolve, reject) { setTimeout(reject, 1000, 'bar'); 
} async function foo() { await new Promise(fooPromiseExecutor); 
} 
foo(); // Uncaught (in promise) bar 
// foo 
// async function (async) 
// foo 

这样一改,栈追踪信息就准确地反映了当前的调用栈。fooPromiseExecutor()已经返回,所以它不在错误信息中。但 foo()此时被挂起了,并没有退出。JavaScript 运行时可以简单地在嵌套函数中存储指向包含函数的指针,就跟对待同步函数调用栈一样。这个指针实际上存储在内存中,可用于在出错时生成栈追踪信息。这样就不会像之前的例子那样带来额外的消耗,因此在重视性能的应用中是可以优先考虑的。

小结

长期以来,掌握单线程 JavaScript 运行时的异步行为一直都是个艰巨的任务。随着 ES6 新增了期约和 ES8 新增了异步函数,ECMAScript 的异步编程特性有了长足的进步。通过期约和 async/await,不仅可以实现之前难以实现或不可能实现的任务,而且也能写出更清晰、简洁,并且容易理解、调试的代码。

期约的主要功能是为异步代码提供了清晰的抽象。可以用期约表示异步执行的代码块,也可以用期约表示异步计算的值。在需要串行异步代码时,期约的价值最为突出。作为可塑性极强的一种结构,期约可以被序列化、连锁使用、复合、扩展和重组。

异步函数是将期约应用于 JavaScript 函数的结果。异步函数可以暂停执行,而不阻塞主线程。无论是编写基于期约的代码,还是组织串行或平行执行的异步代码,使用异步函数都非常得心应手。异步函数可以说是现代 JavaScript 工具箱中最重要的工具之一。

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

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

相关文章

NLP自然语言处理_序章

开一个新篇章&#xff0c;立一个flag&#xff0c;用一段时间来学习一下NLP&#xff0c;涨涨见识。 准备以B站 机器学习算法到transformer神经网络模型应用视频作为入门&#xff0c;此分类专门用于记录学习过程中的知识点以备自用。 一、何为NLP自然语言处理&#xff1f; NLP…

查看linux的主机配置脚本

废话不说 直接上指令 curl -Lso- bench.sh | bash 等待后&#xff0c;结果如图&#xff1a; 使用后没有问题&#xff0c;看情况使用 出事概不负责 介意勿用&#xff01;&#xff01;&#xff01;

RabbitMQ 各种通信模式的Python实现

一、RabbitMQ 原理 1、基本原理 RabbitMQ是流行的开源消息队列系统&#xff0c;用erlang语言开发。RabbitMQ是AMQP&#xff08;高级消息队列协议&#xff09;的标准实现。支持多种客户端&#xff0c;如&#xff1a;Python、Java、Javascript、C#、C/C,Go等&#xff0c;支持AJ…

使用yolov8 进行实例分割训练

1、基于windows 的ISAM标注 直接下载安装包&#xff0c;解压后即可使用 链接&#xff1a;https://pan.baidu.com/s/1u_6jk-7sj4CUK1DC0fDEXQ 提取码&#xff1a;c780 2、标注结果转yolo格式 通过ISAM标注后的json文件路径 原始json格式如下&#xff1a; ISAM.json 转 yolo.…

Leetcode算法训练日记 | day30

一、重新安排行程 1.题目 Leetcode&#xff1a;第 332 题 给你一份航线列表 tickets &#xff0c;其中 tickets[i] [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。 所有这些机票都属于一个从 JFK&#xff08;肯尼迪国际机场&#xff09;出发…

深入刨析 mysql 底层索引结构B+树

文章目录 前言一、什么是索引&#xff1f;二、不同索引结构对比2.1 二叉树2.2 平衡二叉树2.3 B-树2.4 B树 三、mysql 的索引3.1 聚簇索引3.2 非聚簇索引 前言 很多人看过mysql索引的介绍&#xff1a;hash表、B-树、B树、聚簇索引、主键索引、唯一索引、辅助索引、二级索引、联…

【Hadoop大数据技术】——Sqoop数据迁移(学习笔记)

&#x1f4d6; 前言&#xff1a;在实际开发中&#xff0c;有时候需要将HDFS或Hive上的数据导出到传统关系型数据库中&#xff08;如MySQL、Oracle等&#xff09;&#xff0c;或者将传统关系型数据库中的数据导入到HDFS或Hive上&#xff0c;如果通过人工手动进行数据迁移的话&am…

怎么看自己是不是公网IP?

当我们需要进行网络连接或者网络配置的时候&#xff0c;经常会遇到需要知道自己是否拥有公网IP的情况。公网IP是全球唯一的IP地址&#xff0c;在互联网上可直接访问和被访问&#xff0c;而私有IP则是在本地网络中使用&#xff0c;无法从互联网上直接访问。我们将介绍如何查看自…

笔记-----BFS宽度优先搜索

对于BFS&#xff1a;宽搜第一次搜到就是最小值&#xff0c;并且基于迭代&#xff0c;不会爆栈。 Flood Fill 模型 如果直译的话就是&#xff1a;洪水覆盖&#xff0c;意思就是像是从一个点一圈圈的往外扩散&#xff0c;如果遇见能够连通的就扩散&#xff0c;如果遇见无法联通的…

TCP三次握手,但通俗理解

如何用通俗的语言来解释TCP&#xff08;传输控制协议&#xff09;的三次握手过程&#xff1f; 想象一下你正在和朋友电话沟通&#xff0c;但你们之间不是心灵感应&#xff0c;而是需要通过清晰地听到对方的声音来确认通话质量良好。TCP三次握手就像是在电话拨通之前&#xff0…

爱普生发布一款16位MCU产品用于大电流LED驱动

精工爱普生发布一款内置Flash存储器的16位微控制器S1C17M13 该新品可以提供最大56mA的驱动电流用于驱动发光二极管(LED) 以往爱普生的微处理器大多继承了液晶驱动器电路&#xff0c;但近来随着工业自动化和家用设备使用7段LED显示的数量大幅增加&#xff0c;爱普生也推出了对应…

k8s:通过nodeSelector将pod调度到含有指定标签的结点上

一、查看node,并给node打标签 二、在资源清单文件中配置nodeSelector来指定要往满足哪个标签条件的结点进行调度 apiVersion: v1 kind: Pod metadata:name: probe-tcp spec:containers:- name: nginximage: nginxlivenessProbe:initialDelaySeconds: 5timeoutSeconds: 5tcpSo…

宝塔面板使用docker+nginx+gunicorn部署Django项目实战教程

第一步&#xff1a;创建Django项目 使用pip install django安装创建django项目的依赖在电脑某个根目录下执行django-admin startproject app创建一个名为app的Django项目。目录结构如下: ├── app │ ├── init.py │ ├── asgi.py │ ├── settings.py │ ├── url…

【海思Hi3516CV610】是面向新一代视频编解码标准、网络安全和隐私保护、人工智能行业应用方面的IPC SoC

海思Hi3516CV610是面向新一代视频编解码标准、网络安全和隐私保护、人工智能行业应用方面的IPC SoC&#xff0c;除了开发普通摄像机&#xff0c;还可以打造极具竞争力的枪球一体机、双目长短焦摄像机产品&#xff1b; 处理器内核: 支持ARM Cortex-A7 MP2 时钟速率950MHz 支持…

90年代女神返港行李失踪 怒斥国泰航空

现年51岁的童爱玲在1993年拍摄电影《火蝴蝶》入行&#xff0c;外形出众的她当年曾与梁朝伟、黎明等男神合作&#xff0c;因而被封为「男神磁石」。虽然童爱玲与台湾富商王敦民结婚诞下一子后&#xff0c;便淡出演艺圈&#xff0c;但她曾在2022年复出拍摄ViuTV剧集《季前赛》&am…

探索MATLAB在计算机视觉与深度学习领域的实战应用

随着人工智能技术的快速发展&#xff0c;计算机视觉与深度学习已成为科技领域中最热门、最具挑战性的研究方向之一。 它们的应用范围从简单的图像处理扩展到了自动驾驶、医疗影像分析、智能监控行业等多个领域。 在这样的背景下&#xff0c;《MATLAB计算机视觉与深度学习实战…

3D开发工具HOOPS助力CAM软件优化制造流程

在现代制造业中&#xff0c;计算机辅助制造&#xff08;CAM&#xff09;软件的发展已成为提高生产效率和产品质量的关键。为了满足不断增长的需求和日益复杂的制造流程&#xff0c;CAM软件需要具备高效的CAD数据导入、云端协作、移动应用支持以及丰富的文档生成能力。 Tech So…

nginx installed inLinux

yum install nginx [rootmufeng ~]# yum install nginx CentOS系列&#xff1a;【Linux】CentOS7操作系统安装nginx实战&#xff08;多种方法&#xff0c;超详细&#xff09; ———————————————— 版权声明&#xff1a;本文为博主原创文章&#xff0c;遵循 CC …

FPGA_verilog语法整理

FPGA_verilog语法整理 verilog的逻辑值 verilog的常数表达 位宽中指定常数的宽度&#xff08;表示成二进制数的位数&#xff09;&#xff0c;单引号加表示该常数为几进制的底数符号。 二进制底数符号为b&#xff0c;八进制为 o&#xff0c;十进制为d&#xff0c;十六进制为 h…

Kimichat用于学习教育场景的7种高级方法

●AI作为导师 你是一个乐观、鼓励学生的导师&#xff0c;通过解释观点和问学生问题来帮助学生理解概念。下面每一步只一步步的执行&#xff0c;不要全部执行。 #向学生介绍自己&#xff0c;作为他们的AI导师&#xff0c;很高兴帮助他们解答任何问题。一次只问一个问题。 #首…