ES6 异步编程(一)——Promise
来源:互联网 发布:warframe端口脱机 编辑:程序博客网 时间:2024/06/07 09:34
嘿,大家好呀,今天这篇文章和大家聊聊 ES6 的异步编程实现。当然,ES6 中提出了多种解决方案,我们今天先聊聊 Promise 这个异步编程解决方案。
异步编程
JavaScript 中为什么会有异步编程这个概念呢?老生常谈的答案,因为 JavaScript 是单线程的。那么是什么造就了 JavaScript 的单线程呢?在浏览器中,单线程的 JavaScript 的运行机制又是如何的呢?好的,我们先来扯扯这两个问题。
JavaScript 单线程
单线程是历史的原因。很简单,这和 JavaScript 的诞生使命有关。JavaScript 作为浏览器的脚本语言,最初的使命(当然现在大部分也还是 )是用来操作 DOM,产出各种酷炫的用户交互体验。对于浏览器来说,DOM 是浏览器这个环境的一份公共资源。如果 JavaScript 是多线程的,简单点来说,就是两个线程的话,在这种环境中都要考虑很多同步编程的复杂性问题。例如:一个线程要删除一个 DOM 节点,但是另一个线程同时要修改这个 DOM 节点,DOM 是一个共有资源,如何做资源的 lock,然和去调度线程任务,等等这些复杂的问题,对于浏览器这个场景来说,太庞大了。当然,这只是一个部分原因。所以,设计之初,JavaScript 就是以单线程的身份降临世间的。那么,遇到耗时的阻塞任务怎么办,单线程被堵死,那用户岂不是只能盯着浏览器页面,啥子都做不了啥?这时候就需要异步啦,让异步来平息阻塞任务这些暴民。浏览器环境是如何去做的呢?我们接着看。。。
JavaScript 浏览器运行机制
Event Loop
好,让我们来瞻仰一下大神 Philip Roberts 的分析图:
这张图分析的很清晰,stack 是 execution contexts,执行上下文,我们可以理解为 JavaScript 执行的主线程。可能用一个等式来说明更加简洁明了:
1.one thread == one call stack == one thing at a time
在主线程执行的过程中,会执行各种各样的 function
,产生各种异步操作,如 DOM 操作、ajax
请求,setTimeout()
等等。这些操作,会在 task queue(callback queue)中产生对应的 event,当 stack 空闲时,event loop 的机制就会去 task queue 中取排在最前面的 event 进入 stack 执行,以此往复。。。
就是这个原因,所以,下面代码两种写法都是可以的:
1.// 写法一:2.var req = new XMLHttpRequest();3. req.open('GET', url); 4. req.onload = function (){}; 5. req.onerror = function (){}; 6. req.send();
1.// 写法二:2.var req = new XMLHttpRequest();3. req.open('GET', url);4. req.send();5. req.onload = function (){}; 6. req.onerror = function (){};
异步编程解决方案
这也有一个发展的过程,最早的回调函数,到 Promise 对象,再到 Generator 和现在 ES6 的 async 函数,每一次解决方案都在解决不同的编程痛点吧。这里面,Promise 就是我们今天要谈论的对象。Promise 这个异步解决方案很有特点,它非常的“基础”、灵活,灵活到现在很多工具库的异步操作都是它来分装的(经常看库的同学有没有深有体会啊,如果理解的不是太好,很容易迷失在各种 Promise
、 .then
、 return
中)。
通俗的来说,一个 Promise 对象,代表着一个目前不可用,但是将来某个时间点可以被解析的值,当然,这个上面绑定这好几个处理函数喽(如:.then
.all
)。
Promise 机制
Promise 对象拥有三种状态,如下:
- unfulfilled/pending
- fulfilled/resolved
- failed/rejected
状态的转换只能由 pending 到 resolved,或者是 pending 到 rejected。关键点在于,状态一旦转变,是不可更改的。详细的实现机制,有太多的文章在解释了,大多数都是从 Promise 这个状态机的特性出发,进行实现的,我就不在这里啰嗦了。api 的基本使用,MDN 上面很清楚,我们主要来了解一下遇到 Promise 的注意点,和我们用 Promise 能做哪些有趣的事情呢?
注意点
new Promise()
会立即执行,而且是无法取消的;- Promise 不会出现回调函数那种错过监听就得不到结果的情况,只要你有 Promise 这个对象了,随时可以
.then()
它; new Promise()
返回一个 Promise 对象,.then()
方法返回的也是 Promise 实例,但是是一个新的 Promise 实例,不是原来那个了哦,因此,可以有下面这种链式写法:
1.fetch('url').then(res => res.json()).then(...)
实用举例
1. 异步图片加载
1.function loadImageAsync(url) {2. return new Promise(function(resolve, reject) {3. var image = new Image();4.5. image.onload = function() {6. resolve(image);7. };8.9. image.onerror = function() {10. reject(new Error('Could not load image at ' + url));11. };12.13. image.src = url;14. })15.}16.var imgSrc = 'http://images2015.cnblogs.com/blog/1059788/201702/1059788-20170214095743082-1492420313.gif';17.var img1 = loadImageAsync(imgSrc);18.// img1 可以添加到 DOM 中去了
2. 延时函数
1.function delay(time) { 2. return new Promise( function(resolve,reject){ 3. setTimeout( resolve, time ); 4. }); 5.} 6.7.delay(1000).then(function STEP2(){ 8. console.log("step 2 (after 1000ms)");9. console.log(new Date().getSeconds()); 10. return delay(5000); 11.}).then(function STEP3(){ 12. console.log( "step 3 (after another 5000ms)" );13. console.log(new Date().getSeconds()); 14.});
3. api 请求的统一封装
1.// fetch 返回就是 Promise 对象2.fetch(url, config).then((response) => {3. if (response.status === 403) {4. Toast.notice('您发送的内容被服务器阻止!!!', 1000);5. return new Response('{"status_code": 403}');6. }7. return response;8. }).then((response) => response.json(), (error) => (console && console.log('An error occured.', error) // eslint-disable-line no-console9. )).then((json) => {10. if (json.status_code === 4206) {11. // Login.exit();12. Toast.notice('登录已过期,请重新登录', 1000);13. // 第三方登录站 token 过期,后台执行了清 cookie 的操作,前端登出刷新页面14. Login.exit();15. } else if (json.status_code === 1001) {16. Toast.notice(json.message, 1000);17. } else if (json.status_code === 2001) {18. Toast.notice("请求超时,请稍后重试!", 1000);19. } else if (json.status_code === 200) {20. return json;21. }22. return json;23. });
ok,最后再啰嗦一个点,关于 Promise.all()。
Promise.all
里的任务列表[asyncTask(1),asyncTask(2),asyncTask(3)]
,是按顺序发起的,由于它们都是异步的,互相之间并不阻塞,每个任务完成时机是不确定的。尽管如此,所有任务结束之后,它们的结果仍然是按顺序地映射到resultList
里,这样就能和Promise.all里的任务列表[asyncTask(1),asyncTask(2),asyncTask(3)]
一一对应起来了。
- ES6 异步编程(一)——Promise
- ES6 —(Promise)
- Javascript的异步编程(上)及es6的promise
- 异步编程——promise
- 异步编程——Promise
- 【ES6学习】— (2)异步编程Generator函数和Promise对象简介
- ES6 -- 异步编程神器:Promise对象
- 11、异步操作之Promise—ES6学习笔记
- ES6——Promise
- 深入理解 JavaScript 异步系列(3)—— ES6 中的 Promise
- 深入理解 JavaScript 异步系列(3)—— ES6 中的 Promise
- 深入理解 JavaScript 异步系列(3)—— ES6 中的 Promise
- 深入理解 JavaScript 异步系列(3)—— ES6 中的 Promise
- ES6--异步操作之promise(续)
- 浅谈JS异步编程——Promise
- 《深入理解ES6》阅读笔记 --- Promise与异步编程
- 异步编程一 初探 Javascript Promise
- js异步之promise(ES6)
- Scanner接收中文乱码
- webapp开发框架
- 运行jar包时指定端口
- 输入校验
- javaScript基本包装类型及变量定义的区别
- ES6 异步编程(一)——Promise
- python 中join 和 split的用法
- Unity GUI 系统应知
- sql server 查看表的死锁和Kill 死锁进程
- ajax调取php,mysql后台数据实例
- Cron在线生成器
- 在代码中设置RadioButton中图片大小
- fullcalendar日历控件知识点集合
- 解决热块冲突的主要思路