浅谈JavaScript异步发展与Promise/A+原理实现

首先一提到Promise 我们首先想到的就是异步编程。异步编程在JavaScript中我们最早接触的是callback形式的异步,callback形式的异步编程最大的特点就是地狱式回调嵌套,一旦嵌套次数过多,就很容易使我们的代码难以理解和维护。而Permise的出现是为了更好的解决JavaScript中异步编程的问题。

js中异步的方式有哪些?

1.回调函数callback形式:

    $.get(URL,function(res) {
        if(res) {
            $.get(URL,function(res) {
                if(res) {
                    $.get(URL,function(res) {
                        if(res) {
                        }
                    });
                }
            });
        }
    });
复制代码
这就是传说中的回调地狱!!!  
复制代码

2.ES6 Promise 对象:

let p = new Promise(function (resolve, reject) {
    resolve('第一次成功了')
});
p.then(function (res) { //第一次promise的结果
    console.log(res)
    let next_p = new Promise(function (resolve, reject) {
        reject('第二次失败功了')
    });
    return next_p
}, function (err) {
    console.log(err)
}).then(function (res) { //第二次permise的结果
    console.log(res)
    
}, function (err) {
    console.log(err)
})
复制代码
上面是简单的一个 es6 promise对象的用法 代码结构上来看,并没有像 callback回调那样出现回调地狱的形式,而是常见的链式调用,(如jquery)。
复制代码

3.ES6 Generator 函数 :

  Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态.  
  执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,  
可以依次遍历 Generator 函数内部的每一个状态。  
  形式上,Generator 函数是一个普通函数,但是有两个特征:
  一是,function关键字与函数名之间有一个星号;  
  二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)
复制代码
function* read() {
    console.log(1);
    let a = yield '第一次';
    console.log(a);
    let b = yield '第二次'
    console.log(b);
    return b;
}
let it = read();

console.log(it.next('111')); // {value:'第一次',done:false}
console.log(it.next('222')); // {value:'第二次',done:false}
console.log(it.next('333')); // {value:'333',done:true}
复制代码
Generator 应用场景 主要还是要与promise 配合一起使用  
Generator 和 co:  
nodejs出现了 co 模块,它基于 ES6 的 generator 和 yield ,让我们能用同步的形式编写异步代码。  
复制代码
co(function *() {
    var data = yield $.get('/api/data');
    console.log(data);
    var user = yield $.get('/api/user');
    console.log(user);
    var products = yield $.get('/api/products');
    console.log(products);
});
复制代码
以上的 Promise 和 generator 最初创造它的本意都不是为了解决异步流程控制。其中 Promise 是一种编程思想,用于“当xx数据准备完毕,  
then执行xx动作”这样的场景,不只是异步,同步代码也可以用 Promise。而 generator 在 ES6 中是迭代器生成器,被 TJ 创造性的拿来做异步流程  
控制了
复制代码

4.ES7 async / await 语法糖

async / await co + generator的语法糖  
async / await 解决的问题有哪些?  
1.回调地狱  
2.并发执行异步,在同一时刻同步返回结果 Promise.all()  
3.解决了返回值的问题  
4.可以实现代码的try/catch;
复制代码
async function r(){
    try{
        let content1 = await read('./2.promise/100.txt','utf8');
        let content2 = await read(content1,'utf8');
        return content2;
    }catch(e){ // 如果出错会catch
        console.log('err',e)
    }
}
// async函数返回的是promise,
r().then(function(res){
    console.log(res);
},function(err){
    console.log(err);
})
复制代码

Promise 扮演着一个承上启下的作用,非常关键,我们动手写一个自己的Promise!

                    ---->(fulfilled)成功态 --->resolve();
                    |
    new Promise ----(pending)  初始化为等待态
                    |  
                    ---->(rejected)失败态 --->reject();
复制代码
function Promise(executor) {
    let _this = this;
    //promise的 三种状态
    _this.status = 'pending'; //等待态 初始值 实例化promise一开始时是等待状态 唯一性
    _this.value = undefined;  //成功态 表示成功时要传的值 初始默认值
    _this.error = undefined; //失败态 表示失败时要传的值 
    _this.onFufilledCallbacks = []; // 存放then成功的回调函数
    _this.onRejectedCallbacks = []; // 存放then失败的回调函数
    //成功的方法
    function resolve(value) {    
        if (_this.status === 'pending') {
            _this.status = 'fulfilled';  //当调用 resolve()时改为成功态 
            _this.value = value;
            //遍历 存放then成功的回调函数 并一次调用
            _this.onFufilledCallbacks.forEach(function (itemFn) {
                itemFn();
            });
        };
    };
    //失败的方法
    function reject(error) {     
        if (_this.status === 'pending') {
            _this.status = 'rejected'; //当调用 reject()时改为失败态
            _this.error = error;
            _this.onRejectedCallbacks.forEach(function (itemFn) {
                itemFn();
            });
        }

    };
    //处理 实例化时 throw new err('错误') 需要走reject();!!!
    try {
        executor(resolve, reject); //executor执行器,包含两个参数,分别是resolve() 和reject(),new Promise这个executor就会立即执行
    } catch (err) { //捕获时发生异常,就直接失败
        reject(err)
    }
}
function resolvePromise(promise2, x, resolve, reject) {
    // 有可能这里返回的x是别人的promise
    // 尽可能允许其他乱写
    if (promise2 === x) { //这里应该报一个类型错误,有问题
        return reject(new Typeerr('循环引用了'))
    }
    // 看x是不是一个promise,promise应该是一个对象
    let called; // 表示是否调用过成功或者失败
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        // 可能是promise {},看这个对象中是否有then方法,如果有then我就认为他是promise了
        try { // {then:1}
            let then = x.then;
            if (typeof then === 'function') {
                // 成功
                then.call(x, function (y) {
                    if (called) return
                    called = true
                    // y可能还是一个promise,在去解析直到返回的是一个普通值
                    resolvePromise(promise2, y, resolve, reject)
                }, function (err) { //失败
                    if (called) return
                    called = true
                    reject(err);
                })
            } else {
                resolve(x)
            }
        } catch (e) {
            if (called) return
            called = true;
            reject(e);
        }
    } else { // 说明是一个普通值1
        resolve(x); // 表示成功了
    }
}


// then方法注册 当resolve(成功)/reject(失败)的回调函数
Promise.prototype.then = function (onFufilled, onRejected) {
    //成功和失败默认不传给一个函数
    onFufilled = typeof onFufilled === 'function' ? onFufilled : function (value) {
        return value;
    }
    onRejected = typeof onRejected === 'function' ? onRejected : function (err) {
        throw err;
    }
    let _this = this;
    let promise2; //返回的promise 处理 .then() 的链式调用 返回一个新的promise对象
    
    if (_this.status === 'fulfilled') {
        promise2 = new Promise(function (resolve, reject) {
            // 当成功或者失败执行时有异常那么返回的promise应该处于失败状态
            // x 是return的返回值 可能是一个promise 也可能是一个普通值
            setTimeout(function () { //用setTimeout来模拟异步,真正的es6promise不是用setTimeout来实现的
                try {
                    let x = onFufilled(_this.value)
                    // x可能是别人promise,写一个方法统一处理
                    resolvePromise(promise2, x, resolve, reject)
                } catch (err) {
                    reject(err);
                }
            });
        });

    };
    if (_this.status === 'rejected') {
        //当成功或者失败执行时有异常时 那么返回的 promise2应该处于失败状态
        promise2 = new Promise(function (resolve, reject) {
            setTimeout(function () {
                try {
                    let x = onRejected(_this.error); // x 是return的返回值 可能是一个promise 也可能是一个普通值
                    resolvePromise(promise2, x, resolve, reject)
                } catch (err) {
                    reject(err);
                }
            });
        });
    };
    //当调用 then 时 有可能没成功也没失败 还是pending 需要把回调先存起来
    if (_this.status === 'pending') {
        promise2 = new Promise(function (resolve, reject) {
            _this.onFufilledCallbacks.push(function () {
                setTimeout(function () {//用setTimeout来模拟异步,真正的es6promise不是用setTimeout来实现的
                    try {
                        let x = onFufilled(_this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (err) {
                        reject(err)
                    }
                });
            });
            _this.onRejectedCallbacks.push(function () {
                setTimeout(function () {
                    try {
                        let x = onRejected(_this.error);
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (err) {
                        reject(err)
                    }
                });
            });
        });
    };
    return promise2
}

复制代码
使用自己的封装的promise:
复制代码

let p = new Promise(function(resolve,reject) {
    
        resolve('success');
   
});
p.then(function(data) {
    console.log('成功:',data)
    return 'zhanghw'
},function(err) {
    console.log('失败:',err)
}).then(function(data) {
    console.log(data)
},function(err) {
    console.log(err)
})
复制代码
打印结果:  
成功: success
zhanghw
复制代码
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值