六维教程

ES6 异步编程

请求数据、读写文件、定时任务都要异步,但异步是 JavaScript 里最绕也最重要的概念。

为什么需要异步

JS 是单线程语言,即一次只能做一件事,如果要做一件耗时的事(如请求接口)必须等它跑完才能继续,显示的就是页面卡死。

异步就是:先把耗时的任务丢出去,注册一个”完成后的回调”,主线程继续往下走,等任务完成再执行回调。

// 示例:setTimeout 就是最简单的异步
console.log('开始');
setTimeout(function () {
  console.log('3 秒后执行');
}, 3000);
console.log('立刻执行');
// 输出:开始 → 立刻执行 →(3秒后)3 秒后执行

回调地狱

早期异步全靠回调。多个异步按顺序执行时,只能层层嵌套:

// ES5:回调层层嵌套(示意)
doA(function () {
  doB(function () {
    doC(function () {
      console.log('全部完成');
    });
  });
});

问题:理解难、维护难、报错排查难,人称”回调地狱”。

嵌套的根源,在于回调是”传进去”的。函数要跑完内部逻辑,再在深处调用你给它的那个函数,层层传入就层层加深。

要打破嵌套,就得换一种思路,把结果从”往深处传”改成”往外返回”。即让异步函数自己返回一个带状态的对象,先给你一个”还没完成”的占位,等任务真正跑完后,再通过对象身上的方法把下一步接上去。ES6 的 Promise 正是这个对象。

Promise:把异步”链式化”

ES6 引入 Promise,用链式写法替代嵌套,把回调地狱”拍平”。

基本形态

// 示例:创建 Promise
const promise = new Promise(function (resolve, reject) {
  // 模拟异步操作:1 秒后成功
  setTimeout(function () {
    resolve('数据拿到了');   // 成功时调用,并把结果"传出去"
    // reject('出错了');      // 失败时调用
  }, 1000);
});

resolve(成功)和 reject(失败)是 Promise 提供的两个函数,分别表示”成功/失败”。

消费结果:then / catch

promise
  .then(function (data) {   // resolve 时执行
    console.log(data);
  })
  .catch(function (err) {   // reject 时执行
    console.log('出错了:', err);
  });

链式调用:解决回调地狱

.then() 里返回另一个 Promise,可以继续 .then,形成”扁平的链”:

// 示例
getUser()
  .then(function (user) {
    return getOrders(user.id);   // 返回新 Promise 继续链
  })
  .then(function (orders) {
    console.log('订单', orders);
  })
  .catch(function (err) {
    console.log('出错', err);
  });

比起层层嵌套,链式明显更好读。这也真正解决了回调地狱。

async / await:异步代码像同步一样写

ES2017(属于现代 JS)推出的语法糖,让 Promise 写起来更直观:

// 示例:用 async/await 重写 Promise 链
async function getData() {
  try {
    const user = await getUser();       // 等它完成,拿到结果
    const orders = await getOrders(user.id);
    console.log(orders);
  } catch (err) {
    console.log('出错', err);
  }
}

三条规则:

  1. async 标记的函数内部才能用 await
  2. await 表达式暂停函数,等 Promise 出结果,返回值就是 resolve 的值。
  3. try/catch 捕获 reject 的错误。

async 函数本身总是返回一个 Promise

一个完整对比

// Promise 写法
function load() {
  return getUser()
    .then(function (u) { return getOrders(u.id); })
    .then(function (o) { console.log(o); })
    .catch(function (e) { console.log(e); });
}

// async/await 写法(推荐日常使用)
async function load() {
  try {
    const u = await getUser();
    const o = await getOrders(u.id);
    console.log(o);
  } catch (e) {
    console.log(e);
  }
}

两种写法底层一样:async/await 只是 Promise 的”语法糖”。

三个异步 API 速览

日常写请求、文件时高频遇到:

形式 使用场景
Promise 需要手动控制成功/失败
Promise.all([p1,p2]) 多个异步并行执行,全完成才返回
async/await 团队主要写法,最直观推荐
// 示例:Promise.all 并行请求
const [a, b] = await Promise.all([getA(), getB()]); // 同时发起,同时等

速查卡片

概念 作用
回调 异步执行完调用的函数
回调地狱 层层嵌套难维护
Promise 链式处理异步,then/catch
async/await Promise 的语法糖,顺序更直观
try/catch 捕获 async 里的错误

至此 ES6 的核心语法你已基本掌握。异步代码的演进(回调 → Promise → async/await)是通往工程化的一道重要关口,也是后续写请求、读写数据时每天都要用到的能力。

上一篇
ES6 Class 语法
下一篇
ES6 模块化