javaScript Promise 入门
Promise是JavaScript的异步编程模式,为繁重的异步回调带来了福音。
一直以来,JavaScript处理异步都是以callback的方式,假设需要进行一个异步队列,执行起来如下:
animate (ball1, 100, function () {
animate (ball2, 200, function () {
animate (ball3, 300, function () {
animate (ball3, 150, function () {
animate (ball2, 150, function () {
animate (ball1, 150, function () {
// do something
})
})
})
})
})
这就是所谓的回调金字塔,当你想突然增加一个时,你要去算括号,维护起来相当麻烦。
但如果换成以下这种形式:
promiseAnimate(ball1,100)
.then(function(){
return promiseAnimate(ball2,200)
})
.then(function(){
return promiseAnimate(ball3,300)
})
.then(function(){
return promiseAnimate(ball3,150)
})
.then(function(){
return promiseAnimate(ball2,150)
})
.then(function(){
return promiseAnimate(ball1,150)
})
看起来就清晰,舒服多了,而且随意插入,无需顾忌。
接下来,说说这货怎么用。
1.1 Promise分三类:
1.Constructor
Promise类似于 XMLHttpRequest,从构造函数 Promise 来创建一个新建新promise对象作为接口。
要想创建一个promise对象、可以使用new来调用Promise的构造器来进行实例化。
var promise = new Promise(function(resolve, reject) {
// 异步处理
// 处理结束后、调用resolve 或 reject
});
2.Instance Method
对通过new生成的promise对象为了设置其值在 resolve(成功) / reject(失败)时调用的回调函数 可以使用promise.then() 实例方法。
promise.then(onFulfilled, onRejected)
- resolve(成功)时
-
onFulfilled会被调用 - reject(失败)时
-
onRejected会被调用
onFulfilled、onRejected 两个都为可选参数。
promise.then 成功和失败时都可以使用。 另外在只想对异常进行处理时可以采用 promise.then(undefined, onRejected) 这种方式,只指定reject时的回调函数即可。 不过这种情况下 promise.catch(onRejected) 应该是个更好的选择。
promise.catch(onRejected)
3.Static Method
像 Promise 这样的全局对象还拥有一些静态方法。
包括 Promise.all() 还有 Promise.resolve() 等在内,主要都是一些对Promise进行操作的辅助方法。
看到上面,如果不懂可以忽略,后面在看回来看。
接下来,举一个栗子:
function asyncFunction() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('Async Hello world');
}, 16);
});
}
asyncFunction().then(function (value) {
console.log(value); // => 'Async Hello world'
}).catch(function (error) {
console.log(error); // =>如果出错,将打印错误
});
//new Promise构造器之后,会返回一个promise对象
//为promise对象用设置 .then 调用返回值时的回调函数。
asyncFunction 这个函数会返回promise对象, 对于这个promise对象,我们调用它的 then 方法来设置resolve后的回调函数, catch 方法来设置发生错误时的回调函数。(相当于正确执行then,错误执行catch)
上面的处理还可以写成第二类的形式:
asyncFunction().then(function (value) {
console.log(value); // => 'Async Hello world'
}, function (error) {
console.log(error); // =>'错误执行这里'
});
1.2 Promise的状态:
"has-resolution" - Fulfilled
resolve(成功)时。此时会调用 onFulfilled
"has-rejection" - Rejected
reject(失败)时。此时会调用 onRejected
"unresolved" - Pending
既不是resolve也不是reject的状态。也就是promise对象刚被创建后的初始化状态等
初始化后,正确执行,错误执行。


1.3 编写Promise代码
1.创建Promise对象
new Promise(fn)返回一个promise对象在
fn中指定异步等处理处理结果正常的话,调用
resolve(处理结果值)处理结果错误的话,调用
reject(Error对象)
2.编写Promise对象处理方法
XXX.then(function onFulfilled(){ //XXX 返回Promise对象
//正确执行
}).catch(function onRejected (error){
//错误执行
})
例子如下:
function getURL(URL) {
return new Promise(function (resolve, reject) {
var req = new XMLHttpRequest();
req.open('GET', URL, true);
req.onload = function () {
if (req.status === 200) {
resolve(req.responseText);
} else {
reject(new Error(req.statusText));
}
};
req.onerror = function () {
reject(new Error(req.statusText));
};
req.send();
});
}
// 运行示例
var URL = "http://httpbin.org/get";
getURL(URL).then(function onFulfilled(value){
console.log(value);
}).catch(function onRejected(error){
console.error(error);
});
//可以在 Google的console 环境下运行
2.实战Promise(Promise.resolve 和 Promise.reject)
2.1 Promise.resolve
一般我们会用 new Promise() 创建对象
例如:
new Promise(function(resolve){
resolve(42);
});
但可以用 以下的方法 简化:
Promise.resolve(42);
在这段代码中的 resolve(42); 会让这个promise对象立即进入确定(即resolved)状态,并将 42 传递给后面then里所指定的 onFulfilled 函数。
方法 Promise.resolve(value); 的返回值也是一个promise对象,所以我们可以像下面那样接着对其返回值进行 .then 调用。
Promise.resolve(42).then(function(value){
console.log(value); // =>42
});
2.2 Promise.reject
对应前面的用法,我们可以变通一下
new Promise(function(resolve,reject){
reject(new Error("出错了"));
});
简化:
Promise.reject(new Error("出错了"))
这段代码的功能是调用该promise对象通过then指定的 onRejected 函数,并将错误(Error)对象传递给这个 onRejected 函数。
Promise.reject(new Error("BOOM!")).catch(function(error){
console.error(error);
});
2.3 Promise#then
大家已经学会了 .then().catch() 的写法,如果方法链变得更长:
function taskA() {
console.log("Task A");
}
function taskB() {
console.log("Task B");
}
function onRejected(error) {
console.log("Catch Error: A or B", error);
}
function finalTask() {
console.log("Final Task");
}
var promise = Promise.resolve();
promise
.then(taskA)
.then(taskB)
.catch(onRejected)
.then(finalTask);
// Task A
// Task B
// Final Task
taskA() taskB() finalTask() 顺利执行 因为A B没有发生错误,所以没有被执行到。 如果将 taskB() 故意写成错误的
function taskA() {
console.log("Task A");
}
function taskB() {
console.log1111("Task B");
}
function onRejected(error) {
console.log("Catch Error: A or B", error);
}
function finalTask() {
console.log("Final Task");
}
var promise = Promise.resolve();
promise
.then(taskA)
.then(taskB)
.catch(onRejected)
.then(finalTask);
// Task A
// Catch Error: A or B,TypeError: Object [object Object] has no method 'log11'
// Final Task
taskA()正常 onRejected抛出A或B产生的错误

2.3.1 Promise 传值
想要传值,只需要return 示例:
function doubleUp(value) {
return value * 2;
}
function increment(value) {
return value + 1;
}
function output(value) {
console.log(value);// => (1 + 1) * 2
}
var promise = Promise.resolve(1);
promise
.then(increment)
.then(doubleUp)
.then(output)
.catch(function(error){
// promise chain中出现异常的时候会被调用
console.error(error);
});
这段代码的入口函数是 Promise.resolve(1); ,整体的promise chain执行流程如下所示。
Promise.resolve(1);传递 1 给increment函数函数
increment对接收的参数进行 +1 操作并返回(通过return)这时参数变为2,并再次传给
doubleUp函数最后在函数
output中打印结果
每个方法中 return 的值不仅只局限于字符串或者数值类型,也可以是对象或者promise对象等复杂类型。
return的值会由 Promise.resolve(return的返回值); 进行相应的包装处理,因此不管回调函数中会返回一个什么样的值,最终 then 的结果都是返回一个新创建的promise对象。
2.4 Promise#Catch
这里的Promise#catch是 promise.then(undefined, onRejected); 方法的一个别名而已,用法如下:
var promise = Promise.reject(new Error("message"));
promise.catch(function (error) {
console.error(error);
});
但是在IE8及以下版本则会出现 identifier not found 的语法错误,因为catch是保留字有关。
解决方法有两种:
1.使用[]代替.
var promise = Promise.reject(new Error("message"));
promise["catch"](function (error) {
console.error(error);
});
2.使用原始写法
var promise = Promise.reject(new Error("message"));
promise.then(undefined, function (error) {
console.error(error);
})
2.5 每次调用then都会返回一个新创建的promise对象
then 或 catch 方法每次都会创建并返回一个新的promise对象 先看例子:
// 1: 对同一个promise对象同时调用 `then` 方法
var aPromise = new Promise(function (resolve) {
resolve(100);
});
aPromise.then(function (value) {
return value * 2;
});
aPromise.then(function (value) {
return value * 2;
});
aPromise.then(function (value) {
console.log("1: " + value); // => 100
}) // vs // 2: 对 `then` 进行 promise chain 方式进行调用
var bPromise = new Promise(function (resolve) {
resolve(100);
});
bPromise.then(function (value) {
return value * 2;
}).then(function (value) {
return value * 2;
}).then(function (value) {
console.log("2: " + value); // => 100 * 2 * 2
});
第1种写法中并没有使用promise的方法链方式,这在Promise中是应该极力避免的写法。这种写法中的 then 调用几乎是在同时开始执行的,而且传给每个 then 方法的 value 值都是 100 。
第2中写法则采用了方法链的方式将多个 then 方法调用串连在了一起,各函数也会严格按照 resolve → then → then → then 的顺序执行,并且传给每个 then 方法的 value 的值都是前一个promise对象通过 return 返回的值。
then 的错误使用方法function badAsyncCall() {
var promise = Promise.resolve();
promise.then(function() {
// 任意处理
return newVar;
});
return promise;
}
这种写法有很多问题,首先在 promise.then 中产生的异常不会被外部捕获,此外,也不能得到 then 的返回值,即使其有返回值。
由于每次 promise.then 调用都会返回一个新创建的promise对象,因此需要像上述方式2那样,采用promise chain的方式将调用进行链式化,修改后的代码如下所示。
then 返回返回新创建的promise对象function anAsyncCall() {
var promise = Promise.resolve();
return promise.then(function() {
// 任意处理
return newVar;
});
}
2.6 Promise.all
Promise.all 接收一个 promise对象的数组作为参数,当这个数组里的所有promise对象全部变为resolve或reject状态的时候,它才会去调用 .then 方法
首先是一个没有使用.all 的例子:
function getURL(URL) {
return new Promise(function (resolve, reject) {
var req = new XMLHttpRequest();
req.open('GET', URL, true);
req.onload = function () {
if (req.status === 200) {
resolve(req.responseText);
} else {
reject(new Error(req.statusText));
}
};
req.onerror = function () {
reject(new Error(req.statusText));
};
req.send();
});
}
// 发送XHR请求
var request = {
comment: function getComment() {
return getURL('http://azu.github.io/promises-book/json/comment.json').then(JSON.parse);
},
people: function getPeople() {
return getURL('http://azu.github.io/promises-book/json/people.json').then(JSON.parse);
}
};
function main() {
function recordValue(results, value) {
results.push(value);
return results;
}
// [] 用来保存初始化的值
var pushValue = recordValue.bind(null, []);
return request.comment().then(pushValue).then(request.people).then(pushValue);
}
// 运行的例子
main().then(function (value) {
console.log(value);
}).catch(function(error){
console.error(error);
});
发送两个XHR请求,然后mian方法顺序处理,看到main()里面的写法 估计都是晕晕的。
使用.all之后
function getURL(URL) {
return new Promise(function (resolve, reject) {
var req = new XMLHttpRequest();
req.open('GET', URL, true);
req.onload = function () {
if (req.status === 200) {
resolve(req.responseText);
} else {
reject(new Error(req.statusText));
}
};
req.onerror = function () {
reject(new Error(req.statusText));
};
req.send();
});
}
var request = {
comment: function getComment() {
return getURL('http://azu.github.io/promises-book/json/comment.json').then(JSON.parse);
},
people: function getPeople() {
return getURL('http://azu.github.io/promises-book/json/people.json').then(JSON.parse);
}
};
function main() {
return Promise.all([request.comment(), request.people()]);
}
// 运行示例
main().then(function (value) {
console.log(value);
}).catch(function(error){
console.log(error);
});
mian()里面简洁多了,也很好理解了
在上面的代码中,request.comment() 和 request.people() 会同时开始执行,而且每个promise的结果(resolve或reject时传递的参数值),和传递给 Promise.all 的promise数组的顺序是一致的。
main().then(function (results) {
console.log(results); // 按照[comment, people]的顺序
});
以下代码可以看得出.all是同时执行的:
// `delay`毫秒后执行resolve
function timerPromisefy(delay) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(delay);
}, delay);
});
}
var startDate = Date.now();
// 所有promise变为resolve后程序退出
Promise.all([
timerPromisefy(1),
timerPromisefy(32),
timerPromisefy(64),
timerPromisefy(128)
]).then(function (values) {
console.log(Date.now() - startDate + 'ms');
// 約128ms
console.log(values); // [1,32,64,128]
});
这个promise对象数组中所有promise都变为resolve状态的话,至少需要128ms。实际我们计算一下Promise.all 的执行时间的话,它确实是消耗了128ms的时间。
从上述结果可以看出,传递给 Promise.all 的promise并不是一个个的顺序执行的,而是同时开始、并行执行的。
2.7 Promise.race
Promise.race 只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理
// `delay`毫秒后执行resolve
function timerPromisefy(delay) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(delay);
console.log(delay+',')
}, delay);
});
}
// 任何一个promise变为resolve或reject 的话程序就停止运行 但不会取消其他Promise的执行
Promise.race([
timerPromisefy(1),
timerPromisefy(32),
timerPromisefy(64),
timerPromisefy(128)
]).then(function (value) {
console.log(value); // => 1, 32, 1 64, 128,
});
到了最后,估计您已经对promise模式有了一个比较完整的了解,异步编程会变得越来越重要,在这种情况下,我们需要找到办法来降低复杂度,promise模式就是一个很好的例子。
以上文章是参考 JavaScript Promise 迷你书,想更加深入的同学请移步这里。
javaScript Promise 入门的更多相关文章
- Javascript Promise入门
是什么? https://www.promisejs.org/ What is a promise? The core idea behind promises is that a promise r ...
- JavaScript Promise:去而复返
原文:http://www.html5rocks.com/en/tutorials/es6/promises/ 作者:Jake Archibald 翻译:Amio 女士们先生们,请准备好迎接 Web ...
- JavaScript从入门到精通(转)
JavaScript从入门到精通 转自: https://github.com/Eished/JavaScript_notes 视频连接:https://www.bilibili.com/video/ ...
- Promise入门到精通(初级篇)-附代码详细讲解
Promise入门到精通(初级篇)-附代码详细讲解 Promise,中文翻译为承诺,约定,契约,从字面意思来看,这应该是类似某种协议,规定了什么事件发生的条件和触发方法. Pr ...
- promise入门基本使用
Promise入门详解和基本用法 异步调用 异步 JavaScript的执行环境是单线程. 所谓单线程,是指JS引擎中负责解释和执行JavaScript代码的线程只有一个,也就是一次只能完成一项任 ...
- 【转】HTML, CSS和Javascript调试入门
转 http://www.cnblogs.com/PurpleTide/archive/2011/11/25/2262269.html HTML, CSS和Javascript调试入门 本文介绍一些入 ...
- [Javascript] Promise
Promise 代表着一个异步操作,这个异步操作现在尚未完成,但在将来某刻会被完成. Promise 有三种状态 pending : 初始的状态,尚未知道结果 fulfilled : 代表操作成功 r ...
- Javascript Promise 学习笔记
1. 定义:Promise是抽象异步处理对象以及对其进行各种操作的组件,它把异步处理对象和异步处理规则采用统一的接口进行规范化. 2. ES6 Promises 标准中定义的API: ...
- JavaScript快速入门(四)——JavaScript函数
函数声明 之前说的三种函数声明中(参见JavaScript快速入门(二)——JavaScript变量),使用Function构造函数的声明方法比较少见,我们暂时不提.function func() { ...
随机推荐
- js+jquery 常用选择器函数
一.获取当前标签 JS: this,如下: <button onclick="fun(this)"></button> Jquery,如下: $(" ...
- Win10间歇性卡顿
Win10间歇性卡顿 1.关闭不必要的服务:Windows Update.Windows Search.SuperFetch.Background Intelligent Transfer Servi ...
- 洛谷 P2893 [USACO08FEB]修路Making the Grade 解题报告
P2893 [USACO08FEB]修路Making the Grade 题目描述 A straight dirt road connects two fields on FJ's farm, but ...
- WCF分布式开发步步为赢(13):WCF服务离线操作与消息队列MSMQ
之前曾经写过一个关于MSMQ消息队列的文章:WCF分布式开发必备知识(1):MSMQ消息队列 ,当时的目的也是用它来作为学习WCF 消息队列MSMQ编程的基础文章.在那篇文章里,我们详细介绍了MSMQ ...
- 洛谷P1273 有线电视网 (树上分组背包)
洛谷P1273 有线电视网 题目描述 某收费有线电视网计划转播一场重要的足球比赛.他们的转播网和用户终端构成一棵树状结构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树的内部节 ...
- event对象和事件冒泡
<!DOCTYPE HTML><html><head> <meta charset="utf-8"> <title>无标 ...
- NET面试题 (四)
1, 面向对象的思想主要包括什么? 封装.继承.多态. TLW: 封装:用抽象的数据类型将数据和基于数据的操作封装在一起,数据被保护在抽象数据类型内部. 继承:子类拥有父类的所有数据和操作. 多态:一 ...
- 【BZOJ4008】【HNOI2015】亚瑟王 [期望DP]
亚瑟王 Time Limit: 20 Sec Memory Limit: 512 MB[Submit][Status][Discuss] Description 小 K 不慎被 LL 邪教洗脑了,洗 ...
- [BZOJ2946] [Poi2000]公共串解题报告|后缀数组
给出几个由小写字母构成的单词,求它们最长的公共子串的长度. 单词个数<=5,每个单词长度<=2000 尽管最近在学的是SAM...但是看到这个题还是忍不住想写SA... (其实是不 ...
- [POJ1144][BZOJ2730]tarjan求割点
求割点 一种显然的n^2做法: 枚举每个点,去掉该点连出的边,然后判断整个图是否联通 用tarjan求割点: 分情况讨论 如果是root的话,其为割点当且仅当下方有两棵及以上的子树 其他情况 设当前节 ...