欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Js异步编程Promise和async/await方式个人总结

程序员文章站 2022-05-06 15:32:24
js异步编程promise和async/await方式总结 promise 是异步编程的一种解决方案,比传统的解决方案,回调函数和事件——更合理和更强大,promise 是...

    js异步编程promise和async/await方式总结

    promise 是异步编程的一种解决方案,比传统的解决方案,回调函数和事件——更合理和更强大,promise 是一个对象,从它可以获取异步操作的消息,promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject,基本样例:

    
    

    const promise = new promise((resolve, reject)=>{

    ses.sendemail(params, function(err, data) {

    if (err) {

    reject (err);

    } else {

    resolve(data);

    }

    });

    });

    promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

    
    

    promise.then(function(value) {

    resolved(value);

    }, function(error) {

    rejected(error);

    });

    但是一般不要在then方法里面定义reject状态的回调函数,强烈建议使用catch的方式进行处理,上面的写法可以改写为下面这种更加合理:

    
    

    promise.then(function(data) {

    resolved(value);

    }).catch(function(err) {

    console.log(error)

    });

    因为改写后的方式,既可以捕获前面then方法执行中的错误,也更接近同步的写法(try/catch)

     

    async/await

    es2017 标准引入了 async 函数,使得异步操作变得更加方便。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。

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

    我们可以测试一下async的返回值,写一个test.js

    
    

    async function testasync() {

    return "hello async";

    }

    const result = testasync();

    console.log(result);

    macdemac-mini:work mac$ node test.js

    promise { 'hello async' }

    运行之后发现,输出的是一个promise对象,由此可知async 函数返回的是一个 promise 对象,实际上async/await就是为了解决promise写异步时then调用链的问题,如果我们在一个普通的函数里面没法使用await(await必须要在async函数里面才可以使用)来获取async函数的返回值的话,我们就可以用then的方式进行处理

    
    

    testasync().then(v => {

    console.log(v); // 输出 hello async

    });

    我们可以认为await实际上是在等待async函数执行完成,因为 async 函数返回一个 promise 对象,所以 await 可以用于等待一个 async 函数的返回值——这也可以说是 await 在等 async 函数,它等的实际是一个返回值。

    
    

    async function testasync() {

    return "hello async";

    }

    async function test() {

    const value = await testasync();

    console.log(value);

    }

    test();

    当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

    一个函数要调用一个异步方法,同时该函数需要有返回值,我们可以这么写

    
    

    exports.handler = async (event) => {

    var params = ""

    const result = await test_async(params);

    return result;

    };

    function test_async(params){

    const promise = new promise((resolve, reject)=>{

    ses.sendemail(params, function(err, data) {

    if (err) {

    reject (err);

    } else {

    resolve(data);

    }

    });

    });

    
    

    return promise;

    }

    上面的test_async这个方法并没有写async,但是实际上由于test_async这个方法本身返回的就是一个promise对象,我们知道async所起的作用就是返回一个promise的(上面提到过),所以这个地方可以不用写async效果一样的。然后我们就可以使用await来获取test_async的返回值,并且作为函数的return返回

    promise和async/await两种方式比较

    假设一个业务,分多个步骤完成,每个步骤都是异步的,而且依赖于之前每个步骤的结果。我们仍然用settimeout来模拟异步操作:

    
    

    function takelongtime(n) {

    return new promise(resolve => {

    settimeout(() => resolve(n + 200), n);

    });

    }

    function step1(n) {

    console.log(`step1 with ${n}`);

    return takelongtime(n);

    }

    function step2(m, n) {

    console.log(`step2 with ${m} and ${n}`);

    return takelongtime(m + n);

    }

    function step3(k, m, n) {

    console.log(`step3 with ${k}, ${m} and ${n}`);

    return takelongtime(k + m + n);

    }

     

    把这个需求分别用promise和async/await进行改造

    promise使用then来改造:

    
    

    function doit() {

    console.time("doit");

    const time1 = 300;

    step1(time1)

    .then(time2 => {

    return step2(time1, time2)

    .then(time3 => [time1, time2, time3]);

    })

    .then(times => {

    const [time1, time2, time3] = times;

    return step3(time1, time2, time3);

    })

    .then(result => {

    console.log(`result is ${result}`);

    console.timeend("doit");

    });

    }

    doit();

    使用async/await进行改造:

    
    

    async function doit() {

    console.time("doit");

    const time1 = 300;

    const time2 = await step1(time1);

    const time3 = await step2(time1, time2);

    const result = await step3(time1, time2, time3);

    console.log(`result is ${result}`);

    console.timeend("doit");

    }

    doit();

    可以看出来async更加简单

    async注意事项

    1:如果await后面的异步操作出错,那么等同于async函数返回的 promise 对象被reject。所以最好把await命令放到try catch中进行处理

    
    

    async function myfunction() {

    try {

    await test_async();

    } catch (err) {

    console.log(err);

    }

    }

    当然也可以这么写:

    
    

    async function myfunction() {

    await test_async()

    .catch(function (err) {

    console.log(err);

    });

    }

    但是建议还是使用第一种方式比较好,更加符合同步的处理方式。

    如果有多个await,可以统一的放到try catch处理。

    2:多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。

    
    

    let foo = await getfoo();

    let bar = await getbar();

    上面代码中,getfoo和getbar是两个独立的异步操作(即互不依赖),被写成继发关系。这样比较耗时,因为只有getfoo完成以后,才会执行getbar,完全可以让它们同时触发。

    
    

    // 写法一

    let [foo, bar] = await promise.all([getfoo(), getbar()]);

    3:await命令只能用在async函数之中,如果用在普通函数,就会报错

    4:如果在调用async函数的时候没有写await,那么他会被立刻执行,因为async返回的是promise对象,而promise的特点是无等待,那么也就是说在没有await的情况下执行async函数,该函数会立马执行,返回一个 promise 对象,并且,绝不会阻塞后面的语句。这和普通返回 promise 对象的函数并无二致。

    
    

    router.get('/', function(req, res) {

    const html = test_async();

    console.log(html);

    res.sendwithlog(html);

    });

    async function test_async() {

    var value = 'hello async';

    return value;

    }

    输入https://127.0.0.1:8080/test,前端得不到我们要的值,控制台打印的是一个promise对象,因为调用test_async的这个方式是一个非async的方法,所以不能用await,前面提到过如果没法写await,这个时候可以使用then来获取返回值。

    我们可以改造一下,在调动test_async的时候加上await再看下效果:

    
    

    router.get('/test', function(req, res) {

    get_v(res);

    });

    async function get_v(res) {

    const html = await test_async();

    res.sendwithlog(html);

    }

    async function test_async() {

    var value = 'hello async';

    return value;

    }