乐趣区

关于前端:前端JavaScript异步编程async函数

基本概念

传统 JavaScript 异步编程的模式大体分以下几种。

  • 回调函数
  • 事件监听
  • 公布 / 订阅
  • Promise 对象

异步

一个工作间断的执行就叫做同步。如果将工作为分两步执行,执行完第一步,转而执行其它工作,等做好了筹备,再回过头执行第二步,这种不间断的执行就叫做异步。

回调函数

回调函数就是把第二步执行的工作独自写在一个函数外面,等到从新执行这个工作的时候,就间接调用这个函数。回调函数的英语叫callback,直译过去就是 ” 从新调用 ”。

loadData(url, function (data) {console.log(data);
});

留神:工作第一步执行完后,所在的上下文环境就曾经完结了,所以咱们个别会应用var that = this 将第一步执行时的this 指向进行保留,以便回调时应用。

function Api(url) {
    this.url = url;
    this.request = function () {
          var that = this
        setTimeout(function () {console.log('url', that.url)
        }, 1000)
    }
}

var api = new Api('http://127.0.0.1')
api.request() // url http://127.0.0.1

Generator 函数

异步编程解决方案中,ES6 还提供了 Generator 函数。它其实是一个一般函数,独有特色

  1. function关键字与函数名之间有一个星号;
  2. 函数体外部应用 yield 表达式,定义不同的外部状态。

function* statusGenerator() {
  yield 'pending';
  yield 'running';
  return 'end';
}

var st = statusGenerator();

下面代码 statusGenerator 函数返回一个迭代器对象,函数内定义了三个状态,调用迭代器 next 办法指向下一个状态。

st.next() // { value: 'pending', done: false}
st.next() // { value: 'running', done: false}
st.next() // { value: 'end', done: false}

yield 表达式

yield表达式就是暂停标记。迭代器执行 next 时。

  1. 遇到 yield 表达式,就暂停执行前面的操作,并将 yield 前面的那个表达式的值作为返回的对象的 value 属性值。
  2. 下一次调用 next 办法时,再持续往下执行,直到遇到下一个 yield 表达式。
  3. 如果没有再遇到新的 yield 表达式,就始终运行到函数完结,直到 return 语句为止,并将 return 语句前面的表达式的值,作为返回的对象的 value 属性值。
  4. 如果该函数没有 return 语句,则返回的对象的 value 属性值为undefined

for…of 循环

咱们也能够应用 for...of进行遍历。

function* statusGenerator() {
  yield 'pending';
  yield 'running';
  return 'end';
}

var st = statusGenerator();
for(let v of st){console.log(v)// pending running
}

Generator 的利用

协程

协程的意思是多个线程相互合作,实现异步工作。它是一些编程语言的异步编程计划,比方 go 中的协程实现goroutine。协程序执行的大抵流程如下:

  1. 协程 A 开始执行。
  2. 协程 A 执行到一半,进入暂停,执行权转移到协程B
  3. (一段时间后)协程 B 交还执行权。
  4. 协程 A 复原执行。

JavaScript 中的协程实现Generator 函数,它能够在指定的中央(yield)交出函数的执行权(即暂停执行),而后期待执行权交还继续执行。

比方:咱们实现一个倒计时函数,工作就绪后期待倒计时,一起执行。

function* countdown(num, running) {
    do {yield num--} while (num > 0)
    running()}

const tasks = []
const ct = countdown(3, function () {console.log('start run task')
    for (let task of tasks) {task()
    }
})

for (let i = 0; i < 3; i++) {tasks.push(function () {console.log('task'+ i)
    })
    ct.next()}

ct.next()

一个异步申请封装

var fetch = require('node-fetch');

function* request(){
  var url = 'xxxx';
  var user = yield fetch(url); // 返回 promise 对象,data: {'user':'xxxx'}
  console.log(user);
}

var req = request();
var result = req.next();

result.value.then(function(data){return data.user}).then(function(user){req.next(user);                        // 将 user 信息传到 request()函数,被 user 变量接管。});

async 函数

ES2017 引入了 asyncawait 关键字,应用这对关键字,能够用更简洁的形式写出基于Promise 的异步行为,而无需刻意地链式调用promise

async申明的函数个别称为 async 函数。能够把 async 看作是 Generator 的语法糖,因为它们实质的作用一样。

Generator 写法

const loadData = function (url) {return new Promise(function (resolve, reject) {resolve(data);
    });
};

const request = function* () {const user = yield loadData('https://user');
    const goods = yield loadData('https://goods');
    console.log(user, goods);
};

async 写法

const loadData = function (url) {return new Promise(function (resolve, reject) {resolve(data);
    });
};

const request = async function () {const user = await loadData('https://user');
    const goods = await loadData('https://goods');
    console.log(user, goods);
};

根本用法

async函数会返回一个 Promise 对象。当函数执行的时候,一旦遇到 await 就会先返回,等到异步操作实现,再接着执行函数体内前面的语句。

function timeout(ms) {return new Promise((resolve) => {setTimeout(resolve, ms);
  });
}

async function asyncPrint(value, ms) {await timeout(ms);
  console.log(value);
}

asyncPrint('hello', 50);

async函数外部 return 语句返回的值,会成为 then 办法回调函数的参数。

async function hello() {return 'hello';}

hello().then(v => console.log(v))
// "hello"

async函数外部抛出谬误,会导致返回的 Promise 对象变为 reject 状态。抛出的谬误对象会被 catch 办法回调函数接管到。

async function hello() {throw new Error('Error');
}

hello().then(v => console.log(v),
  e => console.log(e)
) // //Error: Error

await 命令

个别状况下,await前面都是一个 Promise 对象,返回该对象的后果。如果不是 Promise 对象,就间接返回对应的值。

async function hello() {return await 'hello'}
hello().then(v => console.log(v)) // hello

async function hello() {return await Promise.resolve('hello');
}
hello().then(v => console.log(v)) // hello

错误处理

如果 await 前面的异步操作出错,那么等同于 async 函数返回的 Promise 对象被reject

async function hello() {await new Promise(function (resolve, reject) {throw new Error('error');
  });
}

hello()
.then(v => console.log(v))
.catch(e => console.log(e))
// Error:error

所以最好把 await命令放在 try...catch 代码块中。

async function hello() {
    try {await new Promise(function (resolve, reject) {throw new Error('error');
        });
    } catch(e) {console.log('err:', e) // error
    }
    return await('hello');
}

const  h = hello();
h.then((v) => {console.log(v)}) // hello

小结

本文记录了 JavaScript 异步编程中的一些形式,Generator函数和 asyncawait 语法,欢送留言交换。

退出移动版