Maison > interface Web > js tutoriel > le corps du texte

Un article vous aidera à maîtriser facilement Promise

青灯夜游
Libérer: 2023-02-10 19:49:16
avant
2241 Les gens l'ont consulté

Un article vous aidera à maîtriser facilement Promise

Dans l'apprentissage front-end js, la chose la plus inconfortable pour tout le monde est le problème de l'asynchrone. Pour résoudre des problèmes tels que asynchrone, l'enfer des rappels et d'autres, vous devez apprendre les promesses. programmeurs front-end, les promesses sont tout simplement un cauchemar, cet article est d'un point de vue facile à comprendre comme point d'entrée pour aider tout le monde à maîtriser facilement les promesses异步、回调地狱等问题时你必须得学会promise,对于多数前端程序员来说promise简直就是噩梦,本篇文章就是从通俗易懂的角度做为切入点,帮助大家轻松掌握promise

异步编程


想要学习promise,你必须要懂得什么是异步编程!众所周知,js语言是单线程机制。所谓单线程就是按次序执行,执行完一个任务再执行下一个。但是不影响存在同步异步的两种操作,这两种操作做事情其实都是在一条流水线上(单线程),只是这两种操作在单线程上的执行顺序不一样罢了!当js触发到异步任务时,会将异步任务交给浏览器处理,当执行有结果时,会把异步任务的回调函数插入待处理队列的队尾!

我们通俗的去解释一下我们的异步:异步就是从主线程发射一个子线程来完成任务,每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的.

Un article vous aidera à maîtriser facilement Promise

该图摘自于菜鸟教程中的异步编程小节,帮助大家更好的理解什么是异步!

回调函数


回调函数的定义非常简单:一个函数被当做一个实参传入到另一个函数(外部函数),并且这个函数在外部函数内被调用,用来完成某些任务的函数。就称为回调函数

回调函数的两种写法(实现效果相同):

const text = () => {
	   document.write('hello james')
}
setTimeout(text,1000)
Copier après la connexion
setTimeout(()=>{
	   document.write("hello james")
},1000)
Copier après la connexion

这段代码中的 setTimeout 就是一个消耗时间较长的过程,它的第一个参数是个回调函数,第二个参数是毫秒数,这个函数执行之后会产生一个子线程,子线程会等待 1 秒,然后执行回调函数 "text",在文本中输出hello james

setTimeout会在子线程中等待1秒,但是主线程的运行不会受到影响!例如以下代码:

setTimeout(()=>{
    document.write("hello davis")
},1000)
console.log('123456');
Copier après la connexion

在这里会先打印出来123456(主线程),然后一秒后在文本中输出hello davis(子线程)

回调地狱


回调地狱这个词听起来就非常的高大上,想要接触Promise之前,必须要懂得什么是回调地狱,以及为什么会产生回调地狱?
先来看看概念:当一个回调函数嵌套一个回调函数的时候就会出现一个嵌套结构当嵌套的多了就会出现回调地狱的情况
举个例子
比如我们发送三个 ajax 请求:

  • 第一个正常发送
  • 第二个请求需要第一个请求的结果中的某一个值作为参数
  • 第三个请求需要第二个请求的结果中的某一个值作为参数

你会看到以下代码

$.ajax({
  url: '我是第一个请求',
  type: 'get',
  success (res) {
    // 现在发送第二个请求
    $.ajax({
      url: '我是第二个请求',
      type:'post',
      data: { a: res.a, b: res.b },
      success (res1) {
        // 进行第三个请求
        $.ajax({
          url: '我是第三个请求',
          type:'post',
          data: { a: res1.a, b: res1.b },
                  success (res2) { 
            console.log(res2) 
          }
        })
      }
    })
  }
})
Copier après la connexion

这种代码看起来属实是折磨人啊!当我们把代码写成这样的时候,就陷入了可维护性差的状态了,代码体验非常的不良好,看一会就给看懵了,为了解决这个问题,于是,就引入了我们的Promise,用Promise去解决回调地狱问题!

Promise


Promise异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大,它是一个 ECMAScript 6 提供的类,目的是更加优雅地书写复杂的异步任务

Promise对象有以下两个特点:

  • 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”

    Programmation asynchrone


    Si vous voulez apprendre les promesses, vous devez comprendre ce qu'est la programmation asynchrone ! Comme nous le savons tous, le langage js est un mécanisme mono-thread. Ce qu'on appelle un thread unique signifie exécuter dans l'ordre, exécuter une tâche avant d'exécuter la suivante. Cependant, cela n'affecte pas l'existence de deux opérations : synchrone et asynchrone. Ces deux opérations font en réalité des choses sur une chaîne d'assemblage (un seul thread), mais ces deux opérations le sont. in L'ordre d'exécution sur un seul thread est juste différent ! Lorsque js déclenche une tâche asynchrone, la tâche asynchrone sera transmise au navigateur pour traitement. Une fois l'exécution terminée, la fonction de rappel de la tâche asynchrone sera insérée à la fin de la file d'attente à traiter !
    🎜🎜Expliquons notre asynchrone de manière simple : Asynchrone consiste à lancer un sous-thread à partir du thread principal pour terminer la tâche. Chaque tâche a une ou plusieurs fonctions de rappel (callback). . Après la fin de la tâche précédente, au lieu d'exécuter la tâche suivante, la fonction de rappel est exécutée Cette dernière tâche est exécutée avant la fin de la tâche précédente, donc l'exécution de la tâche précédente. programme L'ordre est incohérent et asynchrone avec l'ordre des tâches.🎜🎜at Insérez la description de l'image ici🎜
    🎜Cette image est extraite de la Programmation asynchrone< dans le <a href="//m.sbmmt.com/link/1f3ee194d616d6dad309e8516c44af85">Tutoriel Rookie</a> /code> pour aider tout le monde à mieux comprendre ce qu'est l'asynchrone ! 🎜</blockquote><h2><a id="_27"></a><strong>Fonction de rappel</strong></h2><hr/><blockquote>🎜La définition d'une fonction de rappel est très simple : une fonction Une fonction qui est transmise en tant que <code>paramètre réel à une autre fonction (fonction externe), et cette fonction est appelée dans la fonction externe pour accomplir certaines tâches. Cela s'appelle fonction de rappel🎜
    🎜deux façons d'écrire la fonction de rappel (le même effet) : 🎜
    new Promise(function (resolve, reject) {
      // resolve 表示成功的回调
      // reject 表示失败的回调
    }).then(function (res) {
      // 成功的函数
    }).catch(function (err) {
      // 失败的函数
    })
    Copier après la connexion
    Copier après la connexion
            const promise = new Promise((resolve,reject)=>{
                //异步代码
                setTimeout(()=>{
                    // resolve([&#39;111&#39;,&#39;222&#39;,&#39;333&#39;])
                    reject(&#39;error&#39;)
                },2000)
            })
            promise.then((res)=>{
                //兑现承诺,这个函数被执行
                console.log(&#39;success&#39;,res);
            }).catch((err)=>{
                //拒绝承诺,这个函数就会被执行
                console.log(&#39;fail&#39;,err);
            })
    Copier après la connexion
    Copier après la connexion
    🎜setTimeout< dans ce code /code> est un processus qui prend beaucoup de temps. Son premier paramètre est une <code>fonction de rappel, et le deuxième paramètre est millisecondes. Une fois cette fonction exécutée, elle générera un sous-. thread. Le sous-thread attendra 1 seconde, puis exécutera la fonction de rappel "text", et affichera hello james dans le texte 🎜🎜setTimeout attendra 1. deuxième dans le sous-thread , mais le fonctionnement du thread principal ne sera pas affecté ! Par exemple, le code suivant : 🎜
    pajax({
        url:"http://localhost:3000/news",
        data : {
            author : "james"
        }
    }).then(res=>{
        return pajax({
            url : "http://localhost:3000/comments",
            data : {
                newsId : res[0].id
            }
        })
    }).then(res=>{
        console.log(res);
    }).catch(err=>{
        console.log(err);
    })
    Copier après la connexion
    Copier après la connexion
    🎜Ici, 123456 (main thread) sera imprimé en premier, puis hello davis< sera affiché dans le texte une seconde plus tard /code>(<code>fil de discussion enfant)🎜

    L'enfer des rappels


    🎜Le mot « enfer de rappel » semble très noble Avant de vouloir entrer en contact avec Promise, vous devez comprendre ce qu'est l'l'enfer de rappel et pourquoi se produit l'enfer de rappel ?
    Jetons d'abord un coup d'œil au concept : Lorsqu'une fonction de rappel imbrique une fonction de rappel, une structure imbriquée apparaîtra. S'il y a trop de fonctions imbriquées, un enfer de rappel se produira.
    Par exemple :
    Par exemple, nous envoyons trois requêtes ajax :
    🎜
    • La première est envoyée normalement
    • La deuxième requête nécessite une certaine valeur du résultat de la première requête en tant que paramètre
    • La troisième requête nécessite une certaine valeur du résultat de la deuxième requête en tant que paramètre
    🎜Vous verrez le code suivant : 🎜
    const p = Promise.all([p1, p2, p3]);
    Copier après la connexion
    Copier après la connexion
    🎜Ce genre de code a l'air vraiment torturé ! Lorsque nous écrivons le code comme celui-ci, nous tombons dans un état de mauvaise maintenabilité. L'expérience du code est très mauvaise et je me sens confus après l'avoir regardé pendant un moment. Afin de résoudre ce problème, nous avons présenté notre Promesse et utilisé Promise pour résoudre le problème de l'enfer des rappels ! 🎜

    Promise

    🎜🎜
    🎜Promise fait partie de asynchrone programmation Une solution plus raisonnable et puissante que les solutions traditionnelles - fonctions de rappel et événements. C'est une classe fournie par ECMAScript 6, qui vise à écrire du code complexe de manière plus élégante . Tâches asynchrones.
    🎜🎜Les objets Promise ont les deux caractéristiques suivantes :🎜
    • 🎜L'état de l'objet n'est pas affecté par le monde extérieur. L'objet Promise représente une opération asynchrone et a trois états : en attente (en cours), réalisé (réussi) et rejeté (échec). Seul le résultat de l'opération asynchrone peut déterminer l'état actuel, et aucune autre opération ne peut changer cet état. C'est aussi l'origine du nom Promise. Sa signification anglaise est "engagement", ce qui signifie qu'il ne peut pas être modifié par d'autres moyens. 🎜
    • 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

    两个特点摘自于??阮一峰ES6文章

    Promise语法格式

    new Promise(function (resolve, reject) {
      // resolve 表示成功的回调
      // reject 表示失败的回调
    }).then(function (res) {
      // 成功的函数
    }).catch(function (err) {
      // 失败的函数
    })
    Copier après la connexion
    Copier après la connexion

    出现了new关键字,就明白了Promise对象其实就是一个构造函数,是用来生成Promise实例的。能看出来构造函数接收了一个函数作为参数,该函数就是Promise构造函数的回调函数,该函数中有两个参数resolvereject,这两个参数也分别是两个函数!

    简单的去理解的话resolve函数的目的是将Promise对象状态变成成功状态,在异步操作成功时调用,将异步操作的结果,作为参数传递出去。reject函数的目的是将Promise对象的状态变成失败状态,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

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

    代码示例:

            const promise = new Promise((resolve,reject)=>{
                //异步代码
                setTimeout(()=>{
                    // resolve([&#39;111&#39;,&#39;222&#39;,&#39;333&#39;])
                    reject(&#39;error&#39;)
                },2000)
            })
            promise.then((res)=>{
                //兑现承诺,这个函数被执行
                console.log(&#39;success&#39;,res);
            }).catch((err)=>{
                //拒绝承诺,这个函数就会被执行
                console.log(&#39;fail&#39;,err);
            })
    Copier après la connexion
    Copier après la connexion

    代码分析:

    上边说到Promise是一个构造函数,new之后等于说调用了构造函数,构造函数中传的参数是一个函数,这个函数内的两个参数分别又是两个函数(reslovereject),虽然感觉很绕,但是理清思路会很清晰的!


    我们得到对象promise,promise对象中自带有两个方法thencatch,这两个方法中会分别再传入一个回调函数,这个回调函数的目的在于返回你所需要成功或失败的信息!那么怎么去调用这两个回调函数呢?
    看下方图可以快速理解:

    Un article vous aidera à maîtriser facilement Promise
    这两个函数分别做为参数(reslovereject)传到上方的函数中去了.随后在构造函数的回调函数中写入异步代码(例如:ajax定时器),这里使用了定时器作为例子,如果你想表达的是成功回调,你可以在内部调用函数reslove(&#39;一般情况下是后端返回的成功数据)。如果你想表达的是失败回调,你可以调用reject(&#39;一般情况下是后端返回的失败信息&#39;).

    这些就是Promise执行的过程!虽然理解着很绕,但是多读几遍绝对有不一样的收获!

    Promise链式

    then方法返回的是一个新的Promise实例注意:不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法

    实际案例:
    我想要实现在一个数组中查看一个帖子,但是我最终的目的是得到这个帖子下面的所有评论,这该怎么实现呢?

    实现思路
    先从一个接口中获取这个帖子的信息,然后通过该帖子的帖子id从而获取到该帖子下的所有评论

    代码如下:

    pajax({
        url:"http://localhost:3000/news",
        data : {
            author : "james"
        }
    }).then(res=>{
        return pajax({
            url : "http://localhost:3000/comments",
            data : {
                newsId : res[0].id
            }
        })
    }).then(res=>{
        console.log(res);
    }).catch(err=>{
        console.log(err);
    })
    Copier après la connexion
    Copier après la connexion

    代码分析:

    这里使用了一个Promise已经封装过的ajax,我们从第一个接口中得到了帖子id,然后在then中的函数发送第二个请求(携带了第一个请求返回过来的参数),我们最后想要拿到第二个接口的结果,于是又有了一个then方法,但是在第一个then方法中要把一个新的Promise实例return出去,这样的话,第二个then才起作用!(这是因为then方法是Promise 实例所具有的方法,也就是说,then方法是定义在原型对象Promise.prototype上的)====>我们可以打印一下:console.log(Promise.prototype)

    Un article vous aidera à maîtriser facilement Promise
    可以看的出来原型对象Promise.prototype中是有then方法的!

    Promise.all()

    Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例

    语法格式:

    const p = Promise.all([p1, p2, p3]);
    Copier après la connexion
    Copier après la connexion

    Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会调用Promise.reslove() [该方法可自行了解]自动将参数转为 Promise 实例,再进一步处理。

    说那么多白话没用,我们可以根据一个案例,就可以明白Promise.all()的用途了。

    实际案例:
    如果你想实现一个效果:在一个页面中,等到页面中所有的请求返回数据后,再渲染页面,该怎么实现呢?(在实际开发中我们会看到loading加载页面,等数据返回完后,loading加载页面会消失,整个页面就展现出来了,增强用户的体验。)

    实现思路:
    通过Promise.all()方法,等多个接口全部接收到数据后,再统一进行处理,然后渲染页面

    代码如下:

    console.log("显示加载中")
    const q1 = pajax({
        url:"http://localhost:3000/looplist"
    })
    
    const q2 = pajax({
        url:"http://localhost:3000/datalist"
    })
    Promise.all([q1,q2]).then(res=>{
        console.log(res)
        console.log("隐藏加载中...")
    }).catch(err=>{
        console.log(err)
    })
    Copier après la connexion

    代码分析:

    在上方代码中,全局打印显示加载中是代替loading的页面,表示该页面现在正是loading页面中,等到q1q2所请求接口都得到返回的信息后,在then方法中接收收据,并且可以进行渲染页面,同时隐藏了loading加载页面!

    小结

    不论是在前端的项目开发中还是在前端的面试过程中,Promise的地位就是举足轻重的,虽然解决异步编程的终极解决方案是async和await,但是它们也是基于Promise封装而来的,在以往文章中,我就说过,学习编程重要的是搞懂某个技术是怎么实现的,而不是做一个cv侠,多去思考,才能进步。继续加油吧,少年!

    【相关推荐:javascript视频教程编程基础视频

    Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:csdn.net
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal