JS异步

异步在实现上,依赖一些特殊的语法规则。从整体上来说,异步方案经历了如下的四个进化阶段:

回调函数 —> Promise —> Generator —> async/await。

1、promise
Promise 会接收一个执行器,在这个执行器里,我们需要把目标的异步任务给”填进去“。
在 Promise 实例创建后,执行器里的逻辑会立刻执行,在执行的过程中,根据异步返回的结果,决定如何使用 resolve 或 reject 来改变 Promise实例的状态。 Promise 实例有三种状态:

• pending 状态,表示进行中。这是 Promise 实例创建后的一个初始态;
• fulfilled 状态,表示成功完成。这是我们在执行器中调用 resolve 后,达成的状态;
• rejected 状态,表示操作失败、被拒绝。这是我们在执行器中调用 reject后,达成的状态。
当我们用 resolve 切换到了成功态后,Promise 的逻辑就会走到 then 中的传入的方法里去;用 reject 切换到失败态后,Promise 的逻辑就会走到 catch 传入的方法中去。
2、Generator
Generator 一个有利于异步的特性是,它可以在执行中被中断、然后等待一段时间再被我们唤醒。通过这个“中断后唤醒”的机制,我们可以把 Generator看作是异步任务的容器,利用 yield 关键字,实现对异步任务的等待。

形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。
Generator 函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是上一章介绍的遍历器对象(Iterator Object)。

下一步,必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止。换言之,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。

const co = require('co');
co(httpGenerator());

这里的 co,大家就可以把它看作是一个加强版的 runGenerator。我们只需要在代码里引入 co 库,然后把写好的 generator 传进去,就可以轻松地实现 generator 异步了。

3、Async/Await
首先,我们用 async 关键字声明一个函数为“异步函数”:

async function httpRequest() {

}

然后,我们就可以在这个函数内部使用 await 关键字了:

async function httpRequest() {
  let res1 = await httpPromise(url1)
  console.log(res1)
}  

这个 await 关键字很绝,它的意思就是“我要异步了,可能会花点时间,后面的语句都给我等着”。当我们给 httpPromise(url1) 这个异步任务应用了 await 关键字后,整个函数会像被“yield”了一样,暂停下来,直到异步任务的结果返回后,它才会被“唤醒”,继续执行后面的语句。

注:async/await 和 generator 方案,相较于 Promise 而言,有一个重要的优势:Promise 的错误需要通过回调函数捕获,try catch 是行不通的。而 async/await 和 generator 允许 try/catch。这也是一个可以作为命题点细节,大家留心把握。

4、Promise命题思路

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

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

2.Promise 的出现是为了解决什么问题?
回调地狱问题

3.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.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号选手"
});

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

then 方法的入参只能是函数

4.手写Promise

function CutePromise(executor) {
    // value 记录异步任务成功的执行结果
    this.value = null;
    // reason 记录异步任务失败的原因
    this.reason = null;
    // status 记录当前状态,初始化是 pending
    this.status = 'pending';
  
    // 缓存两个队列,维护 resolved 和 rejected 各自对应的处理函数
    this.onResolvedQueue = [];
    this.onRejectedQueue = [];
         
    // 把 this 存下来,后面会用到
    var self = this;
  
    // 定义 resolve 函数
    function resolve(value) {
        // 如果不是 pending 状态,直接返回
        if (self.status !== 'pending') {
            return;
        }
        // 异步任务成功,把结果赋值给 value
        self.value = value;
        // 当前状态切换为 resolved
        self.status = 'resolved'; 
        // 用 setTimeout 延迟队列任务的执行
        setTimeout(function(){
            // 批量执行 resolved 队列里的任务
            self.onResolvedQueue.forEach(resolved => resolved(self.value)); 
        });
    }
        
    // 定义 reject 函数
    function reject(reason) {
        // 如果不是 pending 状态,直接返回
        if (self.status !== 'pending') {
            return;
        }
        // 异步任务失败,把结果赋值给 value
        self.reason = reason; 
        // 当前状态切换为 rejected
        self.status = 'rejected';
        // 用 setTimeout 延迟队列任务的执行
        setTimeout(function(){
            // 批量执行 rejected 队列里的任务
            self.onRejectedQueue.forEach(rejected => rejected(self.reason));
        });
    }
  
    // 把 resolve 和 reject 能力赋予执行器
    executor(resolve, reject);
}

// then 方法接收两个函数作为入参(可选)
CutePromise.prototype.then = function(onResolved, onRejected) {
  
    // 注意,onResolved 和 onRejected必须是函数;如果不是,我们此处用一个透传来兜底
    if (typeof onResolved !== 'function') {
        onResolved = function(x) {return x};
    }
    if (typeof onRejected !== 'function') {
        onRejected = function(e) {throw e};
    }
 
    // 依然是保存 this
    var self = this;
    // 判断是否是 resolved 状态
    if (self.status === 'resolved') {
        // 如果是 执行对应的处理方法
        onResolved(self.value);
    } else if (self.status === 'rejected') {
        // 若是 rejected 状态,则执行 rejected 对应方法
        onRejected(self.reason);
    } else if (self.status === 'pending') {
        // 若是 pending 状态,则只对任务做入队处理
        self.onResolvedQueue.push(onResolved);
        self.onRejectedQueue.push(onRejected);
    }
    return this
};

5、Promise/A+ —— 决议过程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值