zoukankan      html  css  js  c++  java
  • 简述异步编程&Promise&异步函数

    前言:文章由本人在学习之余总结巩固思路,不足之前还请指出。

    一.异步编程

    首先我们先简单来回顾一下同步API和异步API的概念

    1.同步API:只有当前的API执行完成之前,才会执行下一个API

    例:

    console.log(‘first');
    console.log('last);
    结果:
    first
    last

    2.异步API:当前API的执行不会阻塞后续代码的执行

    例:

    console.log('first');
    setTimeout(
       () => { console.log('last');
    }, 2000);
    console.log('middle');
    结果:
    first
    middle
    last

    执行顺序分析:

    首先脚本会先执行同步代码,这时有一个同步代码区,按着从上到下的顺序进行。当所有的同步代码执行完成之后,再进入异步代码区查找是否有异步代码,并开始执行,执行完之后,调用对应的回调函数。

    拿例子中的计时器来说,该异步函数每2秒都会重新调用一次。

    注意:即使是计时器的时间设置为0,它任是一个异步API,不会随着同步一起执行。

    3.Node.js中的异步API

    fs.readFile('./demo.txt', (err, result) => {});

    当硬盘读取了文件之后,调用后方的回调函数;

    但这个时候,我们有了一个需求,依次读取A,B,C三个文件。由于文件大小不一定是我们知道的,所以读取,查找的速度我们也并不知道。

    按照同步的思路来写的话:

    fs.readFile('./1.txt', 'utf8', (err, result1) => {console.log(result1)});
    fs.readFile('./2.txt', 'utf8', (err, result2) => {console.log(result2)});
    fs.readFile('./3.txt', 'utf8', (err, result3) => {console.log(result3)});

    结果未必如我们所愿。

    这时我们也许会想到一个办法,既然该API是异步API,我们把各个API嵌套起来,这样不久可以依次执行了?

    fs.readFile('./1.txt', 'utf8', (err, result1) => {
        console.log(result1)
        fs.readFile('./2.txt', 'utf8', (err, result2) => {
            console.log(result2)
            fs.readFile('./3.txt', 'utf8', (err, result3) => {
                console.log(result3)
            })
        })
    });

    emmm....确实可以,但他的缺点也能预料到:这里只有三个文件,可能不易看出其劣势,但是如果是300个呢?代码的可读性将大幅度降低,维护的难度相应提高,这是我们不愿意看到的。

    这一现象,我们称其为回调地狱(callbackhell)。进来了就si里面了。

    解决的办法当然也有,这时该轮到我们的Promise登场了。

    二.Promise

    Promise出现的目的是解决Node.js异步编程中回调地狱的问,它是一个构造函数,我们要用new Promise的方法调用。

    我们先来简单地介绍一下Promise。

    let promise = new Promise((resolve, reject) => {});
    Promise的参数为一个匿名回调函数,其中reslove,reject也是回调函数,他能将异步API的执行和结果进行分离,reslove对应着result(正常思路下)当fs有返回结果的时候,我们可以将其通过回调函数的方式将其发送到外面,
    同理,当fs出现错误的时候,我们可以将其发送到外面进行处理。这里要用到Promise下面的两个方法promise.then()&promise.catch(),分别用来对结果错误信息进行处理。

    我们结合实例来分析这些回调函数。



    let promise = new Promise((resolve, reject) => {
    
        fs.readFile('./1.txt', 'utf8', (err, result) => {
    
            if (err != null) {
                reject(err);
      相当于执行then里面的回调函数 }
    else { resolve(result);
      相当于执行catch里面的回调函数 } }); });


      promise.then((result) => {
        console.log(result);
      })
      .catch((err)=> {
        console.log(err);
      })




    用此方法来对我们之前的函数进行包装,分析一下,既然我们有三个异步API,我们则需要用三个Promise将他们包裹起来,我们需要让这三个promise依次执行,但是如果我们直接声明了一个变量等于promise的话就直接执行了,所以
    这里我们用一个函数把他封装起来
    function p1 () {
        return new Promise ((resolve, reject) => {
            fs.readFile('./1.txt', 'utf8', (err, result) => {
                resolve(result)
            })
        });
    }
    
    function p2 () {
        return new Promise ((resolve, reject) => {
            fs.readFile('./2.txt', 'utf8', (err, result) => {
                resolve(result)
            })
        });
    }
    
    function p3 () {
        return new Promise ((resolve, reject) => {
            fs.readFile('./3.txt', 'utf8', (err, result) => {
                resolve(result)
            })
        });
    }

    为了实现顺序调用,我们使用链式编程

    p1().then((r1)=> {
        console.log(r1);
        return p2();
    })
    .then((r2)=> {
        console.log(r2);
        return p3();
    })
    .then((r3) => {
        console.log(r3)
    })

    注意:这里return 调用返回的结果我们可以参考MDN的解释

    返回一个已经是接受状态的 Promise,那么 then 返回的 Promise 也会成为接受状态,并且将那个 Promise 的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。

    这里看上去我们的代码的似乎比之前的嵌套关系更为复杂,这里就要引入我们的异步函数了。

    3.异步函数

    异步函数是异步编程语法的终极解决方案,它可以让我们将异步代码写成同步的形式,让代码不再有回调函数嵌套,使代码变得清晰明了。

    举一个简单的函数来看看他的用法

    // 1.在普通函数定义的前面加上async关键字 普通函数就变成了异步函数
    // 2.异步函数默认的返回值是promise对象
    // 3.在异步函数内部使用throw关键字进行错误的抛出
    
    async function fn () {
    这里我们发现我们不需要再new一个Promise再将其返回了
    return 123; //正常的时候用return // throw '发生了一些错误';出错的时候throw } console.log(fn ()) fn () .then(function (data) { console.log(data); }) .catch(function (err){ console.log(err); })
    那么如何让我们的函数有序地进行呢?这里我们不用再采用return嵌套,接下来就要用到await了
    我们定义一个run函数
    // await关键字
    // 1.它只能出现在异步函数中
    // 2.await promise 它可以暂停异步函数的执行 等待promise对象返回结果后再向下执行函数
    async function run () {
        let r1 = await p1()
        let r2 = await p2()
        // await不能直接得到throw并赋值给r3这里我们采用catch试试
        let r3 = p3().catch(n => console.log(n));// p3
        console.log(r1)
        console.log(r2)
        console.log(r3 instanceof Promise)//ture
        // console.log(r3)
    }
    这样就能实现我们的按顺序执行了
    此处r3的值是我在记笔记的时候发现await并不直接接受reject的Promise,所以做了个输出的尝试,随意看看就好 run();
    新手一枚,大家有啥好的想法或者问题欢迎一起讨论


  • 相关阅读:
    python web 2
    python web1(解析url)
    webstrom 今天突然要激活
    数组排序 记录一下
    浏览器添加随机数去除缓存
    vue-cli 安装报错
    vue 初始化项目报错
    深拷贝和浅拷贝
    css3 属性 clip-path
    js数组去重
  • 原文地址:https://www.cnblogs.com/dokom666/p/12883545.html
Copyright © 2011-2022 走看看