javaScript异步编程

一、单线程:
js执行环境中负责执行代码的线程只有一个
优点:安全,简单
缺点:耗时任务,等待拖延,出现假死
二、
1、单线程中的同步模式:
排队依次执行,调用栈,当某个任务执行时间过长就会延时,这叫阻塞。

2、单线程 中的异步模式:
为了解决同步中阻塞情况。
开启过后立即往后下一个任务。一般会通过回调函数的方式定义。

EventLoop(事件循环) 事件循环会监听调用栈,当调用栈中没有任务了,就会从消息队列中取出第一个回调函数,压入调用栈
事件循环是用于异步调用的重要工具。

异步调用图解:
在这里插入图片描述
3、回调函数,所以异步编程的根基
由调用者定义,交给执行者执行的函数

4、Promise,为异步编程提供更标准的规范。
promise是一个承诺,有三种状态,pendding,Fulfilled,Rejected。
当承诺的结果最终明确后会有自动的任务会被执行,成功的回调和失败的回调

promis的then方法的作用是为promise添加状态明确后的回调函数
第一个参数,成功回调。。
第二个参数,失败过后的回调
特点:
a、then方法返回的是一个全新promise对象。为了实现一个链条调用
b、每一个then方法是为上一个then方法返回的promise对象去添加状态明确过后回调。

c、promise的then方法中的回调函数如果返回的是一个promise对象,那下一个then方法就会等待返回的promise状态明确过后添加回调 。就是返回的这个promise的then方法中的回调,依次类推,实现了代码扁平化。
d、如果promise的then方法中的回调函数返回的是一个普通对象,那下个then方法中的回调函数的参数就是这个返回的普通值。
e、如果then方法中的回调没有返回值,默认返回的是undefind。

promise异步处理
onRejected回调函数是为promise中的异常去做处理。

5、宏任务与微任务
宏任务可以理解为在执行站中执行的任务,包括从事件队列中获取回调执行。
微任务是宏任务执行完后立即被执行的任务。
宏任务与微任务执行的关系是:
在执行站中执行的任务中,当遇到宏任务如(setimeout…),会将宏任务放入宏任务事件队列中,往后执行;当遇到微任务(promise,nextTick…)时将微任务放入微任务事件队列中。执行站中当前执行的任务全部执行完后,eventLoop将会监听异步事件队列,首先看微任务事件队列中是否有微任务,如果有把队列中的所有微任务依次执行直到没有了微任务;这时再获取到宏任务事件队列中的回调执行,这时如果有遇到微任务同样先放入微任务事件队列中,遇到宏任务向宏任务事件队列后面放入。当执行站中的任务执行完,又会执行微任务。然后再调用下一个宏任务,依次循环直到所有任务都完成。

7 、Promise
1、Promise是一个类 在执行这个类的时候 需要传递一个执行器进去 执行器会立即执行
2、Promise中分别有三种状态,分别为 成功 fulfilled 失败 rejected 等待pending
pending -> fulfilled
pending ->rejected
一旦状态确定就不可更改
3、resolve和reject函数是用来更改状态的
resolve: fulfilled
reject:rejected

1、手写Promise

/* 1、Promise是一个类,在执行这个类时 需要传递一个执行器 执行器会立即执行。
2、Promise中有三种状态, 分别为 成功 fulfilled 失败 rejected 等待 pending
pending -> fulfilled
pending -> rejected 
状态确定了是不可逆的
3、resolve和reject函数是用来更改状态的
resolve: fulfilled
reject: rejected 
4、then方法内部做的事情就是判断状态 如果状态是成功 调用成功的回调函数  如果状态失败调用失败的回调函数 then方法是定义在原型对象上的方法
5、then方法成功状态下,执行成功函数会传递成功后的参数,失败状态下执行失败回调函数会传递失败原因参数。
6、有时不止执行一次promise的then方法,会多次执行。此时分两种情况,
    如果是同步执行会依次执行,成功方法成功,失败返回失败原因。
    如果是异步执行它不会依次执行,也不会等待,这时就要将依次执行的then方法中的回调都依次保存起来
7、promise中then方法可以被链式调用,并且下一个then方法的回调函数的参数,是上一个then方法回调函数执行的返回值。
8、在promise中,then方法的回调函数返回的promise对象,不能是当前then方法返回的promise,不能自己返回自己。
9、promise会捕获执行器错误
10、如果当前then方法的回调函数执行报错了,那要在下一个then方法的错误回调中捕获到。
11、在promise中可以实现 ,前面的then不传参数,这个转态会依次传递,直到有回调函数的then方法拿到结果。
12、Promise.all方法, 是用来解决异步并发问题的,它允许我们,按照异步代码调用的顺序,拿到异步代码执行的结果。
    接收一个数组作为参数,这个数组中可以有普通值和promise对象,数组的顺序一定是结果的顺序
    返回值也是一个promise对象,所以可以链式调用then方法
    在all方法中当所有的promise是成功的最后的结果才是成功的,如果有个promise是失败结果就是失败的。
13、Promise.resolve方法, 是将给定的值转换成Promise对象,在返回的这个Promise对象当中会包裹给定的这个值
    resolve方法,也可以接收一个Promise对象, 并在返回的Promise对象的then方法中拿到这个promise对象中的返回的结果。

14、Promise.finally方法
    无论当前的Promise对象是成功的还是失败的,finally中的回调都会被执行一次。
    在finally方法的后面可以调用then方法来拿到当前Promise对象中的返回结果
    不是静态方法
    在finally中回调函数中返回的Promise对象,finally方法后的then方法会等待返回的Promise执行完后的结果。

*/


// 定义状态常量。便于复用
const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected' // 失败
class MyPromise {
    // 在类中用constructor中接受执行器executor
    constructor (executor) {
        try {
            // 这个执行器会立即执行、并传递两个参数resolve和reject
            executor(this.resolve, this.reject)
        } catch (error) {
            // 当执行器执行错误后会被catch捕获,这时执行reject并返回错误信息。
            this.reject(error)
        }
    }
    // 定义这个两个参数,resolve,reject是用来改变状态的,并且是实例方法。
    // 这两个方法要使用箭头函数来定义,因为要让它在被执行时的this指向点前的类的实例对象,而不是指向全局对象window 
    status = PENDING; // 默认当前状态为等待
    // 定义两个变量用于保存成功值和失败原因
    value = undefined; // 一开始成功是没有的,定义为undefined
    reason = undefined; // 一开始失败是没有的,定义为undefined
    // 定义实例属性,保存成功回调和失败回调
    // 因为会存在多次执行then方法的时候,会有多个回调函数,所以要将属性定义为数组。
    successCallback = [];
    failCallback = [];
    resolve = (value) => {
        // 因为状态一旦确定就不可更改,此时要判断当前状态, 如果状态不是pending就要阻止程序往下执行。
        if (this.status !== PENDING) return;
        // 将状态更改为成功
        this.status = FULFILLED;
        // 保存成功后的值
        this.value = value;
        // 当异步代码完成时,如果是成功会执行resolve,此时就要执行成功回调
        // 先判断成功回调是否存在,如果存在 执行, 并把成功后的值传递
        // 当存在多个then方法被执行的时候会有多个成功回调,当异步代码完成时会执行resolve,依次往后取出回调来执行,直到没有回调函数了。
        // this.successCallback && this.successCallback(this.value);
        while(this.successCallback.length) {
            // shift方法是从数组里取第一个值,并且放回取到的第一个值,所以可以直接执行这个回调,并传递成功结果。由于shift方法会改变原数组,每取一个减少一个
            // 所以用数组长度来判断是否执行完了回调
            this.successCallback.shift()();
        }
    }
    reject = (reason) => {
        // 因为状态一旦确定就不可更改,此时要判断当前状态, 如果状态不是pending就要阻止程序往下执行。
        if (this.status !== PENDING) return;
        // 将状态更改为失败
        this.status = REJECTED;
        // 保存失败的原因
        this.reason = reason;
        // 当异步代码完成时,如果是失败会执行resolve,此时就要执行失败回调
        // 先判断失败回调是否存在,如果存在 执行, 并把失败后的原因传递
        // 当存在多个then方法被执行的时候会有多个失败回调,当异步代码完成时会执行reject,依次往后取出回调来执行,直到没有回调函数了。
        // this.failCallback && this.failCallback(this.reason);
        while(this.failCallback.length) {
            // shift方法是从数组里取第一个值,并且放回取到的第一个值,所以可以直接执行这个回调,并传递失败原因。由于shift方法会改变原数组,每取一个减少一个
            // 所以用数组长度来判断是否执行完了回调
            this.failCallback.shift()();
        }
    }
    then (successCallback, failCallback) {
        // then没有传参的情况下给到最后一个then,需要设置一个默认的回调函数,这个回调函数就返回当前转态。
        successCallback = successCallback ? successCallback : value => value;
        failCallback = failCallback ? failCallback : reason => {throw reason}; 
        // 要实现then方法的链式调用,就要在原有的then方法中返回一个promise,传递一个执行器,这个执行器要被立即执行,
        let promise2 = new MyPromise((resolve, reject) => {
            // 判断状态
            if (this.status ===  FULFILLED) {
                // 在promise2内部是拿不到promise2的,所以要将下面需要拿到promise2的代码作为异步代码,就可以拿到promise2
                setTimeout(() => {
                    // 捕获成功回调函数的报错
                    try {
                        // 如果成功状态,执行成功回调函数,并传递成功后的值
                        let x = successCallback(this.value);
                        // 要实现then方法中的回调函数的的返回值是下个then方法的回调函数的参数,就要在下一个then方法被调用时,执行器里的resolve方法传递上一个then方法回调返回值
                        // resolve(x)
                        // promisethen方法的回调函数可以返回一个promise,这个返回的promise的结果就是下一个then方法的回调函数的参数。
                        // 1、判断x的值是普通值还是promise对象
                        // 2、如果是普通值直接调用resolve
                        // 3、如果是promise对象 看promise对象返回的结果
                        // 4、再根据promise对象返回的结果、决定调用resolve还是调用reject
                        // x是then方法的回调函数返回的值,promise2是then方法返回值,判断两者是否相等就知道是否自己返回了自己
                        // 将以上判断提取到另一个函数中
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                    
                }, 0)

            } else if (this.status === REJECTED) {
                setTimeout(()=> {
                    // 捕获失败回调错误
                    try {
                        // 如果失败状态,执行失败回调函数, 并传递失败的原因
                        let x = failCallback(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error)
                    }
                }, 0)
                
            } else {
                // 当出现异步代码时,因为不会等待异步代码而是往下执行,这时的then方法被执行,但是,resolve或者reject没有被立即执行,这时的状态为pending。
                // 如果是等待状态, 当为异步代码时,会处在等待的状态,等到异步处理完成后不知道该调用成功回调还是失败回调,因此在等待状态先保存成功回调和失败回调。
                // 保存回调, 因为存在多次调用then方法的情况会有多个回调,都要保存
                // this.successCallback.push(successCallback);
                // this.failCallback.push(failCallback);
                // 当有多个回调函数的时候,也要对每个回调函数做错误处理.,这时就要保存一个函数,这个函数执行这个回调.
                this.successCallback.push(() => {
                    // 在promise2内部是拿不到promise2的,所以要将下面需要拿到promise2的代码作为异步代码,就可以拿到promise2
                    setTimeout(() => {
                        // 捕获成功回调函数的报错
                        try {
                            // 如果成功状态,执行成功回调函数,并传递成功后的值
                            let x = successCallback(this.value);
                            // 要实现then方法中的回调函数的的返回值是下个then方法的回调函数的参数,就要在下一个then方法被调用时,执行器里的resolve方法传递上一个then方法回调返回值
                            // promisethen方法的回调函数可以返回一个promise,这个返回的promise的结果就是下一个then方法的回调函数的参数。
                            // 1、判断x的值是普通值还是promise对象
                            // 2、如果是普通值直接调用resolve
                            // 3、如果是promise对象 看promise对象返回的结果
                            // 4、再根据promise对象返回的结果、决定调用resolve还是调用reject
                            // x是then方法的回调函数返回的值,promise2是then方法返回值,判断两者是否相等就知道是否自己返回了自己
                            // 将以上判断提取到另一个函数中
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                        
                    }, 0)
                });
                this.failCallback.push(() => {
                    setTimeout(() => {
                        // 捕获失败回调错误
                        try {
                            // 如果失败状态,执行失败回调函数, 并传递失败的原因
                            let x = failCallback(this.reason);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (error) {
                            reject(error)
                        }
                    })
                })

            }
        });
        
        return promise2;
    }

    finally(callback) {
        // 无论成功和失败,回调函数都会执行,在类中的then方法中就有成功和失败的回调, finally函数中可以再次调用then,说明返回Promise对象,
        // then 方法本身就返回Promise,
        return this.then(value => {
            // 如果callback返回的是一个Promise,要将该promise的值传给下个then
            // 不管callback返回的是否为Promise对象,都将它转为Promise对象,并将当前then方法的参数value返回,此时如果finally调用了then方法就会拿到上个then返回的value值。
            return MyPromise.resolve(callback()).then(() => value);
        }, reason => {
            return MyPromise.resolve(callback()).then(() => {throw reason})
        })

    }
    catch(failCallback) {
        // catch方法是执行失败后的回调,只要让它执行then方法,第一参数为undefind。
        return this.then(undefined, failCallback);
    }
    // all方法是被类直接调用的,所以定义为静态方法
    static all (array) { //all方法接收一个数组,返回一个promise
        let result  = []; // 用于放回调函数返回的结果
        let index = 0;
        return new Promise((resolve, reject) => {
            // 这里有一个问题,当执行数组中的promise对象时是异步的代码,当for循环结束后,promise还没有执行完
            // 在每一次执行完数据添加后,index加一,最后判断index是否和array的长度相等。如果相等表明数组中的函数执行完了。
            function addData (key, value) {
                result[key] = value;
                index++;
                if (index === array.length) {
                    resolve(result);
                }
            }
            // 将数组中的每一项判断,如果是普通值,直接返回,如果是promise对象,将结果返回。、
            for (let i = 0; i < array.length; i++) {
                // 记录当前值
                let current = array[i];
                if (current instanceof MyPromise) {
                    // Promise 对象
                    current.then(value => addData(i, value), reason => reject(reason))
                } else {
                    // 普通值, 将值保存到数组中
                    addData(i, array[i]);
                }
            }
            
        })

    }
    
    static resolve (value) {
        if (value instanceof MyPromise) return value;
        return new MyPromise(resolve => resolve(value))
    }
}

function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
        // 如果两者相等,要执行reject,并且抛出错误,并且不能再往下执行,直接return
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
    }
    if (x instanceof MyPromise) {
        // 如果是promise对象,根据then方法,如果成功调用resolve,失败调用reject, 此时的resolve和reject就是promise类中的实例方法resolve和reject,所以可以简写。
        // x.then(value => resolve(value), reason => reject(reason))
        x.then(resolve, reject);
    } else {
        resolve(x);
    }
}
// 导出定义的promise,使用node的commonjs方式导出
module.exports = MyPromise;
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值