博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ES6-promise
阅读量:6892 次
发布时间:2019-06-27

本文共 9806 字,大约阅读时间需要 32 分钟。

异步编程简述:

  • 无论是在浏览器环境中还是在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: 100let 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: 100let 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 有一个进程不成功都只会执行rejectPromise.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);            })        });    });}复制代码

转载地址:http://ctebl.baihongyu.com/

你可能感兴趣的文章
云场景实践研究第6期:游族网络
查看>>
记录由Equal基础知识引起的内存泄露
查看>>
Android:Sensor传感器
查看>>
Eclipse配置实现定制登录界面
查看>>
NO.1 进入IT世界
查看>>
Exceeded maximum number of retries. Exceeded max scheduling attempts 3 for instance
查看>>
Asp.net mvc 3.0新特性-浅析1
查看>>
Hadoop FSDataInputStream 流定位的例子
查看>>
在OWA页面中,增加忘记密码项
查看>>
Samba文件共享服务(共享脚本 让你工作更轻松)
查看>>
AJAX 学习笔记[三] get 与post 模式的区别
查看>>
MES技术
查看>>
GO语言练习:网络编程 ICMP 示例
查看>>
ios11--UIButton
查看>>
Jq-公告渐隐弹出
查看>>
Windows Forms中通过自定义组件实现统一的数据验证(二)
查看>>
阿里云海外征战记:跻身全球前三,只用了两年半
查看>>
解密回声消除技术之二(应用篇)
查看>>
Go语言的web程序写法
查看>>
IDF2011:基于SaaS模式的"教学云"案例
查看>>