Pregunta de la entrevista 6: El uso de Promise, explicado en detalle en este artículo

significado

Promise es una solución para programación asincrónica que es más razonable y poderosa que las soluciones tradicionales ( funciones de devolución de llamada y eventos).

El llamado Promise, en pocas palabras, es un contenedor que almacena los resultados de un evento (generalmente una operación asincrónica) que finalizará en el futuro. Sintácticamente hablando, Promise es un objeto del cual se pueden obtener mensajes para operaciones asincrónicas.

 De un vistazo, puede comprender que Promise es un constructor, tiene métodos familiares como all, rechazar y resolver, y el prototipo tiene métodos igualmente familiares como then y catch. Entonces, los objetos creados usando Promise new deben tener métodos then y catch.

Uso básico

ES6 estipula que el objeto Promise es un constructor utilizado para generar instancias de Promise

var promise = new Promise(function(resolve,reject){
  if(/* 异步操作成功 */){
    resolve(value);
  }else{
    reject(error);
  }
});

El constructor de Promise acepta una función como parámetro. Los dos parámetros de la función son resolver y rechazar, que representan respectivamente la función de devolución de llamada después de que la operación asincrónica se ejecuta con éxito y la función de devolución de llamada después de que falla la operación asincrónica. Son dos funciones, proporcionadas por el motor JavaScript y no implementadas por usted mismo.

La función de la función de resolución es cambiar el estado del objeto Promesa de "sin terminar" a "exitoso" (es decir, de Pendiente a Resuelto), se llama cuando la operación asincrónica es exitosa y pasa el resultado de la operación asincrónica. como parámetro,
función de rechazo Su función es llamar cuando falla la operación asincrónica y pasar el error informado por la operación asincrónica como parámetro.

Mira el siguiente código:
 

var p = new Promise(function(resolve, reject){
    //做一些异步操作
    setTimeout(function(){
        console.log('执行完成');
        resolve('数据');
    }, 2000);
});

En el código anterior, realizamos una operación asincrónica, es decir, setTimeout. Después de 2 segundos, se genera "ejecución completa" y se llama al método de resolución.

Cuando ejecute el código, aparecerá "Ejecución completada" después de 2 segundos. ¡Aviso! Acabo de crear un nuevo objeto y no lo llamé. La función que pasamos ya se ejecutó. Este es un detalle al que hay que prestar atención. Entonces, cuando usamos Promise, generalmente lo envolvemos en una función y ejecutamos esta función cuando es necesario, como por ejemplo:
 

function runAsync(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('执行完成');
            resolve('数据');
        }, 2000);
    });
    return p;            
}
runAsync()

Al final de nuestra función envuelta, se devolverá el objeto Promise, es decir, obtenemos un objeto Promise al ejecutar esta función. Recuerde que existen métodos then y catch en el objeto Promise, ¿verdad? Este es su poder, mira el siguiente código:

runAsync().then(function(data){
    console.log(data);
    //后面可以用传过来的数据做些其他操作
    //......
});

Llame al método then directamente al regresar de runAsync(). Luego recibe un parámetro, que es una función, y obtendrá los parámetros que pasamos al llamar a resolve en runAsync. La ejecución de este código generará "ejecución completada" después de 2 segundos, seguido de "datos".

En este momento, deberías haberte dado cuenta de algo: resulta que la función en ese momento es como nuestra función de devolución de llamada habitual y se puede ejecutar después de que se complete la tarea asincrónica runAsync. Esta es la función de Promise: en pocas palabras, puede separar el método de escritura de devolución de llamada original y, después de ejecutar la operación asincrónica, la función de devolución de llamada se puede ejecutar en una llamada en cadena.

Quizás podrías pensar: ¿no sería lo mismo si encapsuláramos la función de devolución de llamada y la pasáramos a runAsync, así:
 

function runAsync(callback){
    setTimeout(function(){
        console.log('执行完成');
        callback('数据');
    }, 2000);
}
 
runAsync(function(data){
    console.log(data);
});

Entonces la pregunta es: ¿qué debo hacer si hay varias capas de devoluciones de llamada? ¿Qué debemos hacer si la devolución de llamada también es una operación asincrónica y se requiere una función de devolución de llamada correspondiente después de la ejecución? No puede definir otra devolución de llamada2 y pasarla a la devolución de llamada. La ventaja de Promise es que puede continuar escribiendo el objeto Promise en el método then y devolverlo, y luego continuar llamándolo para realizar operaciones de devolución de llamada.


operación en cadena

function runAsync1(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('异步任务执行完成1');
            resolve('数据1');
        }, 1000);
    });
    return p;            
}
function runAsync2(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('异步任务执行完成2');
            resolve('数据2');
        }, 2000);
    });
    return p;            
}
function runAsync3(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('异步任务执行完成3');
            resolve('数据3');
        }, 2000);
    });
    return p;            
}
 
runAsync1()
.then(function(data){
    console.log(data);
    return runAsync2();
})
.then(function(data){
    console.log(data);
    return runAsync3();
})
.then(function(data){
    console.log(data);
});

Salida de consola:

异步任务执行完成1
数据1
异步任务执行完成2
数据2
异步任务执行完成3
数据3

En el método entonces, también puede devolver datos directamente en lugar del objeto Promesa, y luego puede recibir los datos en el siguiente entonces. Por ejemplo, modificamos el código anterior a esto:

runAsync1()
.then(function(data){
    console.log(data);
    return runAsync2();
})
.then(function(data){
    console.log(data);
    return '直接返回数据';  //这里直接返回数据
})
.then(function(data){
    console.log(data);
});

Entonces la salida queda así: 

异步任务执行完成1
数据1
异步任务执行完成2
数据2
直接返回数据

uso de rechazar

En este punto, debería tener los conocimientos más básicos de "¿Qué es la promesa?" Entonces, echemos un vistazo a qué otras funciones tiene ES6's Promise. Sólo hemos usado resolver, pero aún no hemos usado rechazar. ¿Qué hace? De hecho, nuestros ejemplos anteriores solo tienen devoluciones de llamada de "ejecución exitosa", y no hay una situación de "falla". La función del rechazo es establecer el estado de la Promesa en rechazado, para que podamos capturarlo en ese momento y luego ejecutarlo. Devolución de llamada "falla". Mire el código a continuación.
 

function getNumber(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            var num = Math.ceil(Math.random()*10); //生成1-10的随机数
            if(num<=5){
                resolve(num);
            }
            else{
                reject('数字太大了');
            }
        }, 2000);
    });
    return p;            
}
 
getNumber()
.then(
    function(data){
        console.log('resolved');
        console.log(data);
    }, 
    function(reason, data){
        console.log('rejected');
        console.log(reason);
    }
);

La función getNumber se utiliza para obtener un número de forma asincrónica. La ejecución se completa después de 2 segundos. Si el número es menor o igual a 5, lo consideramos "exitoso" y llamamos a resolve para modificar el estado de la Promesa. De lo contrario, lo consideramos un "fallo", llamamos a rechazar y pasamos un parámetro como motivo del fallo.

Ejecute getNumber y pase dos parámetros en then. El método then puede aceptar dos parámetros, el primero correspondiente a la devolución de llamada de resolución y el segundo correspondiente a la devolución de llamada de rechazo. Entonces podemos obtener los datos que ellos pasan respectivamente. Ejecute este código varias veces y obtendrá aleatoriamente los dos resultados siguientes: 

 Uso de captura

Sabemos que además del método then, el objeto Promise también tiene un método catch, ¿para qué se utiliza? De hecho, es lo mismo que el segundo parámetro de then, usado para especificar la devolución de llamada de rechazo, el uso es el siguiente:

getNumber()
.then(function(data){
    console.log('resolved');
    console.log(data);
})
.catch(function(reason){
    console.log('rejected');
    console.log(reason);
});

El efecto es el mismo que escribirlo en el segundo parámetro de then. Pero también tiene otro efecto: al ejecutar la devolución de llamada de resolución (es decir, el primer parámetro anterior), si se genera una excepción (el código es incorrecto), entonces el js no se quedará atascado con un error, sino que ingresará. este método de captura. Por favor mire el código a continuación:

getNumber()
.then(function(data){
    console.log('resolved');
    console.log(data);
    console.log(somedata); //此处的somedata未定义
})
.catch(function(reason){
    console.log('rejected');
    console.log(reason);
});

En la devolución de llamada de resolución, usamos console.log(somedata); y la variable somedata no está definida. Si no usamos Promise, el código informará un error directamente en la consola cuando se ejecute aquí y no se ejecutará más. Pero aquí obtendrás este resultado: 

resolved
4
rejected
ReferenceError:somedata is not defined(...)

Es decir, ingresa el método catch y pasa el motivo del error al parámetro de motivo. Incluso si hay un error en el código, no se informará del error. Esto tiene la misma función que nuestra declaración try/catch.

Casos de uso en Ajax

 Si hay solicitudes de a y b, b depende de los datos de la solicitud de a. como sigue:

function a(){
      return new Promise(function(res,rej){
        $.ajax({
          url:"a接口",
          type: "GET",
          async:true,
          dataType:"json",
          success:function(data){
            console.log(data,"a");
            res(data);
          }
        })
      });
    }
    function b(data){
      console.log(data,"data");
      return new Promise(function(res,rej){
        $.ajax({
            url:"b接口",
            type: "POST",
            async:true,
            data:JSON.stringify(data),
            dataType:"json",
            success:function(data){
              console.log(data,"b");
              res();
            }
          })
      });
    }
    $("#btn").click(function(){
      a().then(function (data){
        b(data);
      }).then(function(){
      })
    })

Nota: Axios es una biblioteca HTTP basada en promesas.

 Uso de Promesa.all()

describir

1.await puede obtener  los resultados de devolución de múltiples promesas

2. Promise.all también devuelve una promesa, por lo que puede esperar directamente a Promise.all();

 1. Utilice Promesa.all()

function fn(){
    return new Promise((resolve,reject)=>{
        let randomNum = parseInt(Math.random()*6+1);
        console.log(randomNum);
        if(randomNum>3){
            resolve('买'); 
        }
        else{
            reject('不买');
        }
    })
}
 
Promise.all([fn(),fn()]).then((x)=>{console.log(x,'success')},(y)=>{console.log(y,'error');});

El parámetro en Promise.all es una matriz, y cada elemento de la matriz es una llamada a función que devuelve una promesa.
El primer parámetro de entonces es que todas las promesas se llaman con éxito y el resultado de retorno es una matriz. Cada elemento de la matriz es el resultado de retorno de la promesa de función.
El segundo parámetro de entonces: el resultado devuelto tiene una devolución de llamada que fallará si falla y se obtiene el primer valor fallido.

2. Utilice esperar

await puede obtener múltiples resultados de promesa, y Promise.all () también devuelve resultados de promesa. Entonces, si desea utilizar await para obtener los valores de retorno de múltiples promesas, puede esperar directamente Promise.all();

function fn(){
    return new Promise((resolve,reject)=>{
        let randomNum = parseInt(Math.random()*6+1);
        console.log(randomNum);
        if(randomNum>3){
            resolve('买'); 
        }
        else{
            reject('不买');
        }
    })
}
async function test(){
    try{
    let res = await Promise.all([fn(),fn()]);
    console.log(res,'success');
    }
    catch(error){
        console.log(error,'error');
    }
}
 
test();
  • Promise.all([fn(),fn()]) todos deben devolver resolve(); para obtener un valor de retorno exitoso
  • Promise.all([fn(),fn()]) tiene un retorno de rechazo(), luego ingresa catch(error) y obtiene el valor de retorno de falla

caso real

Aquí se introducen dos interfaces, se obtienen los resultados y luego se obtiene la suma de los valores de las dos interfaces. A través de Promise.all, se puede obtener la suma.

selectRewiewTaskNum() {
      console.log('我要获取复核的数据')
      let promiseValues = [ApiSign.selectReviewCount(), ApiSign.selectAssetPoolNum()]
      Promise.all(promiseValues).then(res => {
        console.log('res', res)
        this.billTaskNum = res[0].data.billTaskNum
        this.assetPoolTaskNum = res[1].data.assetPoolTaskNum
        this.taskNumber2 = this.assetPoolTaskNum + this.billTaskNum
        console.log('数据是', this.assetPoolTaskNum, this.billTaskNum)
      })
    },

Usando Promesa.race() 

Promise.race en realidad no se usa mucho, si realmente quieres usarlo. Podemos proponer tal requisito:

Por ejemplo: haga clic en un botón para enviar una solicitud. Cuando la interfaz de back-end exceda un cierto tiempo, suponiendo que exceda los tres segundos y no se devuelva ningún resultado, le indicaremos al usuario que la solicitud ha expirado.

<template>
  <div class="box">
    <el-button type="primary" plain @click="clickFn">点击测试</el-button>
  </div>
</template>

<script>
export default {
  name: "App",
  methods: {
    async clickFn() {

      // 第一个异步任务
      function asyncOne() {
        let async1 = new Promise(async (resolve, reject) => {
          setTimeout(() => {
            // 这里我们用定时器模拟后端发请求的返回的结果,毕竟都是异步的
            let apiData1 = "某个请求";
            resolve(apiData1);
          }, 4000);
        });
        return async1;
      }
      console.log("异步任务一", asyncOne());  // 返回的是pending状态的Promise对象

      // 第二个异步任务
      function asyncTwo() {
        let async2 = new Promise(async (resolve, reject) => {
          setTimeout(() => {
            let apiData2 = "超时提示";
            resolve(apiData2);
          }, 3000);
        });
        return async2;
      }
      console.log("异步任务二", asyncTwo()); // 返回的是pending状态的Promise对象

      // Promise.race接收的参数也是数组,和Promise.all类似。只不过race方法得到的结果只有一个
      // 就是谁跑的快,结果就使用谁的值
      let paramsArr = [asyncOne(), asyncTwo()]

      Promise
      .race(paramsArr)
      .then((value) => {
        console.log("Promise.race方法的结果", value);
        if (value == "超时提示") {
          this.$message({
            type:"warning",
            message:"接口请求超时了"
          })  
        }else{
          console.log('正常操作即可');
        }
      })
    },
  },
};
</script>

Supongo que te gusta

Origin blog.csdn.net/2201_75705263/article/details/133349292
Recomendado
Clasificación