前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >跨越时空的对白——async&await分析

跨越时空的对白——async&await分析

原创
作者头像
Yerik
修改于 2022-05-08 14:07:40
修改于 2022-05-08 14:07:40
1.2K00
代码可运行
举报
文章被收录于专栏:烹饪一朵云烹饪一朵云
运行总次数:0
代码可运行

同步中的异步

在ES6中新增了asgnc...await...的异步解决方案,对于这种方案,有多种操作姿势,比如这样

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const asyncReadFile = async function(){
    const f1 = await readFile('/etc/fstab')
    const f2 = await readFile('/etc/shells')
    console.log(f1.toString())
    console.log(f2.toString())
}

或者是这样

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
async function f(){
    try{
        await new Promise.reject('出错了')
    } catch(e){
        
    }
    return await Promise.resolve('hello yerik')
}

是否能发现这两种使用方式的各自的特点:

  • async...await...异步解决方案支持同步的方式去执行异步操作
  • async...await...异步解决方案支持通过try...catch...进行异常捕获

对于第一点来说还好理解,但第2种说法就很费解了,以至于有一种颠覆以往理解的绝望感,对于js的世界观都已经灰色。对于try...catch...来说,不都是同步执行过程中捕获异常的吗,为何在async...await...中的try...catch...可以捕获异步执行的异常呢?

这个时候就去翻一下阮一峰老师的ES6教程,还以为是我当年看书走眼了,忘了啥,查漏补缺,结果阮老师就这么轻飘飘一句话

es6教程片段.png
es6教程片段.png

┑( ̄Д  ̄)┍

时间和空间上的分离

阮老师,您说的是平行时空么?还是错位空间?

错位空间.png
错位空间.png

我吹过你吹过的晚风

那我们算不算 相拥

我遇到过你发现的error,那我们算不算相拥,反正我读完也是挺郁闷的,阮老师那种在大气层的理解,对于普通人的我还是需要一层层剖析才能理解,那就先按照自己的理解来说吧,大家一起探讨一下,看看有没有道理

我们知道对于nodejs的异步实现都是借助libuv其他线程完成的。正常情况下,当eventloop通知调用栈处理异步回调函数的时候,原调用栈种的函数应该已经执行完了,因此调用函数和异步逻辑是由完全不同的线程执行的,本质上是没有交集的,这个时候可以理解为空间上是隔离的。异步回调被触发执行时,调用函数早已执行结束,因而,回调函数和调用函数的执行在时间上也是隔离的

好了,时空隔离的问题,勉强解释通了,但是async...await...又是怎么打破这种隔离,让其中的try...catch...可以捕获到异步操作中的异常?曾经大胆猜测,async...await...可以强行拉长try...catch...作用域,让调用函数的生命周期可以尽量延长,以至于可以等待直到异步函数执行完成,在此期间如果异步过程出现异常,调用函数就可以捕捉到,然而这个延长函数生命周期并等待异步执行结束,这不就是相当于是在阻塞线程的执行?阻塞执行——这跟JS的非阻塞的特质又是背道而驰的。

至此我总觉得在调用函数和异步逻辑之间存在某种诡异的tunnel,对!说的就是那股风!其可以在主函数和异步函数这两个不同时空互相隔离的生物进行消息传递,比如说在时空A中捕获了时空B里面的异常消息,这样它们就可以相拥❤

怎么想都觉得这个过程离大谱!

try...catch...不能捕获异步异常

try...catch...能捕获到的仅仅是try模块内执行的同步方法的异常(try执行中且不需要异步等待),这时候如果有异常,就会将异常抛到catch中。

捕获异常.png
捕获异常.png

除此之外,try...catch...执行之前的异常,以及try...catch...内的异步方法所产生的异常(例如ajax请求、定时器),都是不会被捕获的!看代码

无法捕获.png
无法捕获.png

这段代码中,setTimeout的回调函数抛出一个错误,并不会在catch中捕获,会导致程序直接报错崩掉。

这说明在jstry...catch...并不是说写上一个就可以高枕无忧。尤其是在异步处理的场景下。

那这个问题是怎么来的呢?

我从网上扒了个动图,可以比较形象的解释这个问题。图中演示了foo,bar,tmp,baz四个函数的执行过程。同步函数的执行在调用栈中转瞬即逝,异步处理需要借助libuv。比如这个setTimeout这个Web API,它独立于主线程中的libuv中别的线程负责执行。执行结束吼,会将对应回调函数放到等待队列中,当调用栈空闲吼会从等待队列中取出回调函数执行

解释.gif
解释.gif
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const foo = ()=>console.log('Start!')
const bar = ()=>setTimeout(()=>console.log('Timeout!'), 0)
const tmp = ()=>Promise.resolve('Promise!').then(res=>console.log(res))
const baz = ()=>console.log('End!')

foo();
bar();
tmp();
baz();

不能捕获的原因

为了讲清楚不能被捕获的原因,我改一下代码,模拟异步过程发生了异常。大家可以把执行逻辑再套回刚才的动图逻辑再看一下,(后面有机会学习怎么做动图哈哈哈)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const bar = ()=> {
    try{
        setTimeout(()=>{
            throw new Error()
        }, 500)
    }catch(e){
        // catch error.. don't work
    }
}

setTimeout的回调在Queue排队等待执行的时候,Call Stack中的bar就已经执行完了,bar的销毁顺便也终止了try...catch...的捕获域。当主进程开始执行throw new Error()的时候,相当于外层是没有任何捕获机制的,该异常会直接抛出给V8进行处理

回调函数无法捕获?

因为大部分遇到无法catch的情况,都发生在回调函数,就认为回调函数不能catch,这个结论是对的吗?

只能说不一定,且看这个例子

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 定义一个 fn,参数是函数。
const fn = (cb: () => void) => {
  cb();
};

function main() {
  try {
    // 传入 callback,fn 执行会调用,并抛出错误。
    fn(() => {
      throw new Error('123');
    })
  } catch(e) {
    console.log('error');
  }
}
main();

结果当然是可以catch的。因为callback执行的时候,跟main还在同一次事件循环中,即一个eventloop tick。所以上下文没有变化,错误是可以catch的。 根本原因还是同步代码,并没有遇到异步任务。

如何捕获?

简单来说就是哪里抛异常就在哪里捕获

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const bar = ()=> {
    setTimeout(()=>{
        try{
            throw new Error()
        }catch(e){
            // catch error.. don't work
        }
    }, 500)
}

那这样写代码一点都不会快乐了,要出处小心,时候留意以防哪里没有考虑到异常的场景。

基于Promise的解决方案

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise提供统一的 API,各种异步操作都可以用同样的方法进行处理。

本质上,这个就是一个状态管理机,同时又提供resolvereject两个开关。resolve负责将状态机的状态调整成Fulfilledreject将状态处理成Rejected

对于Promise来说是如何处理异常的?我们不妨通过改造前面的代码来试试

code1

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function bar(){
    new Promise((resolve, reject)=>{
        setTimeout(()=>{
            // 通过throw抛出异常
            throw new Error('err')
        }, 500)
    })
}
function exec(){
    try{
        bar().then(res=>{
            console.log('res', res)
        })
    }catch(err){
        console.log('err has been caught in try-catch block')
    }
}

在这个过程中,尝试抛出全局异常Uncaught Error,然而try...catch...并没有捕获到。造成这个问题的原因还是在于异常抛出的时候,exec已经从执行栈中出栈了,此外,在Promise规范里有说明,在异步执行的过程中,通过throw抛出的异常是无法捕获的,异步异常必须通过reject捕获

code1.png
code1.png

code2

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function bar(){
    return new Promise((resolve, reject)=>{
        setTimeout(()=>{
            reject('err')
        }, 500)
    })
}
function exec(){
    try{
        bar().then(res=>{
            console.log('res', res)
        })
    }catch(err){
        console.log('err has been caught in try-catch block')
    }
}

这次通过reject抛出异常,但是try...catch...同样还是没有捕获到异常。原因是reject需要配合Promise.prototype.catch一起使用

code2.png
code2.png

code3

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function bar(){
    return new Promise((resolve, reject)=>{
        setTimeout(()=>{
            reject('err')
        }, 500)
    })
}
function exec(){
    try{
        bar().then(res=>{
            console.log('res', res)
        }).catch(err=>{
            // Promise.prototype.catch捕获异常
            console.log('err has been caught in promise catch')
        })
    }catch(err){
        console.log('err has been caught in try-catch block')
    }
}

这次,异常成功地通过Promise.prototype.catch捕获到了,现在我们完全可以确定,在Promise中,异常的捕获跟try...catch...没有什么关系。

code3.png
code3.png

code4

至此我们已然通过try...catch...捕获异常的测试,那如果采用async...await...的方式呢?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function bar(){
    return new Promise((resolve, reject)=>{
        setTimeout(()=>{
            reject('err')
        }, 500)
    })
}
async function exec(){
    // trycatch 捕获异常
    try{
        await bar()
    }catch(err){
        console.log("err has been caught in try-catch block")
    }
}

惊讶的发现,通过这样的方式,我们终于通过try...catch...捕捉到了异常!对于code3和code4来说,我们的差异在于采用了async...await...,而这,到底是什么原理来实现的呢?至此,问题的根源我们已经模拟出来了,接下来是剖析

code4.png
code4.png

小结

Promise必须为以下三种状态之一:等待态Pending、执行态Fulfilled和拒绝态Rejected。一旦Promiseresolvereject,不能再迁移至其他任何状态(即状态immutable

小结.png
小结.png

基本过程:

  • 初始化Promise状态pending
  • 立即执行Promise中传入的fn函数,将Promise内部resolvereject函数作为参数传递给fn,按事件机制时机处理
  • 执行then(..)注册回调处理数组(then方法可被同一个promise调用多次)
  • Promise里的关键是要保证,then方法传入的参数onFulfilledonRejected,必须在then方法被调用的那一轮事件循环之后的新执行栈中执行。

对于Promise来说,本质上也是基于回调的,只要是基于回调,那就同样无法摆脱try...catch...不能捕获异步异常的事实。不过在Promise规范中有一套自己的异常处理逻辑,尽管这并不能打破时空上的隔离,但由于其将异步的异常逻辑封装在回调逻辑中,当Promise的状态发生改变时,将错误或异常以回调的形式呈现出来

虽然Promise的出现很大程度改变了编程的习惯,不过嘛,这个机制还是有问题的,毕竟其运行的过程非常依赖内部状态的控制,我们知道Promise的状态控制是非常依赖resolvereject,这就意味着,我们必须很清楚明白异常会出现在哪里,然后异常出现的地方需要通过reject的方式将Promise的状态调整成Rejected,也就说,我们需要很明确代码要在什么地方执行reject

异常本无形,它的出现不一定可控,在工程实践的过程中又是大肠包小肠,层层套娃,Promise可以处理我们已经明确的异常,那么那些不明确的又需要怎么处理呢?为了从本质上处理这个问题,async...await...由此而生

async&await今生

啰啰嗦嗦说了这么多,铺垫了async...await...的诞生背景——为了解决异常跨越时空的问题,这部分则是解释async...await...实现的原理,是的,就是那股风的来源,风起之处——Generator

Generator

Generator函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即暂停执行)。

协程

传统的编程语言,早有异步编程的解决方案(其实是多任务的解决方案)。其中有一种叫做协程coroutine,意思是多个线程互相协作,完成异步任务。

对于协程来说其有点像函数,又有点像线程。它的运行流程大致如下

  • 协程A开始执行。
  • 协程A执行到一半,进入暂停,任务挂起,执行权转移到协程B。
  • (一段时间后)协程B交还执行权。
  • 协程A恢复执行。

上面流程的协程A,就是异步任务,因为它分成两段(或多段)执行。

举例来说,读取文件的协程写法如下。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function* asyncJob() {
  // ...其他代码
  var f = yield readFile(fileA);
  // ...其他代码
}

上面代码的函数asyncJob是一个协程,它的奥妙就在其中的yield命令。它表示执行到此处,执行权将交给其他协程。也就是说,yield命令是异步两个阶段的分界线。

协程遇到yield命令就暂停,等到执行权返回,再从暂停的地方继续往后执行。它如果去除yield命令,这种写法非常跟同步操作相比,不要说相似,简直一模一样。

协程的 Generator 函数实现

Generator函数是协程在ES6的实现,最大特点就是可以交出函数的执行权,即暂停执行。

整个Generator函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用yield语句注明。Generator函数的执行方法如下。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function* gen(x) {
  var y = yield x + 2;
  return y;
}
var g = gen(1);
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }

上面代码中,调用Generator函数,会返回一个内部指针(即遍历器)g。这是Generator函数不同于普通函数的另一个地方,即执行它不会返回结果,返回的是指针对象。调用指针g的next方法,会移动内部指针(即执行异步任务的第一段),指向第一个遇到的yield语句,上例是执行到x + 2为止。

换言之,next方法的作用是分阶段执行Generator函数。每次调用next方法,会返回一个对象,表示当前阶段的信息(value属性和done属性)。value属性是yield语句后面表达式的值,表示当前阶段的值;done属性是一个布尔值,表示Generator函数是否执行完毕,即是否还有下一个阶段。

异常捕获

Generator函数可以暂停执行和恢复执行,这是它能封装异步任务的根本原因。除此之外,它还有两个特性,使它可以作为异步编程的完整解决方案:

  • 函数体内外的数据交换
  • 错误处理机制。

注意观察代码中的两个next的不同

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function* gen(x){
  var y = yield x + 2;
  return y;
}
var g = gen(1);
g.next() // { value: 3, done: false }
g.next(2) // { value: 2, done: true }

next返回值的value属性,是Generator函数向外输出数据

next方法还可以接受参数,向Generator函数体内输入数据。

上面代码中,第一个next方法的value属性,返回表达式x + 2的值3。第二个next方法带有参数2,这个参数可以传入Generator函数,作为上个阶段异步任务的返回结果,被函数体内的变量y接收。因此,这一步的value属性,返回的就是2(变量y的值)。

Generator函数内部还可以部署错误处理代码,捕获函数体外抛出的错误。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function* gen(x){
  try {
    var y = yield x + 2;
  } catch (e){
    console.log(e);
  }
  return y;
}
var g = gen(1);
g.next();
g.throw('出错了');
// 出错了

上面代码的最后一行,Generator 函数体外,使用指针对象的throw方法抛出的错误,可以被函数体内的try...catch代码块捕获。这意味着,出错的代码与处理错误的代码,实现了时间和空间上的分离,这对于异步编程无疑是很重要的。

异步任务的封装

下面看看如何使用Generator函数,执行一个真实的异步任务。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var fetch = require('node-fetch');
function* gen(){
  var url = 'https://api.github.com/users/github';
  var result = yield fetch(url);
  console.log(result.bio);
}

上面代码中,Generator 函数封装了一个异步操作,该操作先读取一个远程接口,然后从JSON格式的数据解析信息。就像前面说过的,这段代码非常像同步操作,除了加上了yield命令。

执行这段代码的方法如下。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var g = gen();
var result = g.next();
result.value.then(function(data){
  return data.json();
}).then(function(data){
  g.next(data);
});

上面代码中,首先执行Generator函数,获取遍历器对象,然后使用next方法(第二行),执行异步任务的第一阶段。由于Fetch模块返回的是一个Promise对象,因此要用then方法调用下一个next方法。

可以看到,虽然Generator函数将异步操作表示得很简洁,但是流程管理却不方便(即何时执行第一阶段、何时执行第二阶段)。

co模块

co 模块是著名程序员TJ Holowaychuk于 2013 年 6 月发布的一个小工具,用于Generator 函数的自动执行。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var gen = function* () {
  var f1 = yield readFile('/etc/fstab');
  var f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

co模块可以让你不用编写Generator函数的执行器。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var co = require('co');
co(gen);

上面代码中,Generator 函数只要传入co函数,就会自动执行。

co函数返回一个Promise对象,因此可以用then方法添加回调函数。

代码语言:txt
AI代码解释
复制
co(gen).then(function (){
  console.log('Generator 函数执行完成');
});

上面代码中,等到Generator函数执行结束,就会输出一行提示。

异步实现

先回答了异步实现的前置条件——基于协程,之后我们再来看看异步的关键词async

ES2017标准引入了async函数,使得异步操作变得更加方便。

async函数是什么?一句话,它就是Generator函数的语法糖。

前文有一个Generator函数,依次读取两个文件。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const fs = require('fs');
const readFile = function (fileName) {
  return new Promise(function (resolve, reject) {
    fs.readFile(fileName, function(error, data) {
      if (error) return reject(error);
      resolve(data);
    });
  });
};
const gen = function* () {
  const f1 = yield readFile('/etc/fstab');
  const f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

上面代码的函数gen可以写成async函数,就是下面这样。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const asyncReadFile = async function () {
  const f1 = await readFile('/etc/fstab');
  const f2 = await readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

一比较就会发现,async函数就是将Generator函数的星号(*)替换成async,将yield替换成await,仅此而已。

async函数对Generator函数的改进,体现在以下四点。

  • 内置执行器。asyncReadFile();上面的代码调用了asyncReadFile函数,然后它就会自动执行,输出最后结果。这完全不像Generator函数,需要调用next方法,或者用co模块,才能真正执行,得到最后结果。
    • Generator函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。
  • 更好的语义。
    • asyncawait,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
  • 更广的适用性。
    • co模块约定,yield命令后面只能是Thunk函数或Promise对象,而async函数的await命令后面,可以是Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。
  • 返回值是Promise
    • async函数的返回值是Promise对象,这比Generator函数的返回值是Iterator对象方便多了。你可以用then方法指定下一步的操作。

进一步说,async函数完全可以看作多个异步操作,包装成的一个Promise对象,而await命令就是内部then命令的语法糖。

async实现原理

本质上是将Generator函数和自动执行器,包装在一个函数里。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
async function fn(args) {
  // ...
}
// 等同于
function fn(args) {
  return spawn(function* () {
    // ...
  });
}

所有的async函数都可以写成上面的第二种形式,其中的spawn函数就是自动执行器。

下面给出spawn函数的实现,基本就是前文自动执行器的翻版。

代码语言:txt
AI代码解释
复制
function spawn(genF) {
  return new Promise(function(resolve, reject) {
    const gen = genF();
    function step(nextF) {
      let next;
      try {
        next = nextF();
      } catch(e) {
        return reject(e);
      }
      if(next.done) {
        return resolve(next.value);
      }
      Promise.resolve(next.value).then(function(v) {
        step(function() { return gen.next(v); });
      }, function(e) {
        step(function() { return gen.throw(e); });
      });
    }
    step(function() { return gen.next(undefined); });
  });
}

await分析

根据语法规格,await命令只能出现在async函数内部,否则都会报错。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 报错
const data = await fetch('https://api.github.com');

上面代码中,await命令独立使用,没有放在async函数里面,就会报错。

目前,有一个语法提案,允许在模块的顶层独立使用await命令,使得上面那行代码不会报错了。这个提案的目的,是借用await解决模块异步加载的问题。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// awaiting.js
let output;
async function main() {
  const dynamic = await import(someMission);
  const data = await fetch(url);
  output = someProcess(dynamic.default, data);
}
main();
export { output };

上面代码中,awaiting.js除了输出output,还默认输出一个Promise对象(async 函数立即执行后,返回一个Promise对象),从这个对象判断异步操作是否结束。

下面是加载这个模块的新的写法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// usage.js
import promise, { output } from "./awaiting.js";
function outputPlusValue(value) { return output + value }
promise.then(() => {
  console.log(outputPlusValue(100));
  setTimeout(() => console.log(outputPlusValue(100), 1000);
});

上面代码中,将awaiting.js对象的输出,放在promise.then()里面,这样就能保证异步操作完成以后,才去读取output

这种写法比较麻烦,等于要求模块的使用者遵守一个额外的使用协议,按照特殊的方法使用这个模块。一旦你忘了要用Promise加载,只使用正常的加载方法,依赖这个模块的代码就可能出错。而且,如果上面的usage.js又有对外的输出,等于这个依赖链的所有模块都要使用Promise加载。

顶层的await命令,就是为了解决这个问题。它保证只有异步操作完成,模块才会输出值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// awaiting.js
const dynamic = import(someMission);
const data = fetch(url);
export const output = someProcess((await dynamic).default, await data);

上面代码中,两个异步操作在输出的时候,都加上了await命令。只有等到异步操作完成,这个模块才会输出值。

加载这个模块的写法如下。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// usage.js
import { output } from "./awaiting.js";
function outputPlusValue(value) { return output + value }
console.log(outputPlusValue(100));
setTimeout(() => console.log(outputPlusValue(100), 1000);

上面代码的写法,与普通的模块加载完全一样。也就是说,模块的使用者完全不用关心,依赖模块的内部有没有异步操作,正常加载即可。

这时,模块的加载会等待依赖模块(上例是awaiting.js)的异步操作完成,才执行后面的代码,有点像暂停在那里。所以,它总是会得到正确的output,不会因为加载时机的不同,而得到不一样的值。

小结

协程的引入具备了挂起自己和被重新唤醒的能力。可以想象一下,协程在被中断吼,是需要某种机制来保存当前执行的上下文。在空间上,协程初始化创建的时候为其分配的栈一定的栈空间,用来保存执行过程中的一些关键信息,当函数被唤醒后,通过栈内保存的信息恢复"案发现场"。

总结

至此,前面code4中的案例就解释通了,await的时候exec函数被挂起,等bar函数中的异步操作执行结束后,exec函数被恢复。此时恢复的还有try...catch...。这个时候可以发现Promise的状态已经通过reject触发,由于没有Promise.prototype.catch,所以这个时候Promise会把异常向外抛出,正好被try...catch...捕捉到,这个时候,确实如前文所猜测,在async...await...try...catch...就是守株待兔,并且最后还真的等到了!

总结.png
总结.png

Sync invoke an async function and await its returned awaitable object.

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
端到端自动驾驶:数据、方法与展望
“端到端”,可以说是神经网络战胜传统算法的最重要法宝。一个训练好的神经网络,只需要部署在一个接受输入数据的模块里面,就可以直接输出该任务的最终结果,曾经让目标检测、语义分割、关键点检测等计算机视觉(现在已经不限于视觉了)无论是精度、速度还是模块本身的部署都有了划时代的进展。
一点人工一点智能
2023/05/04
9680
端到端自动驾驶:数据、方法与展望
新能源时代!看大模型(LLMs)如何助力汽车自动驾驶!
本文主要介绍大模型(LLMs)如何助力汽车自动驾驶,简单来说,作者首先带大家了解大模型的工作模式,然后介绍了自动驾驶大模型的3大应用场景,最后指出自动驾驶大模型将会是未来的发展趋势,只要坚持,国内新能源造车新势力还是很有机会的。本文没有深入讲解算法架构,而是化繁为简,能够让您很快的对自动驾驶大模型有个较为全面的理解。
ShuYini
2024/04/12
8880
新能源时代!看大模型(LLMs)如何助力汽车自动驾驶!
thinktwice:用于端到端自动驾驶的可扩展解码器(已开源)
题目:Think Twice before Driving: Towards Scalable Decoders for End-to-End Autonomous Driving
BBuf
2023/08/22
3870
thinktwice:用于端到端自动驾驶的可扩展解码器(已开源)
当视觉遇到毫米波雷达:自动驾驶的三维目标感知基准
文章:Vision meets mmWave Radar: 3D Object Perception Benchmark for Autonomous Driving
点云PCL博主
2023/12/12
7890
当视觉遇到毫米波雷达:自动驾驶的三维目标感知基准
给自动驾驶一双通天眼——环境感知器篇
视频是Tesla自动驾驶简述 给自动驾驶一双”通天眼” ——环境感知器篇 ▌智能驾驶感知任务
WZEARW
2018/04/10
3.9K0
给自动驾驶一双通天眼——环境感知器篇
【智驾深谈】自动驾驶潜在突破点:传感器与车载集成系统(万字干货)
作者:黄武陵 【新智元导读】黄武陵从事无人车系统研发、无人车标准评估体系构建、无人车挑战赛等工作多年,每次发表观点都非常详尽,本文从自动驾驶近期热点入手,结合挑战赛八年积累,深入分析了传感、感知、车联
新智元
2018/03/23
1.7K0
【智驾深谈】自动驾驶潜在突破点:传感器与车载集成系统(万字干货)
K-Radar:适用于各种天气条件的自动驾驶4D雷达物体检测
文章:K-Radar: 4D Radar Object Detection for Autonomous Driving in Various Weather Conditions
点云PCL博主
2023/12/11
7990
K-Radar:适用于各种天气条件的自动驾驶4D雷达物体检测
实时自动驾驶车辆定位技术概述
实时、准确和鲁棒的定位对于自动驾驶汽车(AVs)实现安全、高效驾驶至关重要,而实时性能对于AVs及时实现其当前位置以进行决策至关重要。迄今为止,没有一篇综述文章定量比较了基于各种硬件平台和编程语言的不同定位技术之间的实时性能,并分析了定位方法、实时性能和准确性之间的关系。因此,本文讨论了最先进的定位技术,并分析了它们在AV应用中的整体性能。
一点人工一点智能
2023/02/18
2K0
实时自动驾驶车辆定位技术概述
综述:生成自动驾驶的高精地图技术(2)
文章:High-Definition Map Generation Technologies for Autonomous Driving
点云PCL博主
2022/09/13
1.1K0
综述:生成自动驾驶的高精地图技术(2)
万字综述 | 自动驾驶多传感器融合感知
原文:Multi-modal Sensor Fusion for Auto Driving Perception: A Survey
一点人工一点智能
2022/10/07
5.3K0
万字综述 | 自动驾驶多传感器融合感知
大模型来了,自动驾驶还远吗?关键看“眼睛”
感知系统是自动驾驶最重要的模块之一,被视为智能车的“眼睛”,对理解周围环境起到至关重要的作用。随着深度学习以及传感器技术的发展,感知系统呈现出迅猛的发展趋势,涌现出各种新技术,性能指标不断提升。本文将围绕感知系统架构、方法及挑战,结合驭势科技的具体实践深入探究自动驾驶感知技术。 作者 |耿秀军、李金珂、张丹、彭进展 出品 | 新程序员 感知系统架构与方法 目标的检测与跟踪是感知系统的两大基础任务,主要利用不同传感器数据输入,完成对周围障碍物的检测与跟踪,并将结果传递给下游规划控制模块完成预测、决策、规划、
AI科技大本营
2023/05/15
5120
大模型来了,自动驾驶还远吗?关键看“眼睛”
【自动驾驶专题】|自动驾驶技术概况及挑战
自动驾驶汽车毫无疑问是未来出行的标准方式。世界上的科技巨头,从Google、Uber到丰田及通用汽车等企业,都投入了巨资来促进该技术的成熟及商业化,因为它将是一个万亿级的市场。
用户7623498
2020/08/04
1.2K0
【自动驾驶专题】|自动驾驶技术概况及挑战
简述:机器人BEV检测中的相机-毫米波雷达融合
这篇文章主要介绍了相机-毫米波雷达融合在机器人 BEV 检测中的应用。为了构建自主机器人感知系统,研究人员和工程师们越来越关注传感器融合,以充分利用跨模态信息。然而,要构建大规模的机器人平台,我们还需要关注自主机器人平台的成本。相机和毫米波雷达包含了互补的感知信息,有潜力用于大规模自主机器人平台的开发。但是,相对于相机-激光雷达融合的研究工作而言,视觉-毫米波雷达融合的工作较少。
一点人工一点智能
2023/10/25
8410
简述:机器人BEV检测中的相机-毫米波雷达融合
自动驾驶综述|定位、感知、规划常见算法汇总
这一部分概述了自动驾驶汽车自动化系统的典型体系结构,并对感知系统、决策系统及其子系统的职责进行了评述。
磐创AI
2020/05/26
3K0
自动驾驶综述|定位、感知、规划常见算法汇总
BEV模型部署全栈教程(3D检测+车道线+Occ)
鸟瞰视角(Bird's Eye View,简称BEV)是一种从上方观看对象或场景的视角,就像鸟在空中俯视地面一样。在自动驾驶和机器人领域,通过传感器(如LiDAR和摄像头)获取的数据通常会被转换成BEV表示,以便更好地进行物体检测、路径规划等任务。BEV能够将复杂的三维环境简化为二维图像,这对于在实时系统中进行高效的计算尤其重要。
奔跑企鹅907340320
2024/10/08
6510
深入探究鸟瞰图感知问题综述
文章:Delving into the Devils of Bird’s-eye-view Perception: A Review, Evaluation and Recipe
点云PCL博主
2023/08/21
7420
深入探究鸟瞰图感知问题综述
基于多LiDAR城市自动驾驶定位与地图构建方案
文章:Multi-LiDAR Localization and Mapping Pipeline for Urban Autonomous Driving
点云PCL博主
2023/11/13
6330
基于多LiDAR城市自动驾驶定位与地图构建方案
一文全览 | 2023最新环视自动驾驶3D检测综述!
基于视觉的3D检测任务是感知自动驾驶系统的基本任务,这在许多研究人员和自动驾驶工程师中引起了极大的兴趣。然而,使用带有相机的2D传感器输入数据实现相当好的3D BEV(鸟瞰图)性能并不是一项容易的任务。本文对现有的基于视觉的3D检测方法进行了综述,聚焦于自动驾驶。论文利用Vision BEV检测方法对60多篇论文进行了详细分析,并强调了不同的分类,以详细了解常见趋势。此外还强调了文献和行业趋势如何转向基于环视图像的方法,并记下了该方法解决的特殊情况的想法。总之,基于当前技术的缺点,包括协作感知的方向,论文为未来的研究提出了3D视觉技术的想法。
集智书童公众号
2023/09/04
1.3K0
一文全览 | 2023最新环视自动驾驶3D检测综述!
slam技术前景_技术市场的概念
大家好,又见面了,我是你们的朋友全栈君。 SLAM技术与市场杂谈 SLAM(simultaneous localization and mapping)全称即时定位与地图构建或并发建图与定位,主要的作用是让机器人在未知的环境中,完成定位(Localization),建图(Mapping)和路径规划(Navigation)。 目前,SLAM技术被广泛运用于机器人、无人机、无人驾驶、AR、VR等领域,依靠传感器可实现机器的自主定位、建图、路径规划等功能。 主流的slam技术应用有两种,分别是激光
全栈程序员站长
2022/11/15
1.4K0
slam技术前景_技术市场的概念
ContextVLM 使用视觉-语言模型实现自动驾驶车辆的环境和驾驶上下文识别 !
自动驾驶(AV)在现实生活中部署需要详细规定和应用操作设计域(ODDs)。ODDs是指自动驾驶车辆在广泛测试和预计安全操作的地理位置的道路和环境条件。核心自动驾驶功能(如感知、规划、行为和定位)的性能很大程度上取决于操作环境。特别是,基于传感器的感知可能受到雪、雾、雨和低光照条件的影响。运动规划器生成的路径和速度轨迹也可以从操作域知识中受益,如道路是上坡还是下坡、是否铺砌、鹅卵石或未经铺设。在周围区域的工作区知识有重大的安全意义[1]。此外,AV在乡村地区、城市峡谷、隧道或公路上的驾驶对定位性能有显著影响[2],例如,因为全球导航卫星系统(GNSS)精度的变化。然而,现有的研究通常忽略了可以喂养和影响自动驾驶堆叠的通用的上下文识别需求。
AIGC 先锋科技
2024/09/20
1780
ContextVLM  使用视觉-语言模型实现自动驾驶车辆的环境和驾驶上下文识别 !
推荐阅读
相关推荐
端到端自动驾驶:数据、方法与展望
更多 >
LV.1
北京博文视点资讯有限公司
目录
  • 同步中的异步
  • 时间和空间上的分离
  • try...catch...不能捕获异步异常
    • 不能捕获的原因
    • 回调函数无法捕获?
    • 如何捕获?
  • 基于Promise的解决方案
    • code1
    • code2
    • code3
    • code4
    • 小结
  • async&await今生
    • Generator
      • 协程
      • 协程的 Generator 函数实现
      • 异常捕获
      • 异步任务的封装
      • co模块
    • 异步实现
      • async实现原理
    • await分析
  • 小结
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档