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 会被调用

onFulfilledonRejected 两个都为可选参数。

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对象

  1. new Promise(fn) 返回一个promise对象

  2. 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执行流程如下所示。

  1. Promise.resolve(1); 传递 1 给 increment 函数

  2. 函数 increment 对接收的参数进行 +1 操作并返回(通过return

  3. 这时参数变为2,并再次传给 doubleUp 函数

  4. 最后在函数 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 入门的更多相关文章

  1. Javascript Promise入门

    是什么? https://www.promisejs.org/ What is a promise? The core idea behind promises is that a promise r ...

  2. JavaScript Promise:去而复返

    原文:http://www.html5rocks.com/en/tutorials/es6/promises/ 作者:Jake Archibald 翻译:Amio 女士们先生们,请准备好迎接 Web ...

  3. JavaScript从入门到精通(转)

    JavaScript从入门到精通 转自: https://github.com/Eished/JavaScript_notes 视频连接:https://www.bilibili.com/video/ ...

  4. Promise入门到精通(初级篇)-附代码详细讲解

    Promise入门到精通(初级篇)-附代码详细讲解 ​     Promise,中文翻译为承诺,约定,契约,从字面意思来看,这应该是类似某种协议,规定了什么事件发生的条件和触发方法. ​     Pr ...

  5. promise入门基本使用

    Promise入门详解和基本用法   异步调用 异步 JavaScript的执行环境是单线程. 所谓单线程,是指JS引擎中负责解释和执行JavaScript代码的线程只有一个,也就是一次只能完成一项任 ...

  6. 【转】HTML, CSS和Javascript调试入门

    转 http://www.cnblogs.com/PurpleTide/archive/2011/11/25/2262269.html HTML, CSS和Javascript调试入门 本文介绍一些入 ...

  7. [Javascript] Promise

    Promise 代表着一个异步操作,这个异步操作现在尚未完成,但在将来某刻会被完成. Promise 有三种状态 pending : 初始的状态,尚未知道结果 fulfilled : 代表操作成功 r ...

  8. Javascript Promise 学习笔记

    1.     定义:Promise是抽象异步处理对象以及对其进行各种操作的组件,它把异步处理对象和异步处理规则采用统一的接口进行规范化. 2.     ES6 Promises 标准中定义的API: ...

  9. JavaScript快速入门(四)——JavaScript函数

    函数声明 之前说的三种函数声明中(参见JavaScript快速入门(二)——JavaScript变量),使用Function构造函数的声明方法比较少见,我们暂时不提.function func() { ...

随机推荐

  1. 【bzoj2705】[SDOI2012]Longge的问题 欧拉函数

    题目描述 Longge的数学成绩非常好,并且他非常乐于挑战高难度的数学问题.现在问题来了:给定一个整数N,你需要求出∑gcd(i, N)(1<=i <=N). 输入 一个整数,为N. 输出 ...

  2. 洛谷 P2893 [USACO08FEB]修路Making the Grade 解题报告

    P2893 [USACO08FEB]修路Making the Grade 题目描述 A straight dirt road connects two fields on FJ's farm, but ...

  3. JSOI2008 星球大战 [并查集]

    题目描述 很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系. 某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球.这些星球通过特殊的以太隧 ...

  4. NOIP2016愤怒的小鸟 [状压dp]

    愤怒的小鸟 题目描述 Kiana 最近沉迷于一款神奇的游戏无法自拔. 简单来说,这款游戏是在一个平面上进行的. 有一架弹弓位于 (0,0) 处,每次 Kiana 可以用它向第一象限发射一只红色的小鸟, ...

  5. E. Intercity Travelling

    E. Intercity Travelling time limit per test 1.5 seconds memory limit per test 256 megabytes input st ...

  6. bzoj 5093 [Lydsy1711月赛]图的价值 NTT+第二类斯特林数

    [Lydsy1711月赛]图的价值 Time Limit: 30 Sec  Memory Limit: 256 MBSubmit: 245  Solved: 128[Submit][Status][D ...

  7. 测试数据库DG搭建为正式库以后做准备

    Data guard 部署 1.系统准备(备库只需建立数据库软件) 两台操作系统 oracle linux 7 Node1 172.16.70.191 Node2 172.16.70.192 Orac ...

  8. uva 11427

    题目大意:每天晚上你都玩纸牌,如果第一次赢了就高高兴兴地去睡觉:如果输了就接着玩,假设每盘游戏获胜的的概率都是p,且各盘游戏相互独立.你是一个固执的完美主义者,因此会一直玩到当晚获胜局数的比例严格大于 ...

  9. VR行业纷纷倒闭:有硬件没内容

    从去年年底开始,VR就成为了一个流行词汇,不仅是巨头公司砸钱布局,众多创业公司也纷纷投入其中.但是,一窝蜂拥入的企业基本都没有成熟的商业模式和赢利模式,只能靠融资供血.在资本寒冬中,大部分的VR企业开 ...

  10. COGS2090 Asm.Def找燃料

    时间限制:1 s   内存限制:256 MB [题目描述] “听说咱们要完了?”比利·海灵顿拨弄着操纵杆,头也不回地问Asm.Def. “不要听得风就是雨.” “开个玩笑嘛.不就是打机器人,紧张啥,你 ...