Promise

Promise晋级,需要的全部都在这

主要内容:

  1. promise基本实现原理
  2. promise 使用中难点(链式调用,API基本上返回都是一个新Promise,及参数传递)
  3. promise 对异常处理
  4. promise 简单实现及规范
  5. es6 对promise更进一步掌握Async晋级——完全吃透

参考:

​ 30分钟,让你彻底明白Promise原理

​ 阮一峰ES6入门

​ JavaScript Promise:简介

0. 基本用法

基本的promise使用,读本文需要了解基本的Promise使用。

1. 兼容性

查看caniuse

查兼容性 基本上 主流浏览器支持没有问题。

IE不兼容 问题,本文不予以处理,出门左转,找谷哥。具体查看 babel,或者 自己实现一个Promise

2. ajax XMLHttpRequest封装

//get 请求封装
function get(url) {// Return a new promise.return new Promise(function(resolve, reject) {// Do the usual XHR stuffvar req = new XMLHttpRequest();req.open('GET', url);req.onload = function() {// This is called even on 404 etc// so check the statusif (req.status == 200) {// Resolve the promise with the response textresolve(req.response);}else {// Otherwise reject with the status text// which will hopefully be a meaningful errorreject(Error(req.statusText));}};// Handle network errorsreq.onerror = function() {reject(Error("Network Error"));};// Make the requestreq.send();});
}
复制代码

1. Promse API

Promise API 分为 :MDN

  1. 静态方法

  2. prototype上方法

    Promise.prototype.then() 来分析

     首先来看看 `Promise.prototype.then()`返回一个`Promise`,但`Promise`内部有返回值,且 返回值,可以是个值,也可能就是一个新`Promise`*具体规则如下:*
    复制代码
    • 如果then中的回调函数返回一个值,那么then返回的Promise将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。
    • 如果then中的回调函数抛出一个错误,那么then返回的Promise将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。
    • 如果then中的回调函数返回一个已经是接受状态的Promise,那么then返回的Promise也会成为接受状态,并且将那个Promise的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。
    • 如果then中的回调函数返回一个已经是拒绝状态的Promise,那么then返回的Promise也会成为拒绝状态,并且将那个Promise的拒绝状态的回调函数的参数值作为该被返回的Promise的拒绝状态回调函数的参数值。
    • 如果then中的回调函数返回一个未定状态(pending)的Promise,那么then返回Promise的状态也是未定的,并且它的终态与那个Promise的终态相同;同时,它变为终态时调用的回调函数参数与那个Promise变为终态时的回调函数的参数是相同的。

    上面是官方规则,神马,具体白话就是 核心是 返回参数及返回promise的状态

    参考:MDN

    是不是 觉得很晕,没关系,可以先看 下一节,看完后,再回过来看具体的说明

    /*then 回调中,1. 返回是return function,则返回一个Promise 【参见对比3代码】2. 不是一个function,则 then 将创建一个没有经过回调函数处理的新 Promise 对象,这个新 Promise 只是简单地接受调用这个 then 的原 Promise 的终态作为它的终态。(MDN中解释)【参见对比1代码】3. 返回一个function,但没有return ,则相当于 then(null)*/
    //对比1 穿透问题  返回是'foo' 而不是 'bar'
    Promise.resolve('foo').then(Promise.resolve('bar')).then(function(result){console.log(result)
    })//对比2  打印undefined
    Promise.resolve('foo').then(function(){Promise.resolve('bar')}).then(function(result){console.log(result)
    })//对比3  返回 'bar'
    Promise.resolve('foo').then(function() {return Promise.resolve('bar')
    }).then(function(result) {console.log(result)
    })
    复制代码

2. Prmise 链式调用

链式调用

  1. 核心就是 then catch 等方法返回一个Promise
  2. 链式 调用数据传递(注意)

1. 值传递问题

简单例子

//正常状态
const promise1 = new Promise((resolve, reject) => {resolve('0000')//
})
promise1.then(result => {console.log(result) //0000return '1111';//类似于 return Promise.resolve('1111'); 参数是data,promise 状态时 resolve
}).then(data => {console.log(data) // 1111
})
复制代码

一个实际的例子:(拿来大神的例子JavaScript Promise:简介)

get('story.json').then(function(response) {console.log("Success!", response);
})
复制代码
//这里的 response 是 JSON,但是我们当前收到的是其纯文本。也可以设置XMLHttpRequest.responseType =json
get('story.json').then(function(response) {return JSON.parse(response);
}).then(function(response) {console.log("Yey JSON!", response);
})
复制代码
//由于 JSON.parse() 采用单一参数并返回改变的值,因此我们可以将其简化为:
get('story.json').then(JSON.parse).then(function(response) {console.log("Yey JSON!", response);
})
复制代码
function getJSON(url) {return get(url).then(JSON.parse);
}
//getJSON() 仍返回一个 promise,该 promise 获取 URL 后将 response 解析为 JSON。
复制代码

2. 异步操作队列

上面至今是return 值,直接调用 下一下then就OK了。

但如果return Promise,则?

Promise.resolve(111).then(function(d){console.log(d);return Promise.resolve(d+111);//返回promise
}).then(function(d2){console.log(d2);
})
// 111,222
复制代码

3. 并行问题forEach处理

当多个异步并行执行时,每个异步代码执行时间不定,所以多个异步执行结束时间无法确定(无法确定结束完时间)。

所以需要特殊处理。

//forEach 顺便无法保证
var arrs = [1,2,3,4];
var p = function(d){return new Promise((resolve)=>{setTimeout(()=>{resolve(d);},Math.random()*1000);//因为异步执行时间无法确认});
};
arrs.forEach(function(arr){p(arr).then((d)=>{console.log(d);})
});
复制代码
//使用 Promise.all 来让返回有序
var arrs = [1,2,3,4];
var p = function(d){return new Promise((resolve)=>{setTimeout(()=>{resolve(d);},Math.random()*1000);//因为异步执行时间无法确认});
};
var ps = [];
arrs.forEach(function(arr){ps.push(p(arr));
});
Promise.all(ps).then(values=>{console.log(values);//[1,2,3,4]
})
复制代码

4. 基本实现原理—实现一个简单Promise

自己手撸一个简单的Promise

1. 版本1—极简实现

//版本1 极简实现
function Promise1(fn) {var value = null,callbacks = [];  //callbacks为数组,因为可能同时有很多个回调this.then = function (onFulfilled) {callbacks.push(onFulfilled);return this;//支持链式调用 Promise.then().then};function resolve(value) {callbacks.forEach(function (callback) {callback(value);});}fn(resolve);
}
//Test 对上面实现,写一个简单的测试
new Promise1(function(resolve){setTimeout(function(){resolve(1);},100);
}).then(function(d){console.log(d);
})
//1
复制代码

2. 版本2—加入延时机制

//上面版本1 可能导致问题
//在then注册回调之前,resolve就已经执行了
new Promise1(function(resolve){console.log(0)resolve(1);
}).then(function(d){console.log(d);
})
// 1 不会打印
复制代码
//版本2 解决
function Promise1(fn) {var value = null,callbacks = [];  //callbacks为数组,因为可能同时有很多个回调this.then = function (onFulfilled) {callbacks.push(onFulfilled);return this;//支持链式调用 Promise.then().then};function resolve(value) {setTimeout(function(){callbacks.forEach(function (callback) {callback(value);}),0});}fn(resolve);
}
复制代码

3. 版本3—状态

Promise有三种状态pendingfulfilledrejected ,且状态变化时单向的。

具体细节就是 在then,resolve中加状态判断,具体代码略

4. Promises/A+

具体 Promise实现有一套官方规范,具体参见Promises/A+

5. finnaly 实现


Promise.prototype.finally = function (callback) {let P = this.constructor;return this.then(value  => P.resolve(callback()).then(() => value),reason => P.resolve(callback()).then(() => { throw reason }));
};
复制代码

6. 异常处理

异常分类:

  1. 同步异常
  2. 异步异常 无法try-catch 得到
  3. 多层Promise嵌套,获异常取具体的一个promise异常,而不是全部

1. Promise 异常处理基本套路

基本处理异常中,有两种方案then(undefined, func)catch()

then(undefined, func)catch()不同,具体参见代码方案3

//方案1 使用 Promise.prototype.catch()来catch
const promise1 = new Promise((resolve, reject) => {reject('no')//
})
promise1.then(result => {console.log(result) // 永远不会执行
}).catch(error => {console.log(error) // no
})
复制代码
//方案2 使用 Promise.prototype.then()中第二个参数 来处理
const promise1 = new Promise((resolve, reject) => {reject('no')//
})
promise1.then(result => {console.log(result) // 永远不会执行
},error => {console.log(error) // no
})
复制代码
//方案2  (方案1  方案2 对比)
var promise2 = new Promise((resolve, reject) => {resolve('yes')//
})
promise2.then(result => {throw new Error('then');console.log(result)
},error => {console.log('1111',error) // no
}).catch(error=>{console.log('2222',error)// 最终 err在此处被捕获,而不是 then 中
})
复制代码

2. 异常不同分类

Promise可能遇到的异常种类

//1.异常 reject()
const promise1 = new Promise((resolve, reject) => {reject('no')//
})
promise1.then(result => {console.log(result) // 永远不会执行
}).catch(error => {console.log(error) // no
})复制代码
//2.异常 显示throw
const promise1 = new Promise((resolve, reject) => {throw Error('no')
})
promise1.then(result => {console.log(result) // 永远不会执行
}).catch(error => {console.log(error) //
})
复制代码
//3.执行异常
const promise1 = new Promise((resolve, reject) => {aaaa;
})
promise1.then(result => {console.log(result) // 永远不会执行
}).catch(error => {console.log(error) //
})
复制代码

3. 异常链式调用

asyncThing1().then(function() {return asyncThing2();
}).then(function() {return asyncThing3();
}).catch(function(err) {return asyncRecovery1();
}).then(function() {return asyncThing4();
}, function(err) {return asyncRecovery2();
}).catch(function(err) {console.log("Don't worry about it");
}).then(function() {console.log("All done!");
})
复制代码

上述代码的流程图形式:

// promise链式调用,catch住异常后,后面就不会处理异常了
Promise.reject().then(()=>{console.log(2222);
},(err)=>{console.log(333,err)return err})
.catch((err)=>{console.log(1111,err);
})
//333 undefined  ,没有打印 1111
复制代码
//如果 在链式调用中,then 第二个参数 catch住了异常,没有return Promise.reject()则后续链式调用返回rosolve状态pormise
Promise.reject().then(()=>{console.log(111);},(err)=>{console.log(111,err) //reject return err;}).then((data)=>{console.log(222,data);//resolve 执行},(err)=>{console.log(222,err); //未执行})
//4444 没有执行 1111
复制代码

4. 异常丢失

很多情况下,promise无法捕获异常

场景1 macrotask 队列中抛出异常:

//场景1
//永远不要在 macrotask 队列中抛出异常,因为 macrotask 队列脱离了运行上下文环境,异常无法被当前作用域捕获。
function fetch(callback) {return new Promise((resolve, reject) => {setTimeout(() => {throw Error('用户不存在')})})
}fetch().then(result => {console.log('请求处理', result) // 永远不会执行
}).catch(error => {console.log('请求处理异常', error) // 永远不会执行
})// 程序崩溃
// Uncaught Error: 用户不存在/*参考作者:黄子毅链接:https://www.jianshu.com/p/78dfb38ac3d7來源:简书简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
*/
复制代码
//解决场景1 怎么解决,因为setTimeout 是macrotask任务,执行上下文完全不同
/**如何解决?调用reject
*/
function fetch() {return new Promise((resolve, reject) => {setTimeout(() => {reject('收敛一些')})})
}
fetch().then((resolve, reject) => {console.log('resolve');
}).catch(error => {console.log('捕获异常', error) // 捕获异常 收敛一些
})
复制代码

场景二 Promise 状态只能改变一次

 //异常丢失const promise2 = new Promise((resolve, reject) => {reject('no')console.log('reject after')throw Error('no') //异常丢失})promise1.then(result => {console.log(result) // 永远不会执行}).catch(error => {console.log('err',error) // no}).catch(error => {console.log('err2',error) // 也无法捕获异常})
复制代码

7.async

async是 Promise 更高一层的封装,具体参见深入浅出Async

Promise晋级—完全吃透相关推荐

  1. 常见 Promise 面试问题

    前端面试过程中,基本都会问到 Promise,如果你足够幸运,面试官问的比较浅,仅仅问 Promise 的使用方式,那么恭喜你.事实上,大多数人并没有那么幸运.所以,我们要准备好九浅一深的知识. 常见 ...

  2. 从底层吃透java内存模型(JMM)、volatile、CAS

    前言 随着计算机的飞速发展,cpu从单核到四核,八核.在2020年中国网民数预计将达到11亿人.这些数据都意味着,作为一名java程序员,必须要掌握多线程开发,谈及多线程,绕不开的是对JMM(Java ...

  3. 2021年大数据Hive(三):手把手教你如何吃透Hive数据库和表操作(学会秒变数仓大佬)

    全网最详细的Hive文章系列,强烈建议收藏加关注! 后面更新文章都会列出历史文章目录,帮助大家回顾知识重点. 目录 系列历史文章 前言 Hive数据库和表操作 一.数据库操作 1.创建数据库 2.创建 ...

  4. setTimeout、setInterval、promise、async/await的顺序详解(多种情况,非常详细~)

    本文很长,列举的情况很多. 在阅读本文之前,如果您有充足的时间,请新建一个项目与本文一同实践. 每段代码都有对应的解释,但是自己动手尝试印象才会更深哦~ setInterval:表示多久执行一次,需要 ...

  5. C++多线程:异步操作std::async和std::promise

    文章目录 std::async 简介 使用案例 std::promise 简介 成员函数 总结 之前的文章中提到了C++多线程中的异步操作机制 C++ 多线程:future 异步访问类(线程之间安全便 ...

  6. ES6中的Promise详解

    Promise 在 JavaScript 中很早就有各种的开源实现,ES6 将其纳入了官方标准,提供了原生 api 支持,使用更加便捷. 定义 Promise 是一个对象,它用来标识 JavaScri ...

  7. 关于ES6中Promise的应用-顺序合并Promise,并将返回结果以数组的形式输出

    1.Promise 基础知识梳理 创建一个Promise实例 const promise = new Promise(function(resolve, reject) {if (success){r ...

  8. promise实现多个请求并行串行执行

    早上查资料,偶然发现这个话题,发现自己并不会,于是乎,下来研究了一下. 想想之前我们用jquery写请求的时候,要实现请求的串行执行,我们可能是这么做的. $.ajax({url: '',data: ...

  9. 异步编程之Promise(2):探究原理

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

最新文章

  1. Java:带符号右移和无符号右移
  2. 有关递归的三道算法题总结
  3. u-boot分析之命令实现(四)
  4. 华为防火墙ftp_FTP被动模式访问问题
  5. 计算机win7分盘,win7电脑如何分盘
  6. CF1004F Sonya and Bitwise OR
  7. linux 函数 文件校验,Linux中的文件效验命令
  8. mysql 某列加全文索引_MySQL使用全文索引(fulltext index)---高性能
  9. 测试基础【第二篇】软件测试模型
  10. 如何将自己的代码自动添加版权信息[转]
  11. 我用Python爬取1000封情书助力室友表白班花,却反转再反转...原来这就是班花的终极秘密!
  12. 报错:error while loading shared libraries: libz.so.1: cannot open shared object file
  13. python坐标系教程_python Shapely使用指南详解
  14. 精灵3P+Pix4D简单航测详细应用教程
  15. 单片机控制NOKIA5110液晶屏之模块化编程
  16. Udacity 传感器融合笔记 (一)lidar
  17. Widget Extention开发笔记
  18. 国内云服务器哪家好?
  19. 笔记-中项/高项学习期间的错题笔记1
  20. git pull 与 git push 的区别

热门文章

  1. 打开计算机桌面上没有跑哪里去了,Win8.1我的电脑图标跑哪去了怎么放桌面
  2. 【论文笔记】(VLDB 2020) A Benchmarking Study of Embedding-based Entity Alignment for Knowledge
  3. 闲人闲谈PS之三十——新收入准则中的合同资产和合同负债
  4. 自学html4,HTML4
  5. Linux 版本查询常用命令
  6. 3.23期货品种每日早盘建议
  7. 如何用photoshop CS制作标准一寸照
  8. 猫猫回收站教程:在线撸猫~
  9. np.arange函数的使用
  10. 图像轮廓提取算法(Opencv基于C++实现)