ES8 async & await

Generator 可以解决异步的问题,但却需要多一个帮助函数,如何顺畅的编写异步代码呢? ES6 推出 async、await 来解决,来看看。

简单谈谈

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

既然是语法糖,那么先来对比下 async 函数和 Generator 函数的区别。以下是分别用 asyncGenerator 写的异步操作:

const fs = require('fs');

const readFile = function (fileName) {
    return new Promise((resolve, reject) => {
        fs.readFile(fileName, (error, data) => {
            if (error) return reject(error);
            resolve(data);
        });
    });
};

// Generator 的写法 & 执行
const gen = function* () {
    const f1 = yield readFile('/etc/fstab');
    const f2 = yield readFile('/etc/shells');
    console.log(f1.toString());
    console.log(f2.toString());
}
autoGen(gen())

// async 的写法 & 执行
const asyncReadFile = async function () {
    const f1 = await readFile('/etc/fstab');
    const f2 = await readFile('/etc/shells');
    console.log(f1.toString());
    console.log(f2.toString());
}
asyncReadFile();

**注:**不清楚 autoGen 是什么的可以查看上一节

上诉代码,主要有以下几点区别:

类型Generatorasync函数标识*async暂停语句yieldawait执行方法调用自动执行方法(autoGen)直接调用

如果能理解 Generator 函数在异步中的使用的话,那么我相信,async 函数对于我们来说,简直简单的不能在简单了。那么既然用 Generator 函数能解决的东西,为什么又要多一个 async

和大多数语法糖出现一样,async 主要是为了更好的语义和更广的适用性。主要表现在以下几点:

  1. asyncawait ,比起 *yield ,语义更清楚了。 async 表示函数里有异步操作, await 表示紧跟在后面的表达式需要等待结果。
  2. yield 命令后面只能是 Thunk 函数或 Promise 对象,而 async 函数的 await 命令后面,可以是 Promise 对象或原始类型的值(数值、字符串和布尔值,等同于同步操作)。
  3. async 函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。可以用 then 方法指定下一步的操作。

基本用法

async

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

async function asy(){
    return 'this is async fun';
};

asy().then(res => console.log(res));
// this is async fun

上述例子可见 async 函数最终返回 Promise 其结果为函数的返回值。

async 函数有多种形式(与基本函数的表现形式一致):

// 函数声明
async function foo() {};

// 函数表达式
const foo = async function () {};

// 对象的方法
let obj = { async foo() {} };
obj.foo().then(/* ... */);

// Class 的方法
class Storage {
    constructor() {
        this.cachePromise = caches.open('avatars');
    }
    
    async getAvatar(name) {
        const cache = await this.cachePromise;
        return cache.match(`/avatars/${name}.jpg`);
    }
}

const storage = new Storage();
storage.getAvatar('jake').then(/* ... */);

// 箭头函数
const foo = async () => {};

async 函数的错误处理

async 内部有未捕捉的异常时, async 函数返回的 Promise 将会是 reject 状态。

async function f() {
    throw new Error('出错了');
}

f().then(
    v => console.log(v),
    e => console.log(e)
);
// Error: 出错了

await

正常情况下,await 命令后面是一个 Promise 对象。如果不是,会被转成一个立即 resolvePromise 对象。

async function f() {
    let a = await 123;
    return a;
}

// 等同于
async function f() {
    let a = await Promise.resolve(123);
    return a;
}

f().then(v => console.log(v));
// 123

await 命令后跟的 Promise 状态变更为 fulfilled(resolve) 时,a 会被赋值,a 的值即为异步处理的结果。

当然如果 await 命令后的 Promise 的状态变更为 reject 并且没有对该 Promise 进行 catch 处理,那么 async 函数结束执行返回的 Promise 也将会变成 reject 状态。

async function f() {
    await Promise.reject('出错了');
    await Promise.resolve('hello world');   // 不会执行
}

f();
.then(v => console.log(v));
.catch(e => console.log(e));
// 出错了

错误处理

有两种方式可以在不影响 async 函数执行的情况下进行捕捉异常:

try...catch

async function f() {
  try {
    await Promise.reject('出错了');
  } catch(e) {
  }
  return await Promise.resolve('hello world');
}

f();
.then(v => console.log(v));
// hello world

Promise.catch

async function f() {
  await Promise.reject('出错了').catch(e => console.log(e));
  return await Promise.resolve('hello world');
}

f();
.then(v => console.log(v));
// 出错了
// hello world

使用注意点

  1. await 命令后面的 Promise 对象,运行结果可能是 rejected ,所以最好把 await 命令放在 try...catch 代码块中,或直接后面跟一个 catch 方法进行异常捕捉。
  2. 多个 await 命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。
  3. await 命令只能用在 async 函数之中,如果用在普通函数,就会报错。

关于第二点,由于 async 函数的设计就是以一种同步的写法写异步函数,所以 await 是一个一个执行的,有顺序的关系,所以当一个函数体内有两个互相无关的异步请求的话,直接写 await 是达不成同时进行异步操作的效果的,所以得先使用 Promise.all 进行一层包装。

let [foo, bar] = await Promise.all([getFoo(), getBar()]);

Read more

Gitlab 搭建

Gitlab 搭建

为什么? 想要自己搭建一个代码仓库无非是以下几点原因: 1. 公司内部项目 2. 自己的项目,但不适合在公网 3. 大部分的 git 仓库虽然有私有服务,但价格都不便宜,甚至不如一台云服务器来的便宜 配置及安装文档 Gitlab * 由于 gitlab 会用到 22 端口端口转发的化就走不了 git clone 的默认配置,且占用内存较高,不推荐使用 docker 进行部署; * 由于 gitlab 自带 nginx 默认情况下会与属主机的 nginx 冲突,因此推荐只使用 gitlab 自带的 nginx 进行端口转发; 最小化配置 # path /etc/gitlab/gitlab.rb external_url 'http://git.

By breeze
NPM 私服

NPM 私服

verdaccio 私服搭建,搭建过程中的一些问题以及解决: * docker compose 启动后,可能会报错:配置找不到,添加 config.yaml 文件到映射的 ./conf 目录即可,config.yaml 具体内容可在官网找到,下方也有最小化配置。 services: verdaccio: image: verdaccio/verdaccio container_name: 'verdaccio' networks: - node-network environment: - VERDACCIO_PORT=4873 - VERDACCIO_PUBLIC_URL=http://npm.demo.com ports: - '10001:4873'

By breeze