异步解决方案

在js中任务的执行模型有两种:同步模式异步模式

  • 同步模式:后一个任务B等待前一个任务A结束后,再执行。任务的执行顺序和任务的排序顺序是一致的。

  • 异步模式:每一个任务有一个或多个回调函数,前一个任务A结束后,不是执行后一个任务B,而是执行任务A的回调函数。而后一个任务B是不等任务A结束就执行。任务的执行顺序,与任务的排序顺序不一致。

异步在实现上,依赖一些特殊的语法规则。从整体上来说,异步方案经历了如下的四个进化阶段:回调函数 —> Promise —> Generator —> async/await。其中 Promise、Generator 和 async/await 都是在 ES2015 之后,慢慢发展起来的、具有一定颠覆性的新异步方案。

“回调函数” 时期

事件监听:任务的执行顺序与代码的编写顺序无关,只与点击事件有没有被触发有关

发布订阅:任务执行的时机和某一事件的发生紧密关联了起来。

 

回调函数:回调地狱导致可读性和可维护性被破坏

 Promise 

 说说你理解的 Promise(三种状态,两个过程)

Promise 对象是一个代理对象。它接受你传入的 executor(执行器)作为入参,允许你把异步任务的成功和失败分别绑定到对应的处理方法上去。一个 Promise 实例有三种状态:

pending 状态,表示进行中。这是 Promise 实例创建后的一个初始态;
fulfilled(resolved) 状态,表示成功完成。这是我们在执行器中调用 resolve 后,达成的状态;
rejected 状态,表示操作失败、被拒绝。这是我们在执行器中调用 reject后,达成的状态;
Promise实例的状态是可以改变的,但它只允许被改变一次。当我们的实例状态从 pending 切换为 rejected 后,就无法再扭转为 fulfilled,反之同理。当 Promise 的状态为 resolved 时,会触发其对应的 then 方法入参里的 onfulfilled 函数;当 Promise 的状态为 rejected 时,会触发其对应的 then 方法入参里的 onrejected 函数。

Promise 常见方法有哪些?各自是干嘛的?

Promise的方法有以下几种:
Promise.all(iterable):这个方法返回一个新的 promise 对象,该 promise 对象在 iterable 参数对象里所有的 promise 对象都成功的时候才会触发成功,一旦有任何一个 iterable 里面的 promise 对象失败则立即触发该 promise 对象的失败。

var p1 = Promise.resolve('1号选手');
var p2 = '2号选手';
var p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, "3号选手");
}); 
Promise.all([p1, p2, p3]).then(values => { 
  console.log(values); //  ["1号选手", "2号选手", "3号选手"]
});

使用场景:执行某个操作需要依赖多个接口请求回的数据,且这些接口之间不存在互相依赖的关系。这时使用Promise.all(),等到所有接口都请求成功了,它才会进行操作。

let promise1 = new Promise((resolve) => {
        setTimeout(() => {
            resolve('promise1操作成功');
            console.log('1')
        }, 3000);
    });
   
    let promise2 = new Promise((resolve) => {
        setTimeout(() => {
            resolve('promise1操作成功');
            console.log('2')
        }, 1000);
    });

    Promise.all([promise1, promise2]).then((result) => {
        console.log(result);
    });

Promise.race(iterable):当 iterable 参数里的任意一个子 promise 被成功或失败后,父 promise 马上也会用子 promise 的成功返回值或失败详情作为参数调用父 promise 绑定的相应处理函数,并返回该 promise 对象。

var p1 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 100, "1号选手"); 
});
var p2 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 50, "2号选手"); 
});

// 这里因为 2 号选手返回得更早,所以返回值以 2号选手为准
Promise.race([p1, p2]).then(function(value) {
  console.log(value); //  "2号选手"
});
let promise1 = new Promise((resolve) => {
        setTimeout(() => {
            resolve('promise1操作成功');
            console.log('1')
        }, 3000);
    });

    let promise2 = new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('promise1操作失败');
            console.log('2')
        }, 1000);
    });

    Promise.race([promise1, promise2])
        .then((result) => {
            console.log(result);
        })
        .catch((error) => {
            console.log(error);
        })

 1s后,promise2进入rejected状态,由于一个实例的状态发生了变化,所以Promise.race()就立刻执行了,其他实例中再发生变化,它也不管了。

  • Promise.reject(reason): 返回一个状态为失败的Promise对象,并将给定的失败信息传递给对应的处理方法
  • Promise.resolve(value):它返回一个 Promise 对象,但是这个对象的状态由你传入的value决定,情形分以下两种:
    • 如果传入的是一个带有 then 方法的对象(我们称为 thenable 对象),返回的Promise对象的最终状态由then方法执行决定
    • 否则的话,返回的 Promise 对象状态为 fulfilled,同时这里的 value 会作为 then 方法中指定的 onfulfilled 的入参

真题分析

Promise 中的处理函数是异步任务

const promise = new Promise((resolve,reject) =>{
        console.log(1);
        resolve();
        console.log(2);
    })
    promise.then(()=>{
        console.log(3);
    })
    console.log(4);

 then 方法中传入的任务是一个异步任务。resolve() 这个调用,作用是将 Promise 的状态从 pending 置为 fulfilled,这个新状态会让 Promise 知道“我的 then 方法中那个任务可以执行了”——注意是”可以执行了“,而不是说”立刻就会执行“。毕竟作为一个异步任务,它的基本修养就是要等同步代码执行完之后再执行。所以说数字 3 的输出排在最后。

Promise 对象的状态只能被改变一次

const promise = new Promise((resolve,reject) =>{
        resolve('第1次resolve')
        console.log('resolve后的普通逻辑1');
        reject('error')
        resolve('第2次resolve');
        console.log('resolve后的普通逻辑2');
    })
    promise.then((res)=>{
        console.log('then:',res);
    }).catch((err)=>{
        console.log('catch',err);
    })

 这段代码里,promise 初始状态为 pending,我们在函数体第一行就用 resolve 把它置为了 fulfilled 态。这个切换完成后,后续所有尝试进一步作状态切换的动作全部不生效,所以后续的 reject、resolve大家直接忽略掉就好;需要注意的是,我们忽略的是第一次 resolve 后的 reject、resolve,而不是忽略它身后的所有代码。因此 console.log(‘resolve后的普通逻辑’) 这句,仍然可以正常被执行。至于这里为啥它输出在 ”then: 第 1 次 resolve“ 的前面,原因和上一题是一样一样的~

Promise 值穿透

Promise.resolve(1)
  .then(Promise.resolve(2))
  .then(3)
  .then()
  .then(console.log)

then 方法里允许我们传入两个参数:onFulfilled(成功态的处理函数)和 onRejected(失败态的处理函数)。

你可以两者都传,也可以只传前者或者后者。但是无论如何,then 方法的入参只能是函数。万一你想塞给它一些乱七八糟的东西,它就会“翻脸不认人”。

具体到我们这个题里,第一个 then 方法中传入的是一个 Promise 对象,then 说:”我不认识“;第二个 then 中传入的是一个数字, then 继续说”我不认识“;第四个干脆啥也没穿,then 说”入参undefined了,拜拜“;直到第五个入参,一个函数被传了进来,then 哭了:”终于等到一个我能处理的!“,于是只有最后一个入参生效了。

在这个过程中,我们最初 resolve 出来那个值,穿越了一个又一个无效的 then 调用,就好像是这些 then 调用都是透明的、不存在的一样,因此这种情形我们也形象地称它是 Promise 的“值穿透”。

 实例

 现在有三个请求,请求A、请求B、请求C。请求C要将请求B的请求回来的数据做为参数,请求B要将请求A的请求回来的数据做为参数。

 回调写法

$.ajax({
        success:function(res1){
            //------请求B 开始----
            $.ajax({
                success:function(res2){
                    //----请求C 开始---
                    $.ajax({
                        success:function(res3){
                        }
                    });
                    //---请求C 结束---
                }    
            });
            //------请求B 结束-----
        }
    });
    //------请求A 结束---------

promise写法

let promise = new Promise((resolve, reject) => {
        if (true) {
            //调用操作成功方法
            resolve('操作成功');
        } else {
            //调用操作异常方法
            reject('操作异常');
        }
    });

    //then处理操作成功,catch处理操作异常
    promise.then(requestA)
        .then(requestB)
        .then(requestC)
        .catch(requestError);

    function requestA() {
        console.log('请求A成功');
        return '下一个是请求B';
    }

    function requestB(res) {
        console.log('上一步的结果:' + res);
        console.log('请求B成功');
        return '下一个是请求C';
    }

    function requestC(res) {
        console.log('上一步的结果:' + res);
        console.log('请求C成功');
    }

    function requestError() {
        console.log('请求失败');
    }

原文地址:https://www.cnblogs.com/yuyujuan/p/13274447.html