zoukankan      html  css  js  c++  java
  • async函数学习笔记

    含义

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

    
    const fs = require('fs')
    const readFile = function(fileName){
        return new Promise(function(resolve,reject){
            fs.readFile(fileName,function(error,data){
                if(error) return reject(error);
                resolve(data);
            })
        })
    }
    const gen = function*(){
        const f1 = yield readFile('/etc/fstab');
        const f2 = yield readFile('/etc/shells');
        console.log(f1.toString());
        console.log(f2.toString());
    }
    //上面代码的函数gen可以写成async函数,就是下面这样。
    const asyncReadFile = async function(){
        const f1 = await readFile('/etc/fstab');
        cosnt f2 = await readFile('/etc/shells');
        console.log(f1.toString());
        console.log(f2.toString());
    }
    

    一比较就会发现,async函数就是将Generator函数的星号替换成async,将yield替换成await,仅此而已。
    async函数对Generator函数的改进,体现在以下四点。
    (1)内置执行器
    Generator函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。

    
    asyncReadFile()
    

    上面的代码调用了asyncReadFile函数,然后它就会自动执行,输出最后结果。这完全不像Generator函数,需要调用next方法,或则用co模块,才能真正执行,得到最后结果。
    (2)更好的语义
    async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
    (3)更广的适用性
    co模块约定,yield命令后面只能是Thunk函数或Promise对象,而async函数的await命令后面,可以是Promise对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。
    (4)返回值是Promise
    async函数的返回值是Promise对象,这比Generator函数的返回值Iterator对象方便多了。你可以用then方法指定下一步的操作。
    进一步说,async函数完全可以看作多个异步操作,包装成的一个Promise对象,而await命令就是内部then命令的语法糖。

    
    基本用法
    

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

    
    async function getStockPriceByName(name){
        const symbol = await getStockSymbol(name);
        const stockPrice = await getStockPrice(symbol);
        return stockPrice;
    }
    getStockPriceByName('goog').then(function(result){
        console.log(result);
    })
    

    上面代码是一个获取股票报价的函数,函数前面的async关键字,表明该函数内部有异步操作。调用该函数时,会立即返回一个Promise对象。
    下面是另一个例子,指定多少毫秒后输出一个值。

    
    function timeout(ms){
        return new Promise((resolve)=>{
            setTimeout(resolve,ms);
        })
    }
    async function asyncPrint(value,ms){
        await timeout(ms);
        console.log(value)
    }
    asyncPrint('hello world',50);
    

    由于async函数返回的是Promise对象,可以作为await命令的参数。所以,上面的例子也可以写成下面的形式。

    
    async function timeout(ms){
        await new Promise((resolve)=>{
            setTimeout(resolve,ms);
        })
    }
    
    async function asyncPrint(value, ms) {
        await timeout(ms);
        console.log(value);
    }
    
    asyncPrint('hello world', 5000);
    

    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()=>{};
    

    返回Promise对象

    async函数返回一个Promise对象。
    async函数内部return语句返回的值,会成为then方法回调函数的参数。

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

    上面代码中,函数f内部return命令返回的值,会被then方法回调函数接收到。
    async函数内部抛出错误,会导致返回Promise对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。

    
    async function f(){
        throw new Error('出错了');
    }
    f().then(
        v=>console.log(v),
        e=>console.log(e)
    )
    //Error;出错了
    

    Promise对象的状态变化

    async函数返回的Promise对象,必须等到内部所有await命令后面的Promise对象执行完,才会发生状态改变,除非遇到return语句或则抛出错误。也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。

    
    async function getTitle(url) {
      let response = await fetch(url);
      let html = await response.text();
      return html.match(/<title>([sS]+)</title>/i)[1];
    }
    getTitle('https://tc39.github.io/ecma262/').then(console.log)
    

    上面代码中,函数getTitle内部有三个操作:抓取网页、取出文本、匹配页面标题。只有这三个操作全部完成,才会执行then方法里面的console.log()
    await命令
    正常情况下,await命令后面是一个Promise对象,返回该对象的结果。如果不是Promsie对象,就直接返回对应的值。

    
    async function f(){
        //等同于123;
        return await 123;
    }
    f().then(v=>console.log(v))
    

    上面代码中,await命令的参数是数值123,这时等同于return 123.
    另一种情况是,await命令后面是一个thenable对象(即定义then方法的对象),那么await会将其等同于Promise对象。

    
    class Sleep{
        constructor(timeout){
            this.timeout = timeout;
        }
        then(resolve,reject){
            const startTime = Date.now();
            setTimeout(
                ()=>resolve(Date.now()-startTime)
                this.timeout
            )
        }
    }
    (async()=>{
        const actualTime = await new Sleep(1000);
        console.log(actualTime);
    })();
    

    上面代码中,await命令后面是一个Sleep对象的实例。这个实例不是Promise对象,但是因为定义了then方法,await会将其视为Promise处理。
    await命令后面的Promise对象如果变为reject状态,则reject的参数会被catch方法的回调函数接收到。

    
    async function f(){
        await Promise.reject('出错了');
    }
    f().then(v=>console.log(v)).catch(e=>console.log(e));
    

    注意,上面代码中,await语句前面没有return,但是reject方法的参数依然传入到catch方法的回调函数。这里如果是await前面加上return,效果是一样的。
    任何一个await语句后面的Promise对象变为reject状态,那么整个async函数都会中断执行。
    有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以将第一个await放在try...catch结构里面,这样不管这个异步操作是否成功,第二个await都会执行。

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

    另一种方法是await后面的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
    

    错误处理
    如果await后面的异步操作出错,那么等同于async函数返回的Promise对象被reject。防止出错的方法,也是将其放在try...catch代码块之中。

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

    如果有多个await命令,可以统一放在try...catch结构中。

    
    async function main(){
        try{
            const val1 = await firstStep();
            const val2 = await secondStep(val1);
            const val3 = await thirdStep(val1,val2);
            console.log('Final',val3);
        }
        catch(err){
            console.error(err);
        }
    }
    

    下面的例子使用try...catch结构,实现多次重复尝试。

    
    const superagent = require('superagent');
    const NUM_RETRIES = 3;
    async function test(){
        let i;
        for(i=0;i<NUM_RETRIES;i++){
            await superagent.get('http://goole.com/this-throws-an-error');
            break;
        }catch(err){}
        console.log(i);//3
    }
    test();
    

    上面代码中,如果await操作成功,就会使用break语句退出循环;如果失败,会被catch语句捕捉,然后进入下一轮循环。

    来源:https://segmentfault.com/a/1190000017469670

  • 相关阅读:
    iOS截取长图,自定义截取size
    工作
    UITableView适配iOS11
    利用脚本实现build号自动加一
    iOS原生与JS互调
    CSS高级技巧
    伪元素选择器
    CSS设置过渡
    CSS文本属性 二
    css设置圆角矩形
  • 原文地址:https://www.cnblogs.com/qixidi/p/10160582.html
Copyright © 2011-2022 走看看