JS中的异步方案总结

228 阅读6分钟

Promise

  • 优势:使用链式调用解决回调过程中的问题
  1. 基本用法
const promise = new Promise(function(resolve, reject) {
    resolve(100);
    reject(new Error('promise reject'))
})
promise.then(function(value){
    console.log(value)
},function(err){
    console.log(err)
})

// 使用案例
function ajax (url) {
    return new Promise(function (resolve, reject) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url)
        xhr.responseType = 'json';
        xhr.onload = function () {
            if(this.status === 200) {
                resolve(this.response);
            } else {
                reject(new Error(this.responseText));
            }
        }
        xhr.send()
    })
}
ajax('/api/foo.json').then(function (response) {
    console.log(response)
},function (err) {
    console.log(err)
})
  1. 链式调用
    • 可以避免回调嵌套
    • Promise 对象的then 方法会返回一个全新的 Promise 对象
    • 后面的then方法就是在为上一个then返回的 Promise 注册回调
  2. 并行请求
  • all:等待所有任务结束后结束
ajax('api/urls.json')
    .then(value => {
        const urls = Object.values(value);
        const tasks = urls.map(url => ajax(url));
        return Promise.all(tasks)
    })
    .then(value => {
        console.log(value);
    })
  • race:以第一个结束的promise为准
const request = ajax('/api/posts.json')
const timeout = new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error('timeout')), 500)
})

Promise.race([
    request,
    timeout,
])
.then(value => {
    console.log(value)
})
.catch(err => {
    console.log(err)
})
  1. 如果一个报错,其他会执行吗:
  • 实例化的时候就已经执行了

Generator

生成器是一种返回迭代器的函数,通过function关键字后的星号(*)来表示,函数中会用到新的关键字yield。星号可以紧挨着function关键字,也可以在中间添加一个空格.Generator 函数只要传入co函数,就会自动执行。

  • 每当执行完一条yield语句后函数就会自动停止执行, 直到再次调用next();

  • yield关键字只可在生成器内部使用,在其他地方使用会导致程序抛出错误;

  • 可以通过函数表达式来创建生成器, 但是不能使用箭头函数

    let generator = function *(){}

  1. 基本用法
function * foo(){
    console.log('start');
    try{
        const res = yield 'foo'
        console.log(res) // bar
    }catch(e){
        console.log(e) // err
    }
}

const generator = foo();
const result = generator.next();
console.log(result) // { value: 'foo', done: false }

generator.next('bar');
generator.throw(new Error('err'))
  1. 异步方案
function * main(){
    try{
        const users = yield ajax('...')
        console.log(users)

        const posts = yield ajax('...')
        console.log(posts)
    }catch(e){
        console.log(e)
    }
}
function co (generator){
    const g = generator()

    function handleResult(result){
        if(result.done) return
        result.value.then(data=>{
            handleResult(g.next(data))
        }, err => {
            g.throw(err)
        })
    }

    handleResult(g.next())
}
co(main)
  • Promise 解决回调呀地狱的最大问题是代码冗余,原来的任务被 Promise 包装了一下,不管什么操作,一眼看去都是一堆then,原来的语义变得很不清楚。generator的语法解决了这个问题。

Async

  • 优势: 它是 Generator 函数的语法糖。而async函数自带执行器,不需要调用next方法,或者用co模块,就能真正执行; async函数的await命令就是Promise内部then命令的语法糖。
async function main(){
    try{
        const users = await ajax('...')
        console.log(users)

        const posts = await ajax('...')
        console.log(posts)
    }catch(e){
        console.log(e)
    }
}
const promise = main()

promise.then(()=>{
    console.log('completed')
})
  • 利用Async封装一个函数, 能够让generator自动执行到完毕
function longTimeFn(time) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(time);
        }, time);
    })
};

function asyncFunc(generator) {
    const iterator = generator(); // 接下来要执行next
    // data为第一次执行之后的返回结果,用于传给第二次执行
    const next = (data) => {
        const {
            value,
            done
        } = iterator.next(data); // 第二次执行,并接收第一次的请求结果 value 和 done

        if (done) return; // 执行完毕, 直接返回
        // 第一次执行next时,yield返回的 promise实例 赋值给了 value
        value.then(data => {
            next(data); // 当第一次value 执行完毕且成功时,执行下一步(并把第一次的结果传递下一步)
        });
    }
    next();
};

asyncFunc(function* () {
    let data = yield longTimeFn(1000);
    console.log(data);
    data = yield longTimeFn(2000);
    console.log(data);
    return data;
})

Promise原理

// 1.Promise 中有三种状态分别为 成功 fulfilled 失败 rejected 等待 pending;pending -> fulfilled pending -> rejecte d一旦状态确定就不可更改
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
// 2.Promise就是一个类,在执行这个类的时候需要传递一个执行器进去执行
class MyPromise {
    constructor(expector){
        try{
            expector(this.resolve, this.reject)
        }catch(e){
            this.reject(e)
        }
    }
    status = PENDING;
    value = undefined;
    reason = undefined;
    successCallback = [];
    errorCallback = [];
    // 3.resolve和reject函数是用来更改状态的:resolve: fulfilled reject: rejected
    resolve = value=>{
        if(this.status !== PENDING) return;
        this.status = FULFILLED;
        this.value = value;
        while(this.successCallback.length) this.successCallback.shift()();
    }
    reject = reason =>{
        if(this.status !== PENDING) return;
        this.status = REJECTED;
        this.reason = reason;
        while(this.errorCallback.length) this.errorCallback.shift()();

    }
    // 4.then方法内部做的事情就判断状态,如果状态是成功 调用成功的回调函数,失败则调用失败函数;then成功回调有一个参数表示成功之后的值,then失败回调有一个参数表示失败原因
    then(successCallback, errorCallback){
        // 9. 处理空参数
        successCallback = successCallback ?  successCallback : value => value;
        errorCallback = errorCallback ?  errorCallback : reason => {throw reason};
        // 5. then方法是可以被链式调用的,后面then方法的回调函数拿到值的是是上一个then方法的回调函数的返回值
        let promise2 = new Promise((resolve, reject)=>{
            if(this.status === FULFILLED) {
                // 8.解决循环调用问题需要的异步
                setTimeout(()=>{
                    try{
                        let x = successCallback(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    }catch(e){
                        reject(e)
                    }
                },0)
            }else if(this.status === REJECTED){
                setTimeout(()=>{
                    try{
                        let x = errorCallback(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    }catch(e){
                        reject(e)
                    }
                },0)
            }else{
                // 6.处理异步情况、then方法多次调用情况
                this.successCallback.push(()=>{
                    setTimeout(()=>{
                        try{
                            let x = successCallback(this.value)
                            resolvePromise(promise2, x, resolve, reject)
                        }catch(e){
                            reject(e)
                        }
                    },0)
                });
                this.errorCallback.push(()=>{
                    setTimeout(()=>{
                        try{
                            let x = errorCallback(this.reason)
                            resolvePromise(promise2, x, resolve, reject)
                        }catch(e){
                            reject(e)
                        }
                    },0)
                });
            }
            })
        return promise2
    }
}
// 7. 判断x的值是普通值还是promise对象
function resolvePromise(promise2, x, resolve, reject) {
    if(promise2 === x){
        reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if(x instanceof MyPromise){
        // 如果是promise对象,查看promsie对象返回的结果,再根据promise对象返回的结果决定调用
        x.then(resolve, reject)
    }else{
        // 如果是普通值 直接调用resolve
        resolve(x)
    }
}
  1. 基本用法
let promise = new MyPromise((resolve, reject) =>{
    resolve('suc');
    reject('err')
})
promise.then(value =>{
    console.log(value);
}, reason=>{
    console.log(reason);
})
  1. 异步逻辑
let promise = new MyPromise((resolve, reject) =>{
    setTimeout(() => {
        resolve('suc');
    },5000)
})
promise.then(value =>{
    console.log(value);
}, reason=>{
    console.log(reason);
})
  1. then多次调用
let promise = new MyPromise((resolve, reject) =>{
    setTimeout(() => {
        resolve('suc');
    },2000)
})
promise.then(value =>{
    console.log(value);
}, reason=>{
    console.log(reason);
})
promise.then(value =>{
    console.log(value);
}, reason=>{
    console.log(reason);
})
  1. then链式调用(普通值)
let promise = new MyPromise((resolve, reject) =>{
    resolve('suc');
})
promise.then(value =>{
    console.log(value);
    return 100
}).then(value =>{
    console.log(value);
})
  1. then链式调用(Promise对象)
let promise = new MyPromise((resolve, reject) =>{
    resolve('suc');
})
function other(){
    return new MyPromise((resolve, reject) =>{
        resolve('other')
    })
}
promise.then(value =>{
    console.log(value);
    return other()
}).then(value =>{
    console.log(value);
})
  1. then循环调用Promise的处理
let promise = new MyPromise((resolve, reject) =>{
    resolve('suc');
})

let p1 = promise.then(value =>{
    console.log(value);
    return p1
})

p1.then(() =>{},reason=>{
    console.log(reason.message);//Chaining cycle detected for promise #<Promise>
})
  1. 错误处理
    • 执行器
    • then的各种情况
  2. then方法的参数为可选
let promise = new MyPromise((resolve, reject) =>{
    resolve('suc');
})

promise
.then()
.then()
.then(value =>{
    console.log(value);
})
  1. all方法
static all (array){
    let result = [];
    let index = 0;
    return new MPromise((resolve, reject) => {
        for(let i = 0; i < array.length; i++) {
            let current = array[i];
            if(current instanceof MyPromise) {
                current.then(value => {addData(i, value), reason => reject(reason)})
            } else {
                addData(i, array[i])
            }
        }
        function addData(key, value) {
            result[key] = value;
            index++;
            if(index === array.length) {
                resolve(result);
            }
        }
    })
}
  1. resolve方法
  • 返回一个Promise
static resolve(value){
    if(value instanceof MyPromise) return value;
    return new MyPromise(resolve => resolve(value));
}
function p1(){
    return new MyPromise(function(resolve, reject){
        resolve('hello')
    })
}
Promise.resolve(10).then(value => console.log(value)) // 10;
Promise.resolve(p1()).then(value => console.log(value)) // hello
  1. finally方法
  • 用于指定不管 Promise 对象最后状态如何,都会执行的操作
finally(callback){
    return this.then(value => {
        return MyPromise.resolve(callback()).then(()=>value);
    }, reason => {
        return MyPromise.resolve(callback()).then(()=>{throw reason});
    })
}

function p1(){
    return new MyPromise(function(resolve, reject){
        setTimeout(function(){
            resolve('p1')
        },2000)
    })
}
function p2(){
    return new MyPromise(function(resolve, reject){
        resolve('p2')
    })
}
p2().finally(()=>{
    console.log('finally');
    return p1()
}).then(value=>{
    console.log(value);
},reason=>{
    console.log(reason);
})
  1. catch方法
catch(errorCallback){
    return this.then(undefined, errorCallback)
}
function p2(){
    return new MyPromise(function(resolve, reject){
        resolve('p2')
    })
}
p2()
.then(value=>{console.log(value)})
.catch(reason=>{console.log(reason)})
  1. allSeettled,返回所有promise的状态和结果
function PromiseAllSettled(promiseArray) {
    return new Promise(function (resolve, reject) {
        //判断参数类型
        if (!Array.isArray(promiseArray)) {
            return reject(new TypeError('arguments muse be an array'))
        }
        let counter = 0;
        const promiseNum = promiseArray.length;
        const resolvedArray = [];
        for (let i = 0; i < promiseNum; i++) {
            Promise.resolve(promiseArray[i])
                .then((value) => {
                    resolvedArray[i] = {
                        status: 'fulfilled',
                        value
                    };

                })
                .catch(reason => {
                    resolvedArray[i] = {
                        status: 'rejected',
                        reason
                    };
                })
                .finally(() => {
                    counter++;
                    if (counter == promiseNum) {
                        resolve(resolvedArray)
                    }
                })
        }
    })
}