前言
随着前端技术的发展,在开发中,Async/Await的应用越来越高。本篇文章从Generator函数到Generator自动执行器再到async/await语法糖,深入理解它们其中的原理。
一、Generator函数
序号 | 资料名称 | 链接 | 备注 |
---|---|---|---|
1 | 可迭代协议 迭代器协议 | https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols#%E5%8F%AF%E8%BF%AD%E4%BB%A3%E5%8D%8F%E8%AE%AE | 介绍可迭代协议和迭代器协议 |
2 | 介绍 | https://cnodejs.org/topic/58ddd7a303d476b42d34c911 | 介绍协程 |
3 | co源码 | https://github.com/tj/co |
1. 协程
协程是一种基于线程之上(协程在线程内部),但又比线程更加轻量级的存在,这种由程序员自己写程序来管理的轻量级线程叫做【用户空间线程】,具有对内核来说永不可见的特性(如果系统知道了协程的存在,就会将其注册成一个线程)。
像一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程。
协程的执行顺序可能千变万化,但协程的执行权切换只会发生在用户明确放弃执行权之后。
2. 协程的Generator函数实现
function* gen(x){
var y = yield x+2;
return y;
}
var g = gen(1); //gen(1)返回的是一个内部的指针对象,并非执行这个函数
g.next(); //{value: 3, done: false}
g.next(); //{value: undefined, done: true}
3. Generator函数数据交换和错误处理
//数据交换
function* gen(x){
var y = yield x+2; //第一阶段
return y; //第二阶段
}
var g = gen(1);
g.next(); //{value: 3, done: false} //执行第一阶段(此时不需要传参)
g.next(2); //{value: 2, done: true} //执行第二阶段(此时传入的参表示上一次异步执行的结果)
//错误处理
function* gen(x){
try{
var y = yield x+2;
} catch(e){
console.log(e);
}
return y;
}
var g = gen(1);
g.next();
g.throw("出错了");
4. 异步封装与执行
var fetch = require('node-fetch');
funtion* gen(){
var url = 'https://api.github.com/users/github';
var result = yield fetch(url); //fetch(url)返回的是一个Promise对象
console.log(result.bio);
}
var g = gen();
var result = g.next(); //执行第一阶段
result.value.then(function (data) {
return data.json();
}).then(function (data) {
g.next(data); //执行第二阶段
})
以上我们可以看到,“以同步代码的形式书写异步代码”。
二、基于Thunk函数的Generator自动执行器
当Generator函数中存在多个异步操作的话,我们需要操作next()这个方法手动去改变内部的指针(书写起来比较麻烦),如果当异步操作有了结果之后可以自动交回执行权,书写起来就会方便很多。
1. 将异步操作包裹成promise对象,通过then方法交回执行权(半自动)
var fetch = require('node-fetch');
funtion* gen(){