异步:
同步是像排队一样,只有前一个任务执行完,才能执行下一个。而异步就是打乱这种顺序,当前一个任务宕机了,被挂起了,可以跳过它,接着往下执行。或者干脆将可能会失败或卡住的这种任务放到最后执行。这就叫做异步
异步任务
异步任务指的是,不进入主线程、而进入”任务队列”(task queue)的任务,只有”任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
异步任务还可分为宏任务和微任务
宏任务和微任务是异步里 api 的分类;
1.微任务是**es 提供的 api,**常用的有: promise 实例.then().catch() & 异步函数中 await 行后面的代码 ;
2**.宏任务是浏览器提供的 api**,常用的有 :定时器 & ajax & dom 事件;
举个栗子:
1 | async function fn1() { |
第 1 轮宏任务 start,执行 all 同步代码
async1 start
async2
promise start
第 1 轮宏任务 end,执行第 1 轮微任务
async1 end
promise then
第 2 轮宏任务
在编程中,如请求资源失败,若没有使用异步编程,页面就会卡死,这非常不友好
那么,我们如何异步呢?
1.回调函数+定时器异步(宏任务)
这是异步编程最基本的方法。
假定有两个函数 f1 和 f2,后者等待前者的执行结果
如果 f1 是一个很耗时的任务,可以考虑改写 f1,把 f2 写成 f1 的回调函数。
然后,使用 setTimeout 方法,将可能造成阻塞的部分作为异步宏任务执行(setTimeout 是浏览器 api,是宏任务)
1 | function f1(callback) { |
执行代码就变成下面这样:
1 | f1(f2); |
但是,逻辑比较复杂的时候回调会层层嵌套形成回调地狱,比利于理解和维护
我们就需要更清晰的方法进行异步编程–
2.Promise
它是为了解决异步处理回调地狱而产生的,在语法上它是一个构造函数,接受一个回调函数作为参数,允许我们在其中处理业务逻辑,这个回调函数接收两个参数分别是 resolve 和 reject,二者都是由 JavaScript 引擎提供,不用自己传入。
通过 new Promise()生成的 Promise 实例对象,有三种状态
Promise 对象的三种状态
- pending 初始状态
- fulfilled 成功状态
- rejected 失败状态
resolve 和 reject
- resolve 方法的作用是,将 Promise 对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),我们必须在异步操作成功时调用,然后将异步操作的结果,作为参数传递出去
- reject:让 Promise 对象的状态从 pending==>rejected ,也需要以参数形式将异步处理的结果传出
- 所谓传递到外面,指的是就是传递到回调函数的外部的–刚 new 出来的 Promise 实例对象的.then 方法);
.then 方法
- 它默认传入两个回调函数作为参数(也可以只写一个成功的),第一个用于接收
resolve()
传入的数据,第二个用于接收reject()
传入的参数(一般为 Error 信息)
1 | pro.then( |
3.Generator 生成器
生成器本质上是一种特殊的迭代器,使用生成器异步编程往往需要配合 Promise 使用。
举个雷子:依次读取两个文件,
1 | var fs = require("fs"); |
4.async/await 基本用法
async 函数就是 Generator 函数的语法糖
注意:
async 异步函数本身是同步的,await 所在语句后面的代码才是**异步**执行,(微任务)
上面生成器的代码写成 async 函数,就是下面这样。
1 | var asyncReadFile = async function () { |
一比较就会发现,async 函数就是将 Generator 函数的星号(*)替换成 async,将 yield 替换成 await,仅此而已。
另一个注意点:
await 命令后面的 Promise 对象,运行结果可能是 rejected,所以最好把 await 命令放在 try…catch 代码块中。
1 | async function myFunction() { |
参考:
谈谈我对 Promise 的理解(一)– Macchiato_go | 简书
评论