关于promise的详细讲解
到处是回调函数,代码非常臃肿难看, Promise 主要用来解决这种编程方式, 将某些代码封装于内部。
Promise 直译为“承诺”,但一般直接称为 Promise;
代码的可读性非常重要,因为开发人员支出一般比计算机硬件的支出要大很多倍。
虽然同步代码更容易跟踪和调试, 但异步方式却具有更好的性能与灵活性. 怎样在同一时刻发起多个请求, 然后分别处理响应结果? Promise 现已成为 JavaScript 中非常重要的一个组成部分, 很多新的API都以 promise 的方式来实现。下面简要介绍 promise, 以及相应的 API 和使用示例!
Promises 周边
XMLHttpRequest 是异步API, 但不算 Promise 方式。当前使用 Promise 的原生 api 包括:
•Battery API
•fetch API (用来替代 XHR)
•ServiceWorker API
Promise 会越来越流行,所以前端开发需要快速掌握它们。当然, Node.js 是另一个使用 Promise 的平台(显然, Promise 在Node中是一个核心特性)。
测试 promises 可能比你想象的还要容易, 因为 setTimeout 可以用来当作异步“任务”!
Promise 基本用法
直接使用 new Promise() 构造函数的方式, 应该只用来处理遗留的异步任务编程, 例如 setTimeout或者 XMLHttpRequest。 通过 new 关键字创建一个新的 Promise 对象, 该对象有 resolve(搞定!) 和 reject(拒绝!) 两个回调函数:
var p = new Promise(function(resolve, reject) {
// ... ...
// 此处,可以执行某些异步任务,然后...
// 在回调中,或者任何地方执行 resolve/reject
if(/* good condition */) {
resolve('传入成果结果信息,如 data');
}
else {
reject('失败:原因...!');
}
});
p.then(function(data) {
/* do something with the result */
}).catch(function(err) {
/* error :( */
});
一般是由开发人员根据异步任务执行的结果,来手动调用 resolve 或者 reject. 一个典型的例子是将 XMLHttpRequest 转换为基于Promise的任务:
// 本段示例代码来源于 Jake Archibald's Promises and Back:
// http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest
function get(url) {
// 返回一个 promise 对象.
return new Promise(function(resolve, reject) {
// 执行常规的 XHR 请求
var req = new XMLHttpRequest();
req.open('GET', url);
req.onload = function() {
// This is called even on 404 etc
// so check the status
if (req.status == 200) {
// Resolve the promise with the response text
resolve(req.response);
}
else {
// Otherwise reject with the status text
// which will hopefully be a meaningful error
reject(Error(req.statusText));
}
};
// Handle network errors
req.onerror = function() {
reject(Error("网络出错"));
};
// Make the request
req.send();
});
};
// 使用!
get('story.json').then(function(response) {
console.log("Success!", response);
}, function(error) {
console.error("Failed!", error);
});
有时候在 promise 方法体中不需要执行异步任务 —— 当然,在有可能会执行异步任务的情况下, 返回 promise 将是最好的方式, 这样只需要给定结果处理函数就行。在这种情况下, 不需要使用 new 关键字, 直接返回 Promise.resolve() 或者 Promise.reject()即可。例如:
var userCache = {};
function getUserDetail(username) {
// In both cases, cached or not, a promise will be returned
if (userCache[username]) {
// Return a promise without the "new" keyword
return Promise.resolve(userCache[username]);
}
// Use the fetch API to get the information
// fetch returns a promise
return fetch('users/' + username + '.json')
.then(function(result) {
userCache[username] = result;
return result;
})
.catch(function() {
throw new Error('Could not find user: ' + username);
});
};
因为总是会返回 promise, 所以只需要通过 then 和 catch 方法处理结果即可!
then
每个 promise 实例都有 then 方法, 用来处理执行结果。 第一个 then 方法回调的参数, 就是 resolve() 传入的那个值:
new Promise(function(resolve, reject) {
// 通过 setTimeout 模拟异步任务
setTimeout(function() { resolve(10); }, 3000);
})
.then(function(result) {
console.log(result);
});
// console 输出的结果:
// 10
then 回调由 promise 的 resolved 触发。你也可以使用链式的 then` 回调方法:
new Promise(function(resolve, reject) {
// 通过 setTimeout 模拟异步任务
setTimeout(function() { resolve(10); }, 3000);
})
.then(function(num) { console.log('first then: ', num); return num * 2; })
.then(function(num) { console.log('second then: ', num); return num * 2; })
.then(function(num) { console.log('last then: ', num);});
// console 输出的结果:
// first then: 10
// second then: 20
// last then: 40
每个 then 收到的结果都是之前那个 then 返回的值。
如果 promise 已经 resolved, 但之后才调用 then 方法, 则立即触发回调。如果promise被拒绝之后才调用 then, 则回调函数不会被触发。
catch
当 promise 被拒绝时, catch 回调就会被执行:
new Promise(function(resolve, reject) {
// 通过 setTimeout 模拟异步任务
setTimeout(function() { reject('Done!'); }, 3000);
})
.then(function(e) { console.log('done', e); })
.catch(function(e) { console.log('catch: ', e); });
// console 输出的结果:
// 'catch: Done!'
传入 reject 方法的参数由你自己决定。一般来说是传入一个 Error 对象:
reject(Error('Data could not be found'));
Promise.all
想想JavaScript加载器的情形: 有时候会触发多个异步交互, 但只在所有请求完成之后才会做出响应。—— 这种情况可以使用 Promise.all 来处理。Promise.all 方法接受一个 promise 数组, 在所有 promises 都搞定之后, 触发一个回调:
Promise.all([promise1, promise2]).then(function(results) {
// Both promises resolved
})
.catch(function(error) {
// One or more promises was rejected
});
Promise.all 的最佳示例是通过fetch同时发起多个 AJAX请求时:
var request1 = fetch('/users.json');
var request2 = fetch('/articles.json');
Promise.all([request1, request2]).then(function(results) {
// Both promises done!
});
你也可以组合使用 fetch 和 Battery 之类的 API ,因为他们都返回 promises:
Promise.all([fetch('/users.json'), navigator.getBattery()]).then(function(results) {
// Both promises done!
});
当然, 处理拒绝的情况比较复杂。如果某个 promise 被拒绝, 则 catch 将会被第一个拒绝(rejection)所触发:
var req1 = new Promise(function(resolve, reject) {
// 通过 setTimeout 模拟异步任务
setTimeout(function() { resolve('First!'); }, 4000);
});
var req2 = new Promise(function(resolve, reject) {
// 通过 setTimeout 模拟异步任务
setTimeout(function() { reject('Second!'); }, 3000);
});
Promise.all([req1, req2]).then(function(results) {
console.log('Then: ', one);
}).catch(function(err) {
console.log('Catch: ', err);
});
// From the console:
// Catch: Second!
随着越来越多的 API 支持 promise, Promise.all 将会变得超级有用。
Promise.race
Promise.race 是一个有趣的函数. 与 Promise.all 相反, 只要某个 priomise 被 resolved 或者 rejected, 就会触发 Promise.race:
var req1 = new Promise(function(resolve, reject) {
// 通过 setTimeout 模拟异步任务
setTimeout(function() { resolve('First!'); }, 8000);
});
var req2 = new Promise(function(resolve, reject) {
// 通过 setTimeout 模拟异步任务
setTimeout(function() { resolve('Second!'); }, 3000);
});
Promise.race([req1, req2]).then(function(one) {
console.log('Then: ', one);
}).catch(function(one, two) {
console.log('Catch: ', one);
});
// From the console:
// Then: Second!
一个案例是请求的资源有 主站资源和备用资源(以防某个不可用)。
改变习惯, 使用 Promise
在过去几年中 Promise 一直是个热门话题(如果你是 Dojo Toolkit 用户,那么就是已经有10年了), 已经从一个JavaScript框架变成了语言的一个主要成分. 很快你就会看到大多数新的 JavaScript api 都会基于 Promise 的方式来实现……
... 当然这是一件好事! 开发人员能够避开回调的地狱, 异步交互也可以像其他变量一样传递. Promise 还需要一段时间来普及, 现在是时候去学习他们了!
关于promise的详细讲解的更多相关文章
- Promise入门到精通(初级篇)-附代码详细讲解
Promise入门到精通(初级篇)-附代码详细讲解 Promise,中文翻译为承诺,约定,契约,从字面意思来看,这应该是类似某种协议,规定了什么事件发生的条件和触发方法. Pr ...
- vue-cli 目录结构详细讲解
https://juejin.im/post/5c3599386fb9a049db7351a8 vue-cli 目录结构详细讲解 目录 结构预览 ├─build // 保存一些webpack的初始化配 ...
- head标签详细讲解
head标签详细讲解 head位于html网页的头部,后前的标签,并以开始以结束的一html标签. Head标签位置如图: head标签示意图 head包含标签 meta,title,link,bas ...
- 详细讲解nodejs中使用socket的私聊的方式
详细讲解nodejs中使用socket的私聊的方式 在上一次我使用nodejs+express+socketio+mysql搭建聊天室,这基本上就是从socket.io的官网上的一份教程式复制学习,然 ...
- iOS KVC详细讲解
iOS KVC详细讲解 什么是KVC? KVC即NSKeyValueCoding,就是键-值编码的意思.一个非正式的 Protocol,是一种间接访问对象的属性使用字符串来标识属性,而不是通过调用存取 ...
- Android webservice的用法详细讲解
Android webservice的用法详细讲解 看到有很多朋友对WebService还不是很了解,在此就详细的讲讲WebService,争取说得明白吧.此文章采用的项目是我毕业设计的webserv ...
- 详细讲解Android对自己的应用代码进行混淆加密防止反编译
1.查看项目中有没有proguard.cfg. 2.如果没有那就看看这个文件中写的什么吧,看完后将他复制到你的项目中. -optimizationpasses 5 -dontusemixedcasec ...
- 详细讲解Hadoop源码阅读工程(以hadoop-2.6.0-src.tar.gz和hadoop-2.6.0-cdh5.4.5-src.tar.gz为代表)
首先,说的是,本人到现在为止,已经玩过. 对于,这样的软件,博友,可以去看我博客的相关博文.在此,不一一赘述! Eclipse *版本 Eclipse *下载 Jd ...
- [iOS]数据库第三方框架FMDB详细讲解
[iOS]数据库第三方框架FMDB详细讲解 初识FMDB iOS中原生的SQLite API在进行数据存储的时候,需要使用C语言中的函数,操作比较麻烦.于是,就出现了一系列将SQLite API进行封 ...
随机推荐
- 轻松学会ES6新特性之生成器
生成器虽然是ES6最具魔性的新特性,但也是最难懂得的一节,笔者写了大量的实例来具体化这种抽象的概念,能够让人一看就懂,目的是希望别人不要重复或者减少笔者学习生成器的痛苦经历. 在说具体的ES6生成器之 ...
- instanceof 原理
运行流程 function instance_of(L, R) { //L 表示左表达式,R 表示右表达式 var O = R.prot ...
- 完善tab页面定位
当我们用锚点定位到页面某个元素时,接下来按tab的话是想进入到目前元素( id="content")的下一个连接 <a href="#content"&g ...
- 爱pia戏推出PC客户端,为您自动置顶窗口,方便查找
爱pia戏推出PC客户端, 可以在无法使用插件的时候,使用PC客户端, 将为您自动置顶窗口,方便查看剧本. 百度网盘下载地址: 链接: http://pan.baidu.com/s/1pLpvn5p ...
- ueditor 和 umeditor 粘贴过滤问题
最近遇到需要将WORD WPS等复制的带有格式的内容粘贴到富文本编辑器里面去掉冗余的HTML,只保留最有用的部分. 第一步肯定是先查官方文档了. http://fex.baidu.com/uedito ...
- C#自定义运行时窗体设计器Runtime FormDesigner
写在前面:因为业务的需要,有时会使用到自定义运行时窗体设计器Runtime FormDesigner,实现的功能,就是IDE设计器的简化.设想一下,如果可以在程序运行时,再设计一个Form,然后编译代 ...
- 重构手法之Split Temporary Variable(分解临时变量)
返回总目录 本小节目录 Split Temporary Variable(分解临时变量) Remove Assignments to Parameters(移除对参数的赋值) 6.6Split Tem ...
- 如何基于 eolinker 的进行接口管理
由于工作的原因,经常要接触到很多API接口,而API接口在设计时往往需要编写大量的文档,而且编写完成后往往需要根据实际情况,经常改动文档,这使得文档编写维护工作量相对较大,这让我也包括很多的开发者都很 ...
- 选择排序-Python与PHP实现版
选择排序Python实现 import random # 生成待排序数组 a=[random.randint(1,999) for x in range(0,36)] # 选择排序 def selec ...
- 安装VisualSVN Server时候,端口号冲突
今天在本机安装VisualSVN Server 时,发现https默认端口号:443被占用了, 于是到cmd下面执行 netstat -ano命令发现是pid:4276的进程在试用, 打开任务管理里一 ...