用 async/await 改写异步代码,让代码更直观

zpfei
返回值只要是 Promise, 就可用 async/await 处理异步

服务端代码见文末

业务用例

登录
1.认证 获取 token
2.用 token 获取角色 id
3.用 token 和角色 id 获取菜单

类似 fetch 的异步请求 如 axios window.fetch umi-request dva/fetch 等

  • 基本使用形式
new Promise(请求1)
    .then(请求2(请求结果1))
    .then(请求3(请求结果2))
    .then(请求4(请求结果3))
    .then(请求5(请求结果4))
    .catch(处理异常(异常信息))
  • 改写

直接在浏览器 console 可运行以下代码

async function login() {
  const baseUrl = 'http://127.0.0.1:3005';

  const { token } = await fetch(`${baseUrl}/auth`, {
    method: 'POST',
    data: { username: 'test', password: 'xxxxxxx' },
  }).then(v => v.json()); // 这里后面在then一次是因为fetch直接返回的不是Promise 而是Response 而response.json()才会返回Promise
  console.log('1.token->', token);

  const roles = await fetch(`${baseUrl}/roles?token=${token}`).then(v => v.json());
  console.log('2.roles->', roles);

  const menus = await fetch(`${baseUrl}/menus?token=${token}&roles=${roles}`).then(v => v.json());
  console.log('3.menus->', menus);
}

callback 方式返回处理, 以小程序为例,其他回调同理

  • 基本使用形式
wx.request({
    url: '请求一',
    data: {
        x: '',
        y: ''
    },
    header: {
        'content-type': 'application/json'
    },
    success (res) {
        wx.request({
            url: '请求二',
            data: res.data,
            header: {
                'content-type': 'application/json'
            },
            success (res) {
                ......
            }
        })
    }
})
  • 改写

1.用 Promise 封装一个请求函数

const request = (url, options = { headers: {} }) => {
  const baseUrl = 'http://127.0.0.1:3005';

  const headers = {
    'content-type': 'application/json',
    ...options.headers,
  };

  const method = options.method || 'GET';
  const data = options.data || '';

  return new Promise((resolve, reject) => {
    wx.request({
      url: baseUrl + url,
      data,
      method,
      header: headers,
      success(res) {
        resolve(res);
      },
      fali(err) {
        reject(err);
      },
    });
  });
};

2.业务代码

const login = async () => {
  const {
    data: { token },
  } = await request('/auth', { method: 'POST', data: { username: 'test', password: 'xxxxxxx' } });
  console.log('1.token->', token);

  const { data: roles } = await request('/roles', { data: { token } });
  console.log('2.roles->', roles);

  const { data: menus } = await request('/menus', { data: { token, roles } });
  console.log('3.menus->', menus);
};

定时器

  • 改写
const sleep = time => {
  return new Promise(resolve => {
    console.log(`等待${time}秒...`);
    setTimeout(() => {
      resolve(true);
    }, time * 1000);
  });
};
  • 使用
const request = require('axios');
request.defaults.baseURL = 'http://127.0.0.1:3005';

async function login() {
  const {
    data: { token },
  } = await request('/auth', { method: 'POST', data: { username: 'test', password: 'xxxxxxx' } });
  console.log('1.token->', token);
  await sleep(2);

  const { data: roles } = await request('/roles', { params: { token } });
  console.log('2.roles->', roles);
  await sleep(2);

  const { data: menus } = await request('/menus', { params: { roles, token } });
  console.log('3.menus->', menus);
}

任务间隔定时器

传统 setInterval 是每个一段固定的时间调用一次任务,不关心任务时长,期望一个关心任务时长的 interval
  • 以任务为间隔的定时器,关心任务时长,即等到所有任务执行完成后,再间隔一定时间,开始下一次任务
const interval = async (task, time, id) => {
  while (true) {
    if (!global[id]) break;
    await new Promise(res => {
      res(task());
    });
    await sleep(time);
  }
};

const addInterval = (task, time = 5) => {
  const id = Symbol();
  global[id] = true;
  setTimeout(() => {
    interval(task, time, id);
  }, 0);
  return id;
};

const delInterval = id => {
  global[id] = false;
};
  • 使用

以上面的login为例

const id = addInterval(login, 2); //任务间隔2s

setTiemt(() => {
  // 在某个时间段或者某个操作时停止循环
  delInterval(id);
}, xxx);

服务端代码示例

const app = require('express')();
const bodyParser = require('body-parser');
const { sleep } = require('./tools');

app.use(bodyParser.json());
app.use(
  bodyParser.urlencoded({
    extended: true,
  }),
);

app.all('*', function(req, res, next) {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Headers', 'X-Requested-With');
  res.header('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS');
  res.header('X-Powered-By', ' 3.2.1');
  res.header('Content-Type', 'application/json;charset=utf-8');
  next();
});

app.post('/auth', (req, res) => {
  const { username } = req.body;
  console.log(username);
  res.send({
    id: 1,
    name: username,
    age: 18,
    token: `absfgsakjdhjskahdsklh`,
  });
});

app.post('/auth/slow', async (req, res) => {
  const { username } = req.body;
  console.log(username);
  await sleep(4);
  res.send({
    id: 1,
    name: username,
    age: 18,
    token: `absfgsakjdhjskahdsklh`,
  });
});

app.get('/roles', (req, res) => {
  res.send([1, 2, 3]);
});

app.get('/roles/slow', async (req, res) => {
  await sleep(4);
  res.send([1, 2, 3]);
});

app.get('/menus', (req, res) => {
  res.send([
    { name: '首页', path: '/' },
    { name: '用户管理', path: '/users' },
  ]);
});

app.get('/slow', async (req, res) => {
  const second = Math.round(Math.random() * 7) + 1;
  await sleep(second);
  // res.append('Access-Control-Allow-Origin','*')
  res.send({
    code: 0,
    data: Date.now(),
    msg: 'ok',
  });
});

app.listen(3005, () => {
  console.log(`server run time: ${Date.now()}`);
});
阅读 702

往事如风~

95 声望
3 粉丝
0 条评论
你知道吗?

往事如风~

95 声望
3 粉丝
宣传栏