资讯专栏INFORMATION COLUMN

Generator和Async/Await

Eastboat / 1201人阅读

摘要:以往的异步方法无外乎回调函数和。出错了出错了总结接口遍历器对象除了具有方法,还可以具有方法和方法。函数调用函数,返回一个遍历器对象,代表函数的内部指针。

引言

接触过Ajax请求的会遇到过异步调用的问题,为了保证调用顺序的正确性,一般我们会在回调函数中调用,也有用到一些新的解决方案如Promise相关的技术。

在异步编程中,还有一种常用的解决方案,它就是Generator生成器函数。顾名思义,它是一个生成器,它也是一个状态机,内部拥有值及相关的状态,生成器返回一个迭代器Iterator对象,我们可以通过这个迭代器,手动地遍历相关的值、状态,保证正确的执行顺序。

Iterator接口

什么是Iterator接口

遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。

Iterator的作用

为各种数据结构,提供一个统一的、简便的访问接口

使得数据结构的成员能够按某种次序排列

ES6 创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。

Iterator实现
function makeIterator(array) {
  var nextIndex = 0;
  return {
    next: function() {
      return nextIndex < array.length ?
        {value: array[nextIndex++], done: false} :
        {value: undefined, done: true};
    }
  };
}

var it = makeIterator(["a", "b"]);

it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }
原生具备Iterator接口的数据结构

Array

Map

Set

String

TypedArray

函数的 arguments 对象

NodeList 对象

查看一下Map下面的所挂载的Iterator

let map = new Map();
console.log(map.__proto__);

输出结果:

clear:ƒ clear()
constructor:ƒ Map()
delete:ƒ delete()
entries:ƒ entries()
forEach:ƒ forEach()
get:ƒ ()
has:ƒ has()
keys:ƒ keys()
set:ƒ ()
size:(...)
values:ƒ values()
Symbol(Symbol.iterator):ƒ entries()
Symbol(Symbol.toStringTag):"Map"
get size:ƒ size()
__proto__:Object
如何为Object部署一个Iterator接口
function iteratorObject(obj){
    let keys = Object.keys(obj);
    let index = -1;
    return {
        next(){
            index++;
            return index

通过上面的方法可以简单的为Object部署了一个Iterator接口。

Generator函数

Generator是ES6的新特性,通过yield关键字,可以让函数的执行流挂起,那么便为改变执行流程提供了可能。

Generator语法

dome:

function * greneratorDome(){
    yield "Hello";
    yield "World";
    return "ending";
}
let grenDome = greneratorDome();
console.log(grenDome)

上面的代码中定义了一个Generator函数,获取到了函数返回的对象。下面是其输出结果。

原型链:

greneratorDome {}
__proto__:Generator
    __proto__:Generator
    constructor:GeneratorFunction {prototype: Generator, constructor: ƒ, Symbol(Symbol.toStringTag): "GeneratorFunction"}
    next:ƒ next()
    return:ƒ return()
    throw:ƒ throw()
    Symbol(Symbol.toStringTag):"Generator"
    __proto__:Object
[[GeneratorStatus]]:"suspended"
[[GeneratorFunction]]:ƒ* greneratorDome()
[[GeneratorReceiver]]:Window
[[GeneratorLocation]]:test.html:43
[[Scopes]]:Scopes[3]

通过上面的输出结果可以看的出来,沿着原型链向上查找就存在一个next方法,这个方法与Iterator接口返回的结果是大同小异的。

继续延续dome代码,并使用next方法向下执行。

function * greneratorDome(){
    yield "Hello";
    yield "World";
    return "Ending";
}
let grenDome = greneratorDome();
console.log(grenDome.next());
// {value: "Hello", done: false}
console.log(grenDome.next());
// {value: "World", done: false}
console.log(grenDome.next());
// {value: "Ending", done: true}
console.log(grenDome.next());
// {value: undefined, done: true}

在最开始的地方有提到过Generator函数,最后返回的是一个Iterator对象,这也就不难理解了。

异步的Generator

dome

function a (){
    setTimeout(() => {
        alert("我是后弹出");
    },1000)
}
function b (){
    alsert("我是先弹出");
}
function * grenDome (){
    yield a();
    yield b();
}
let gren = grenDome();
gren.next();
gren.next();
// 输出结果
// 我是先弹出
// 我是后弹出

结合Promise

function a (){
    return new Promise((resolve,reject) => {
        setTimeOut(() => {
            console.log(1)
            resolve("a");
        })
    })
}
function b (){
    return new Promise((resolve,reject) => {
         console.log(2)
        resolve("b");
    })
}
function * grenDome (){
    yield a();
    yield b();
    return new Promise((resolve,reject) => {
        resolve("grenDome内部")
    })
}
let gren = grenDome();

// console.log(gren.next())
// {value: Promise, done: false}
// console.log(gren.next())
// {value: Promise, done: false}
// console.log(gren.next())
// {value: Promise, done: true}
// console.log(gren.next())
// {value: undefined, done: true}

gren.next().value.then((res) => {
    console.log(res);
    // a函数
})
gren.next().value.then((res) => {
    console.log(res);
    // b函数
})
gren.next().value.then((res) => {
    console.log(res);
    // grenDome内部
})
// 输出结果
// a
// b
// grenDome内部

在上面的代码中有一点是需要注意的,在grenDome函数里面最后return出去了一个Promise,但是在输出的时候虽然done属性已经为true但是value里面仍然会存有一个promise对象,实际上done表示的是对应yield关键字的函数已经遍历完成了。

Async/Await

Async/awaitJavascript编写异步程序的新方法。以往的异步方法无外乎回调函数和Promise。但是Async/await建立于Promise之上,换句话来说使用了Generator函数做了语法糖。

async函数就是隧道尽头的亮光,很多人认为它是异步操作的终极解决方案。

什么是Async/Await

async顾名思义是“异步”的意思,async用于声明一个函数是异步的。而await从字面意思上是“等待”的意思,就是用于等待异步完成。并且await只能在async函数中使用。

Async/Await语法
function timeout(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
};
async function asyncPrint(value, ms) {
  await timeout(ms);
  console.log(value);
};
asyncPrint("hello world",2000);
// 在2000ms之后输出`hello world`
返回Promse对象

通常asyncawait都是跟随Promise一起使用的。为什么这么说呢?因为async返回的都是一个Promise对象同时async适用于任何类型的函数上。这样await得到的就是一个Promise对象,如果不是Promise对象的话那async返回的是什么就是什么。

async function f() {
  return "hello world";
}
f().then(v => console.log(v));
// hello world

async函数返回一个Promise对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

function a(){
    return new Promise((resolve,reject) => {
        console.log("a函数")
        resolve("a函数")
    })
}
function b (){
    return new Promise((resolve,reject) => {
        console.log("b函数")
        resolve("b函数")
    })
}
async function dome (){
    let A = await a();
    let B = await b();
    return Promise.resolve([A,B]);
}
dome().then((res) => {
    console.log(res);
});
执行机制

前面已经说过await是等待的意思,之后等前面的代码执行完成之后才会继续向下执行。

function a(){
    return new Promise((resolve,reject) => {
        resolve("a");
        console.log("a:不行")
    })
}
function b (){
    return new Promise((resolve,reject) => {
        resolve("b");
        console.log("b:不行");
    })
}
async function dome (){
    await a();
    await b();
    console.log("虽然我在后面,但是我想要先执行可以么?")
}
dome();
// 输出结果
// a:不行
// b:不行
// 虽然我在后面,但是我想要先执行可以么?

另外一个列子

function timeout1(ms) {
  return new Promise((resolve) => {
    setTimeout(() => {
        console.log("timeout1")
        resolve();
    },ms);
  });
};
function timeout2(ms) {
  return new Promise((resolve) => {
    setTimeout(() => {
        console.log("timeout2");
        resolve();
    },ms);
  });
};
async function asyncPrint() {
  await timeout1(1000);
  await timeout2(2000);
};
asyncPrint().then((res) => {
    console.log(res);
}).catch((err) => {
    console.log(err)
})
// 1s 后输出timeout1
// 3s 后输出timeout2
// undefined
async、await错误处理

JavaScript异步请求肯定会有请求失败的情况,上面也说到了async返回的是一个Promise对象。既然是返回一个Promise对象的话那处理当异步请求发生错误的时候我们就要处理reject的状态了。

在Promise中当请求reject的时候我们可以使用catch。为了保持代码的健壮性使用async、await的时候我们使用try catch来处理错误。

async function f() {
  await Promise.reject("出错了");
  await Promise.resolve("hello world");
}

async function b() {
    try {
      await f();
    } catch(err) {
     console.log(err);
    }
}
b();
//  出错了
总结
Iterator接口

遍历器对象除了具有next方法,还可以具有return方法和throw方法。如果你自己写遍历器对象生成函数,那么next方法是必须部署的,return方法和throw方法是否部署是可选的。

Es6提供很多API都是基于Iterator接口,比如解构,for...of循环,拓展运算等。

Generator函数

调用Generator函数,返回一个遍历器对象,代表Generator函数的内部指针。以后每次调用遍历器对象的next方法,就会返回一个有着valuedone两个属性的对象。
value属性表示当前的内部状态的值,是yield语句后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束

Async/Await

Async/await是近些年来JavaScript最具革命性的新特性之一。他让读者意识到使用Promise存在的一些问题,并提供了自身来代替Promise的方案。他使得异步代码变的不再明显,我们好不容易已经学会并习惯了使用回调函数或者then来处理异步。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/108516.html

相关文章

  • JavaScript异步编程:GeneratorAsync

    摘要:从开始,就在引入新功能,来帮助更简单的方法来处理异步编程,帮助我们远离回调地狱。而则是为了更简洁的使用而提出的语法,相比这种的实现方式,更为专注,生来就是为了处理异步编程。 从Promise开始,JavaScript就在引入新功能,来帮助更简单的方法来处理异步编程,帮助我们远离回调地狱。 Promise是下边要讲的Generator/yield与async/await的基础,希望你已...

    leon 评论0 收藏0
  • 理解 async/await

    摘要:而函数的命令后面则可以是或者原始类型的值,,,但这时等同于同步操作返回值是。抛出的错误而会被方法回调函数接收到。 ES7 提出的async 函数,终于让 JavaScript 对于异步操作有了终极解决方案。No more callback hell。async 函数是 Generator 函数的语法糖。使用 关键字 async 来表示,在函数内部使用 await 来表示异步。想较于 G...

    kid143 评论0 收藏0
  • JS中的async/await -- 异步隧道尽头的亮光

    摘要:结果输出可以看出函数返回的是一个对象,如果函数中一个直接量,函数会封装成对象返回,而如果没有返回值时,函数会返回在没有结合时,函数会立即执行,返回一个对象。 JS中的异步操作从最初的回调函数演进到Promise,再到Generator,都是逐步的改进,而async函数的出现仿佛看到了异步方案的终点,用同步的方式写异步。showImg(https://segmentfault.com/i...

    jaysun 评论0 收藏0
  • ES6 系列之我们来聊聊 Async

    摘要:标准引入了函数,使得异步操作变得更加方便。在异步处理上,函数就是函数的语法糖。在实际项目中,错误处理逻辑可能会很复杂,这会导致冗余的代码。的出现使得就可以捕获同步和异步的错误。如果有错误或者不严谨的地方,请务必给予指正,十分感谢。 async ES2017 标准引入了 async 函数,使得异步操作变得更加方便。 在异步处理上,async 函数就是 Generator 函数的语法糖。 ...

    Songlcy 评论0 收藏0
  • 简单理解Generator自执行及asyncawait语法原理

    摘要:为了更加方便的处理异步操作问题,现在最新的前端框架生态都开始用上了和,有的甚至已经开始使用最新的语法了,这两样都是基于自动执行的原理。这里就简单理解下自执行及语法原理一函数函数指的是能将执行结果传入回调函数,并将该回调函数返回的函数。 为了更加方便的处理异步操作问题,现在最新的前端框架生态都开始用上了Generator和yield,有的甚至已经开始使用最新的async、await语法了...

    stackfing 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<