详解JS的四种异步解决方案:回调函数、Promise、Generator、async/await
-
同步&异步的概念
在讲这四种异步方案之前,我们先来明确一下同步和异步的概念:
所谓同步(synchronization),简单来说,就是顺序执行,指的是同一时间只能做一件事情,只有目前正在执行的事情做完之后,才能做下一件事情。 比如咱们去火车站买票,假设窗口只有1个,那么同一时间只能处理1个人的购票业务,其余的需要进行排队。这种one by one的动作就是同步。 同步操作的优点在于做任何事情都是依次执行,井然有序,不会存在大家同时抢一个资源的问题。 同步操作的缺点在于会阻塞后续代码的执行。如果当前执行的任务需要花费很长的时间,那么后面的程序就只能一直等待。从而影响效率,对应到前端页面的展示来说,有可能会造成页面渲染的阻塞,大大影响用户体验。
所谓异步(Asynchronization),指的是当前代码的执行不影响后面代码的执行。当程序运行到异步的代码时,会将该异步的代码作为任务放进任务队列,而不是推入主线程的调用栈。等主线程执行完之后,再去任务队列里执行对应的任务即可。 因此,异步操作的优点就是:不会阻塞后续代码的执行。
js中异步的应用场景
开篇讲了同步和异步的概念,那么在JS中异步的应用场景有哪些呢?
- 定时任务:setTimeout、setInterval
- 网络请求:ajax请求、动态创建img标签的加载
- 事件监听器:addEventListener
ajax请求:Ajax即Asynchronous Javascript And XML(异步JavaScript和XML)在 2005年被Jesse James Garrett提出的新术语,用来描述一种使用现有技术集合的‘新’方法,包括:HTML 或 XHTML, CSS, JavaScript, DOM, XML, XSLT, 以及最重要的XMLHttpRequest。
实现异步的四种方法
对于setTimeout、setInterval、addEventListener这种异步场景,不需要我们手动实现异步,直接调用即可。 但是对于ajax请求、node.js中操作数据库这种异步,就需要我们自己来实现了~
回调函数
在微任务队列出现之前,JS实现异步的主要方式就是通过回调函数。 以一个简易版的Ajax请求为例,我们在业务代码里可以这样调用ajax请求:
ajax({url:'#',type:GET,success:function(e){// 回调函数里就是对请求结果的处理}
});
ajax的success方法就是一个回调函数,回调函数中执行的是我们请求成功之后要做的进一步操作。 这样就初步实现了异步,但是回调函数有一个非常严重的缺点,那就是回调地狱的问题。 大家可以试想一下,如果我们在回调函数里再发起一个ajax请求呢?那岂不是要在success函数里继续写一个ajax请求?那如果需要多级嵌套发起ajax请求呢?岂不是需要多级嵌套?如果嵌套的层级很深的话,我们的代码结构可能就会变成这样:
因此,为了解决回调地狱的问题,提出了Promise、async/await、generator的概念。
Promise
Promise作为典型的微任务之一,它的出现可以使JS达到异步执行的效果。 一个Promise函数的结构如下列代码如下:
const promise = new Promise((resolve, reject) => {resolve('a');
});
promise.then((arg) => { console.log(`执行resolve,参数是${arg}`) }).catch((arg) => { console.log(`执行reject,参数是${arg}`) }).finally(() => { console.log('结束promise') });
如果,我们需要嵌套执行异步代码,相比于回调函数来说,Promise的执行方式如下列代码所示:
const promise = new Promise((resolve, reject) => {resolve(1);
});
promise.then((value) => {console.log(value);return value * 2;}).then((value) => {console.log(value);return value * 2;}).then((value) => {console.log(value);}).catch((err) => {console.log(err);});
即,通过then来实现多级嵌套(链式调用),这看起来是不是就比回调函数舒服多了~
每个Promise都会经历的生命周期是:
- 进行中(pending) - 此时代码执行尚未结束,所以也叫未处理的(unsettled)
- 已处理(settled) - 异步代码已执行结束 已处理的代码会进入两种状态中的一种:
- 已完成(fulfilled) - 表明异步代码执行成功,由resolve()触发
- 已拒绝(rejected)- 遇到错误,异步代码执行失败 ,由reject()触发
因此,pending,fulfilled, rejected就是Promise中的三种状态啦~ 大家一定要牢记,在Promise中,要么包含resolve()来表示Promise的状态为fulfilled,要么包含reject()来表示Promise的状态为rejected。 不然我们的Promise就会一直处于pending的状态,直至程序崩溃...
除此之外,Promise不仅很好的解决了链式调用的问题,它还有很多神奇的操作呢:
- Promise.all(promises):接收一个包含多个Promise对象的数组,等待所有都完成时,返回存放它们结果的数组。如果任一被拒绝,则立即抛出错误,其他已完成的结果会被忽略
- Promise.allSettled(promises): 接收一个包含多个Promise对象的数组,等待所有都已完成或者已拒绝时,返回存放它们结果对象的数组。每个结果对象的结构为{status:'fulfilled' // 或 'rejected', value // 或reason}
- Promise.race(promises): 接收一个包含多个Promise对象的数组,等待第一个有结果(完成/拒绝)的Promise,并把其result/error作为结果返回
function getPromises(){return [new Promise(((resolve, reject) => setTimeout(() => resolve(1), 1000))),new Promise(((resolve, reject) => setTimeout(() => reject(new Error('2')), 2000))),new Promise(((resolve, reject) => setTimeout(() => resolve(3), 3000))),];
}Promise.all(getPromises()).then(console.log);
Promise.allSettled(getPromises()).then(console.log);
Promise.race(getPromises()).then(console.log);
打印结果如下:
Generator
Generator是ES6提出的一种异步编程的方案。因为手动创建一个iterator十分麻烦,因此ES6推出了generator,用于更方便的创建iterator。也就是说,Generator就是一个返回值为iterator对象的函数。
在讲Generator之前,我们先来看看iterator是什么:
iterator是什么?
iterator中文名叫迭代器。它为js中各种不同的数据结构(Object、Array、Set、Map)提供统一的访问机制。任何数据结构只要部署了Iterator接口,就可以完成遍历操作。 因此iterator也是一种对象,不过相比于普通对象来说,它有着专为迭代而设计的接口。
iterator 的作用:
- 为各种数据结构,提供一个统一的、简便的访问接口;
- 使得数据结构的成员能够按某种次序排列;
- ES6 创造了一种新的遍历命令for…of循环,Iterator 接口主要供for…of消费
iterator的结构: 它有next方法,该方法返回一个包含value和done两个属性的对象(我们假设叫result).
value是迭代的值,后者是表明迭代是否完成的标志。true表示迭代完成,false表示没有。iterator内部有指向迭代位置的指针,每次调用next,自动移动指针并返回相应的result
原生具备iterator接口的数据结构如下:
- Array
- Map
- Set
- String
- TypedArray
- 函数里的arguments对象
- NodeList对象
这些数据结构都有一个Symbol.iterator属性,可以直接通过这个属性来直接创建一个迭代器。 也就是说,Symbol.iterator属性只是一个用来创建迭代器的接口,而不是一个迭代器,因为它不含遍历的部分。
使用Symbol.iterator接口生成iterator迭代器来遍历数组的过程为:
let arr = ['a','b','c'];let iter = arr[Symbol.iterator]();iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }
for ... of的循环内部实现机制其实就是iterator,它首先调用被遍历集合对象的 Symbol.iterator 方法,该方法返回一个迭代器对象,迭代器对象是可以拥有.next()方法的任何对象,然后,在 for ... of 的每次循环中,都将调用该迭代器对象上的 .next 方法。然后使用for i of打印出来的i也就是调用.next方法后得到的对象上的value属性。
对于原生不具备iterator接口的数据结构,比如Object,我们可以采用自定义的方式来创建一个遍历器。
比如,我们可以自定义一个iterator来遍历对象:
let obj = {a: "hello", b: "world"};
// 自定义迭代器
function createIterator(items) {let keyArr = Object.keys(items);let i = 0;return {next: function () {let done = (i >= keyArr.length);let value = !done ? items[keyArr[i++]] : undefined;return {value: value,done: done,};}};
}let iterator = createIterator(obj);
console.log(iterator.next()); // "{ value: 'hello', done: false }"
console.log(iterator.next()); // "{ value: 'world', done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
接下来,我们来聊聊Generator:
我们通过一个例子来看看Gnerator的特征:
function* createIterator() {yield 1;yield 2;yield 3;
}
// generators可以像正常函数一样被调用,不同的是会返回一个 iterator
let iterator = createIterator();
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3
Generator 函数是 ES6 提供的一种异步编程解决方案。形式上,Generator 函数是一个普通函数,但是有两个特征:
- function关键字与函数名之间有一个星号
- 函数体内部使用yield语句,定义不同的内部状态
Generator函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是遍历器对象(Iterator Object)
发现generator函数的返回值的原型链上确实有iterator对象该有的next,这充分说明了generator的返回值是一个iterator。除此之外还有函数该有的return方法和throw方法。
在普通函数中,我们想要一个函数最终的执行结果,一般都是return出来,或者以return作为结束函数的标准。运行函数时也不能被打断,期间也不能从外部再传入值到函数体内。 但在generator中,就打破了这几点,所以generator和普通的函数完全不同。 当以function*的方式声明了一个Generator生成器时,内部是可以有许多状态的,以yield进行断点间隔。期间我们执行调用这个生成的Generator,他会返回一个遍历器对象,用这个对象上的方法,实现获得一个yield后面输出的结果。
function* generator() {yield 1yield 2
};
let iterator = generator();
iterator.next() // {value: 1, done: false}
iterator.next() // {value: 2, done: false}
iterator.next() // {value: undefined, done: true}
yield和return的区别:
- 都能返回紧跟在语句后面的那个表达式的值
- yield相比于return来说,更像是一个断点。遇到yield,函数暂停执行,下一次再从该位置继续向后执行,而return语句不具备位置记忆的功能。
- 一个函数里面,只能执行一个return语句,但是可以执行多次yield表达式。
- 正常函数只能返回一个值,因为只能执行一次return;Generator 函数可以返回一系列的值,因为可以有任意多个yield
语法注意点:
- yield表达式只能用在 Generator 函数里面
- yield表达式如果用在另一个表达式之中,必须放在圆括号里面
- yield表达式用作函数参数或放在赋值表达式的右边,可以不加括号。
- 如果 return 语句后面还有 yield 表达式,那么后面的 yield 完全不生效
使用Generator的其余注意事项:
需要注意的是,yield 不能跨函数。并且yield需要和*配套使用,别处使用无效
function* createIterator(items) {items.forEach(function (item) {// 语法错误yield item + 1;});
}
箭头函数不能用做 generator
讲了这么多,那么Generator到底有什么用呢?
- 因为Generator可以在执行过程中多次返回,所以它看上去就像一个可以记住执行状态的函数,利用这一点,写一个generator就可以实现需要用面向对象才能实现的功能。
- Generator还有另一个巨大的好处,就是把异步回调代码变成“同步”代码。这个在ajax请求中很有用,避免了回调地狱.
async/await
async/await是ES7提出的关于异步的终极解决方案。我看网上关于async/await是谁的语法糖这块有两个版本:
- 第一个版本说async/await是Generator的语法糖
- 第二个版本说async/await是Promise的语法糖
其实,这两种说法都没有错。
关于async/await是Generator的语法糖: 所谓Generator语法糖,表明的就是aysnc/await实现的就是generator实现的功能。但是async/await比generator要好用。因为generator执行yield设下的断点采用的方式就是不断的调用iterator方法,这是个手动调用的过程。针对generator的这个缺点,后面提出了co这个库函数来自动执行next,相比于之前的方案,这种方式确实有了进步,但是仍然麻烦。而async配合await得到的就是断点执行后的结果。因此async/await比generator使用更普遍。
总结下来,async函数对 Generator函数的改进,主要体现在以下三点:
- 内置执行器:Generator函数的执行必须靠执行器,因为不能一次性执行完成,所以之后才有了开源的 co函数库。但是,async函数和正常的函数一样执行,也不用 co函数库,也不用使用 next方法,而 async函数自带执行器,会自动执行。
- 适用性更好:co函数库有条件约束,yield命令后面只能是 Thunk函数或 Promise对象,但是 async函数的 await关键词后面,可以不受约束。
- 可读性更好:async和 await,比起使用 *号和 yield,语义更清晰明了。
关于async/await是Promise的语法糖: 如果不使用async/await的话,Promise就需要通过链式调用来依次执行then之后的代码:
function counter(n){return new Promise((resolve, reject) => { resolve(n + 1);});
}function adder(a, b){return new Promise((resolve, reject) => { resolve(a + b);});
}function delay(a){return new Promise((resolve, reject) => { setTimeout(() => resolve(a), 1000);});
}
// 链式调用写法
function callAll(){counter(1).then((val) => adder(val, 3)).then((val) => delay(val)).then(console.log);
}
callAll();//5
虽然相比于回调地狱来说,链式调用确实顺眼多了。但是其呈现仍然略繁琐了一些。 而async/await的出现,就使得我们可以通过同步代码来达到异步的效果:
async function callAll(){const count = await counter(1);const sum = await adder(count, 3);console.log(await delay(sum));
}
callAll();// 5
由此可见,Promise搭配async/await的使用才是正解!因为通常await关键字后面都是跟一个Promise,这个promise状态变为fulfilled才会执行await
后续的代码,所以await
后面的代码,相当于包括在.then
方法的回调中,如果状态变为rejected,你则需要在函数内部try catch
,或者进行链式调用进行.catch
操作。
总结
- promise让异步执行看起来更清晰明了,通过then让异步执行结果分离出来。
- async/await其实是基于Promise的。async函数其实是把promise包装了一下。使用async函数可以让代码简洁很多,不需要promise一样需要些then,不需要写匿名函数处理promise的resolve值,也不需要定义多余的data变量,还避免了嵌套代码。
- async函数是Generator函数的语法糖。async函数的返回值是 promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。同时,我们还可以用await来替代then方法指定下一步的操作。
- 感觉Promise+async的操作最为常见。因为Generator的常用功能可以直接由async来体现呀
相关文章:

详解JS的四种异步解决方案:回调函数、Promise、Generator、async/await
同步&异步的概念 在讲这四种异步方案之前,我们先来明确一下同步和异步的概念: 所谓同步(synchronization),简单来说,就是顺序执行,指的是同一时间只能做一件事情,只有目前正在执行的事情做完之后&am…...

Python进行多线程爬取数据通用模板
首先,我们需要导入所需的库,包括requests和BeautifulSoup。requests库用于发送HTTP请求,BeautifulSoup库用于解析HTML文档。 import requests from bs4 import BeautifulSoup然后,我们需要定义一个函数来发送HTTP请求并返回响应。…...

基于springboot实现沁园健身房预约管理系统【项目源码】
基于springboot实现沁园健身房预约管理系统演示 B/S架构 B/S结构是目前使用最多的结构模式,它可以使得系统的开发更加的简单,好操作,而且还可以对其进行维护。使用该结构时只需要在计算机中安装数据库,和一些很常用的浏览器就可以…...

论文笔记:Deep Trajectory Recovery with Fine-Grained Calibration using Kalman Filter
TKDE 2021 1 intro 1.1 背景 用户轨迹数据对于改进以用户为中心的应用程序很有用 POI推荐城市规划路线规划由于设备和环境的限制,许多轨迹以低采样率记录 采样的轨迹无法详细说明物体的实际路线增加了轨迹中两个连续采样点之间的不确定性——>开发有效的算法以…...

ubuntu下tensorrt环境配置
文章目录 一、Ubuntu18.04环境配置1.1 安装工具链和opencv1.2 安装Nvidia相关库1.2.1 安装Nvidia显卡驱动1.2.2 安装 cuda11.31.2.3 安装 cudnn8.21.2.4 下载 tensorrt8.4.2.4 二、编写CMakeLists.txt三、TensorRT系列教程 一、Ubuntu18.04环境配置 教程同样适用与ubuntu22.04…...

网络安全基础之php开发文件下载的实现
前言 php是网络安全学习里必不可少的一环,简单理解php的开发环节能更好的帮助我们去学习php以及其他语言的web漏洞原理 正文 在正常的开发中,文件下载的功能是必不可少,比如我们在论坛看到好看图片好听的歌时,将其下载下来时就…...

【学习笔记】 - GIT的基本操作,IDEA接入GIT以及上传hub
用github蛮多,但git没怎么用,看着视频对着写点笔记以及操作 一、GIT文件的三种状态和模式 已提交(committed) 已提交表示数据已经安全的保存在本地数据库中。 已修改(modified) 已修改表示修改了文件,但还没保存到数据库中。…...

Antd React Form.Item内部是自定义组件怎么自定义返回值
在线演示https://stackblitz.com/edit/stackblitz-starters-xwtwyz?filesrc%2FSelfTreeSelect.tsx 需求 当我们点击提交,需要返回用户名和选中树的id信息,但是,我不关要返回树的id信息,还需要返回选中树的名称 //默认返回的 {userName:梦洁,treeInfo:leaf1-value } //但是需…...
2023最新ACL大模型论文分类汇总(有代码的)
1 大模型文化道德 Knowledge of cultural moral norms in large language models url:https://aclanthology.org/2023.acl-long.26/code:https://github.com/AidaRamezani/cultural_inference 2 长文本推理 Open-ended Long Text Generation via Mask…...

Java版 招投标系统简介 招投标系统源码 java招投标系统 招投标系统功能设计
功能描述 1、门户管理:所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含:招标公告、非招标公告、系统通知、政策法规。 2、立项管理:企业用户可对需要采购的项目进行立项申请,并提交审批,查看所…...

Ubuntu 22.04源码安装cmake 3.27.7
安装参考博客是《ubuntu安装cmake》和《Ubuntu 安装CMake》。 https://cmake.org/download是cmake官网下载的网址。 sudo wget -c https://github.com/Kitware/CMake/releases/download/v3.27.7/cmake-3.27.7.tar.gz可以下载源码,最后显示‘cmake-3.27.7.tar.gz’…...

无人地磅称重系统|自助过磅 料仓联动 自助卸料
上海思伟无人地磅系统 自助过磅、 自助卸料 、料仓联动 智能、省人、安全 无人监管过磅 对地磅及其相关的所有硬件进行配置和管理; 支持红外、道闸、车牌识别、AI分析、拍照存档、LED语音播报一体机等设备; 实现稳定可靠的无人监管称重功能…...
冥想第九百七十三天
1.今天周六,很冷的天,上午上了一上午的日语课。 2.下午去看了朋友刚出生的孩子。 3.充实的一天。感谢父母,感谢朋友,感谢家人,感谢不断进步的自己....

ROS 学习应用篇(三)话题Topic学习之自定义话题消息的类型的定义与调用
自定义消息类型的定义 Person.msg文件的定义(数据接口文件的定义) 创建msg文件 首先在功能包下新建msg文件夹,接着在该文件夹下创建文件。 定义msg文件内容 一个消息最重要的就是数据结构类型。这就需要引入一个msg文件,用于…...

财税服务展示预约小程序的作用是什么
财税财政往往困扰着很多公司,尤其是公司里没有相应职员或工作压力大的情况下,不少商家就会寻找代理记账、审计服务、会计代理等服务的机构。 对财政服务代理机构(会计公司)来说,市场企业多而广,理论上来说…...

RT-Thread提供的网络世界入口 -net组件
作为一款在RTOS领域对网络支持很丰富的RT-Thread,对设备联网功能的支持的工具就是net组件。 位于/rt-thread/components/net路劲下,作为一款基础组件,env与Studio的工程配置项界面的配置项都依赖该目录下的Kconfig。 我们对网络功能的选择&am…...

分享一些有趣的MATLAB提示音(代码可直接复制)
先做一个声明:文章是由我的个人公众号中的推送直接复制粘贴而来,因此对智能优化算法感兴趣的朋友,可关注我的个人公众号:启发式算法讨论。我会不定期在公众号里分享不同的智能优化算法,经典的,或者是近几年…...

软件测试|selenium执行js脚本
JavaScript是运行在客户端(浏览器)和服务器端的脚本语言,允许将静态网页转换为交互式网页。可以通过 Python Selenium WebDriver 执行 JavaScript 语句,在Web页面中进行js交互。那么js能做的事,Selenium应该大部分也能…...

【源码复现】图神经网络之PPNP/APPNH
目录 1、论文简介2、论文核心介绍2.1、现有方法局限2.2、PageRank&Personalized PageRank2.3、PPNP&APPNP 3、源码复现3.1、模型总体框架3.2、PPNP3.3、APPNP3.4、MLP(两层) 1、论文简介 论文题目——《PREDICT THEN PROPAGATE: GRAPH NEURAL NETWORKS MEET PERSONALI…...

【算法与数据结构】131、LeetCode分割回文串
文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析:本题仍然使用回溯算法的一般结构。加入了一个判断是否是回文串的函数,利用起始和终止索引进…...

测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...

EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...

Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...

【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...