ES6-promise

异步编程简述:
  • 无论是在浏览器环境中还是在node环境中,我们都会使用JavaScript
  • 完成各种异步操作,如在浏览器环境中的定时器、事件、ajax等或是node环
  • 境中的文件读取、事件等。伴随着异步编程的就是回调机制(复习jQuery)。
  • 明确一点异步编程避免不了回调。
let obj = {};

let fs = require('fs');

fs.readFile('./data/name.txt', 'utf-8', (err, data) => {
    obj.name = data;
    Store.fire(obj);
});
fs.readFile('./data/number.txt', 'utf-8', (err, data) => {
    obj.number = data;
    Store.fire(obj);
});
fs.readFile('./data/score.txt', 'utf-8', (err, data) => {
    obj.score = data;
    Store.fire(obj);
});


let Store = {
    list: [],
    times: 3,
    subscribe (func) {
        this.list.push(func);
    },
    fire (...args) {
        --this.times == 0 && this.list.forEach(ele=>{
            ele.apply(null, args);
        })
    }
}
Store.subscribe(show);

function show (data) {
    console.log(data);
}
复制代码
异步编程问题:
  • 产生回调地狱,难于维护和扩展。
  • try catch只能捕获同步代码中出现的异常。
  • 同步并发的异步存在一定的问题。
解决方案:
  • ES6 Promise可以解决回调地狱、以及同步并发的异步问题。(异步操作
  • 的异常捕获有其他方式解决。)
  • 但依旧会有明显的回调痕迹,之后学习ES6的generator 、ES7的
  • async await争取让异步代码看起来和同步一样。写起来更优雅一些。

Promise(.then)
微任务和宏任务
console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});

console.log('script end');
复制代码

打印顺序是什么?

正确答案是: ==script start, script end, promise1, promise2, setTimeout==

  • 每个线程都会有它自己的event loop(事件循环),所以都能独立运行。然而所有同源窗口会共享一个event loop以同步通信。event loop会一直运行,来执行进入队列的宏任务。一个event loop有多种的宏任务源(译者注:event等等),这些宏任务源保证了在本任务源内的顺序。但是浏览器每次都会选择一个源中的一个宏任务去执行。这保证了浏览器给与一些宏任务(如用户输入)以更高的优先级。好的,跟着我继续……
宏任务(task)
  • 浏览器为了能够使得JS内部task与DOM任务能够有序的执行,会在一个task执行结束后,在下一个 task 执行开始前,对页面进行重新渲染 (task->渲染->task->...)
  • 鼠标点击会触发一个事件回调,需要执行一个宏任务,然后解析HTMl。还有下面这个例子,setTimeout
  • setTimeout的作用是等待给定的时间后为它的回调产生一个新的宏任务。这就是为什么打印‘setTimeout’在‘script end’之后。因为打印‘script end’是第一个宏任务里面的事情,而‘setTimeout’是另一个独立的任务里面打印的。
微任务(Microtasks )
  • 微任务通常来说就是需要在当前 task 执行结束后立即执行的任务,比如对一系列动作做出反馈,或或者是需要异步的执行任务而又不需要分配一个新的 task,这样便可以减小一点性能的开销。只要执行栈中没有其他的js代码正在执行且每个宏任务执行完,微任务队列会立即执行。如果在微任务执行期间微任务队列加入了新的微任务,会将新的微任务加入队列尾部,之后也会被执行。微任务包括了mutation observe的回调还有接下来的例子promise的回调。

  • 一旦一个pormise有了结果,或者早已有了结果(有了结果是指这个promise到了fulfilled或rejected状态),他就会为它的回调产生一个微任务,这就保证了回调异步的执行即使这个promise早已有了结果。所以对一个已经有了结果的promise调用.then(yey, nay)会立即产生一个微任务。这就是为什么‘promise1’,'promise2'会打印在‘script end’之后,因为所有微任务执行的时候,当前执行栈的代码必须已经执行完毕。‘promise1’,'promise2'会打印在‘setTimeout’之前是因为==所有微任务总会在下一个宏任务之前全部执行完毕。==

.then的链式操作

  1. 上一个不抛出错误的话, 那下一个then会默认执行成功回调函数, 抛出错误的话会执行失败回调函数
  2. 上一个then的返回至会作为下一个then注册函数的执行参数
  3. 上一个then返回一个Promise对象的话,后面的then就会成为这个对象的注册函数
// resolve 和 resolve是互斥事件
let oP = new Promise((resolve, reject)=> {
    // 在构造函数中同步执行
    console.log('123');
    reject('aaa');
    resolve('ddd');
    
});

oP.then((data)=> { // 被当作微任务
    // 异步执行
    // setTimeout 是宏任务
    // 宏任务          微任务
    // task queue 1    task queue 2
    // 微任务有优先执行权
    console.log(data);
    return 20;
}, (reason)=>{
    console.log(reason);
    return 30;
}).then((data)=> {
    console.log('ok then2 ' + data);
}, (reason)=> {
    console.log('no then2 ' + reason);
});

// .then的链式操作
// 上一个不抛出错误的话, 那下一个then会默认执行成功回调函数, 抛出错误的话会执行失败回调函数
// 上一个then的返回至会作为下一个then注册函数的执行参数
// 上一个then返回一个Promise对象的话,后面的then就会成为这个对象的注册函数 
复制代码

用解决回调地狱

回调地狱

// name.txt: ./data/number.txt
// number.txt: ./data/score.txt
// score.txt: 100
let fs = require('fs');
fs.readFile('./data/name.txt', 'utf-8', (err, data)=> {
    console.log(data);
    if(data) {
        fs.readFile(data, 'utf-8', (err, data)=> {
            console.log(data);
            if(data) {
                fs.readFile(data, 'utf-8', (err, data)=> {
                    console.log(data);
                });
            }
        });
    }
});
复制代码

使用Promise后

// name.txt: ./data/number.txt
// number.txt: ./data/score.txt
// score.txt: 100
let fs = require('fs');
function readFile(path) {
    return new Promise((resolve, reject) => {
        fs.readFile(path, 'utf-8', (err, data) => {
            if (data) {
                resolve(data);
            }
        });
    });
}
readFile('./data/name.txt').then((data) => {
    console.log(data);
    return readFile(data);
}).then((data) => {
    console.log(data);
    return readFile(data);
}).then((data) => {
    console.log(data);
});
// node运行结果 
// ./data/number.txt
// ./data/score.txt
// 100
复制代码

.then .catch .finally
// 链式操作当遇到空的.then时,就当它不存在
let oP = new Promise((res, rej) => {
    res();
});
oP.then(() => {
    throw new Error('www');
}).then(() => {

}).catch((err)=>{
    console.log(err);
    // throw new Error('dasf');
    return 10;
}).then((val)=>{
    console.log('catch after ok:' + val); // 输出
},(reason)=>{
    console.log('catch after no:' + reason) // 解开catch报错注释的时候输出
}).finally(()=>{
    console.log('over'); 
});
复制代码

Promise.all()获取并发进程的结果 Promise.race() 赛跑
  • Promise.all可以将多个Promise对象实例包装成一个新的Promise实例

  • 同时,成功和失败的返回至使不同的,成功的时候返回的是一个结果数组失败的时候则返回最先被reject失败状态的值

// 在数组中的所有异步进程都成功执行后再回执行.then后的resolve 有一个进程不成功都只会执行reject
Promise.all([readFile('./data/number.txt'), readFile('./data/name.txt'), readFile('./data/score.txt')]).then((val) => {
    console.log(val);
}, (reason)=> {
    console.log('no');
})
复制代码
  • 顾名思义, Promise.race就是赛跑的意思,意思就是说,Promise.race([p1,p2,p3])里面那个结果获得的快,就返回那个结果,不管结果本身世成功状态还是失败状态
// race 赛跑  数组中的线程谁先成功 后面的then参数就是谁的返回值
Promise.race([readFile('./data/number.txt'), readFile('./data/name.txt'), readFile('./data/score.txt')]).then((val) => {
    console.log(val);
}, (reason)=> {
    console.log('no');
})
复制代码

MyPromise

执行

let oP = new MyPromise((res, rej) => {
                    res('ee');
            });
            oP.then((val)=> {
                console.log(val,'ok')
                return new MyPromise((res, rej)=>{
                    res(0);
                });
            },(reason)=>{
                console.log(reason,'no');
                return 12;
            }).then(val=> {
                console.log(val);
            });
复制代码

MyPromise文件

function MyPromise(executor) {
    var self = this;
    self.status = 'pending'; // 状态
    self.resolveValue = null;
    self.rejectReason = null;
    self.resolveCallBacksList = []; // 实现在执行函数中可以使用异步例如setTimeout
    self.rejectCallBacksList = []; // 
    function resolve(value) {
        if (self.status === 'pending') {
            self.status = 'Fulfilled';
            self.resolveValue = value;
            self.resolveCallBacksList.forEach((ele) => {
                ele();
            });
        }
    }
    function reject(reason) {
        if (self.status === 'pending') {
            self.status = 'Rejected';
            self.rejectReason = reason;
            self.rejectCallBacksList.forEach((ele) => {
                ele();
            });
        }
    }
    // 报错时的处理方法 所有执行用 try catch 包裹
    try {
        // 执行我们在外界传入的函数, 将对象里的resolve, reject函数传进去, 在new一个新的Promise时, 自己定义的名字只是用来接收 有点绕?
        executor(resolve, reject); 
    } catch (e) {
        reject(e);
    }
};


// 如果then的返回值是对象时的处理方式
function ResolutionReturnPromise(nextPromise, returnValue, res, rej) {
    if(returnValue instanceof MyPromise) {
        // Promise对象
        returnValue.then(function (val) {
            res(val);
        }, function (reason) {
            rej(reason);
        })
    }else {
        res(returnValue);
    }
}
MyPromise.prototype.then = function (onFulfilled, onRejected) {
    var self = this;
    // 判断注册函数是否为空, 如果是true就返回以下函数 将返回值作为下一个then的参数
    if (!onFulfilled) {
        onFulfilled = function (val) {
            return val;
        }
    }
    if (!onRejected) {
        onRejected = function (reason) {
            return new Error(reason);
        }
    }
    // 链式操作? new一个新的Promise时,里面的代码是同步执行的
    var nextPromise = new MyPromise(function (res, rej) {
        if (self.status === 'Fulfilled') { // 判断状态 下同
            setTimeout(() => {
                try {
                    var nextResolveValue = onFulfilled(self.resolveValue); // 执行注册函数并接收返回值
                    ResolutionReturnPromise(nextPromise, nextResolveValue, res, rej);
                } catch (e) {
                    rej(e);
                }
            }, 0);

        }
        if (self.status === 'Rejected') {
            setTimeout(() => {
                try {
                    var nextRejectReason = onRejected(self.rejectReason);
                    ResolutionReturnPromise(nextPromise, nextRejectReason, res, rej);
                } catch (e) {
                    rej(e);
                }
            }, 0);

        }
        if (self.status === 'pending') {
            if (onFulfilled) {
                self.resolveCallBacksList.push(function () {
                    setTimeout(() => {
                        try {
                            var nextResolveValue = onFulfilled(self.resolveValue);
                            ResolutionReturnPromise(nextPromise, nextResolveValue, res, rej);
                        } catch (e) {
                            rej(e);
                        }
                    }, 0);
                });
            }
            if (onRejected) {
                self.rejectCallBacksList.push(function () {
                    setTimeout(() => {
                        try {
                            var nextRejectReason = onRejected(self.rejectReason);
                            ResolutionReturnPromise(nextPromise, nextRejectReason, res, rej);
                        } catch (e) {
                            rej(e);
                        }
                    }, 0);
                });
            }
        }
    });

    return nextPromise; // 返回一个Promise对象
}

MyPromise.all = function (promiseArr) {
    return new MyPromise(function (resolve, reject) {
        if(!Array.isArray(promiseArr)) {
            return reject(new TypeError("argument must be anarray"));
        }
        var promiseArrValue = [];
        promiseArr.forEach((promise, index) => {
            promise.then((val)=>{
                promiseArrValue.push(val);
                if(index == promiseArr.length - 1) {
                    return resolve(promiseArrValue);
                }
            }, (reason)=>{
                reject(reason);
            })
        });
    });
}


复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值