使用 Bluebird 开发异步的 JavaScript 程序(1)
- UID
- 1066743
|
使用 Bluebird 开发异步的 JavaScript 程序(1)
JavaScript 异步编程JavaScript 是一门单线程语言,被广泛的应用于浏览器端和页面 DOM 元素交互。自从 Node.js 横空出世,JavaScript 的领域由浏览器扩展到服务器端,变成了一种通用的计算机程序设计语言。那么,JavaScript 是怎么实现异步编程的呢?
Callback 和 Callback 地狱JavaScript 的异步是用 Callback 函数来实现的。Callback 函数可以接收外部程序传入的参数,但是没办法向外部传值,只能通过下一个 Callback 函数来使用。这使得当逻辑复杂的时候,Callback 嵌套变得很深。在这种情况下,参数互相影响,导致 bug 很多。这种嵌套被称为 Callback 地狱。
Promise 和 BluebirdPromise 是 JavaScript 中解决 Callback 地狱的方式,也被 ECMAScript 协会承认,固化为 ES6 语法的一部分。Bluebird 是早期 Promise 的一种实现,它提供了丰富的方法和语法糖,一方面降低了 Promise 的使用难度,一方面扩展了 Promise 的功能。下文中所有的方法来自于 Bluebird api 接口。
Promise 基础Promise 是一个承诺,表示一次操作的结果。和一般操作不同的是,Promise 对象不是实时的,而是在未来的某一时刻,会有返回。我们知道,JavaScript 在浏览器中是单线程调用的,但是有时我们又需要异步操作,例如访问文件,或者是调用 ajax。这时候,我们就需要 Promise 了。
以下清单 1 展示了 Promise 创建和使用过程。
- 首先,new Promise 可以传入两个函数,resolve 和 reject 方法。当 Promise 正确调用返回 resolve,并封装返回用户数据;当调用失败时,调用 reject 方法,并封装失败原因。
- 其次,Promise 的返回值只能在链式方法中调用。
清单 1. 创建 Promise 对象并返回1
2
3
4
5
6
7
8
9
10
11
12
13
| "use strict";
describe("Suite1", function(){
let Promise = require("bluebird");
it("Promise basic function arch", function(){
let promise = new Promise(function(resolve, reject){
let returnBody = true;
return returnBody?resolve(returnBody):reject(returnBody);
});
promise.then(function(data){
expect(data).toEqual(true);
});
});
});
|
Promise resolve 和 reject 语法糖以上创建 Promise 用的是 new 的完整方法。Bluebird 也提供 resolve 和 reject 的语法糖,可以方便的创建一个 Promise 对象,如清单二。
清单 2. Promise resolve 和 reject 语法糖的应用1
2
3
4
5
6
7
8
9
10
| it("Promise resolve and reject sugar", function(){
let resolvePromise = Promise.resolve(true);
let rejectPromise = Promise.resolve(false);
resolvePromise.then(function(data){
expect(data).toBeTruthy();
})
rejectPromise.then(function(data){
expect(data).toBeFalsy();
})
});
|
Then 方法Then 方法可以说是 Promise 中最基本的方法,它可以接受两个参数,第一个 onFulfilled handler,第二个 onRejected handler。一般来说这两个都是函数,在需要忽略任意一个 handler 的时候,可以用对象来代替。如清单 3 代码。
清单 3. 忽略 Then 中的完成函数1
2
3
4
5
6
7
8
9
| it("Then function with ignore fulFilled handler", function(){
let promise = new Promise(function(resolve, reject){
let returnBody = false;
return returnBody?resolve(returnBody):reject(returnBody);
});
promise.then({},function(error){
expect(error).toEqual(false);
});
});
|
Promise 链需要注意的是 Promise.then 方法返回的也是一个 Promise 对象,它同时也可以被下一个方法承接。类似于清单 4 中的伪代码。
清单 4. Promise 链式调用Promise.then().then().then()
那么在后面的 then 中接受的对象是什么呢?Promise 中,then 中可以有一个 return 方法,这里的 return 返回的是下一个 then 方法中的参数对象。如清单 5 代码中分别返回了一个对象,和一个函数。Bluebird 会把返回值转化为 Promise 对象供下一个函数调用。
清单 5. Promise 函数中的返回值1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| it("Promise chain with return function", function(){
let promise = new Promise(function(resolve, reject){
let returnBody = true;
return returnBody?resolve(returnBody):reject(returnBody);
});
promise.then(function(data){
expect(data).toEqual(true);
return {
a:1,
b:2
};
}).then(function(data){
expect(data.a).toEqual(1);
expect(data.b).toEqual(2);
return function(){
return "string";
};
}).then(function(func){
expect(_.isFunction(func)).toBeTruthy();
let string = func();
expect(string).toEqual("string");
});
});
|
Spread 方法spread 方法适用于 Promise 对象返回值是数组的时候。与 then 方法相同,spread 方法返回一个 Promise 对象。如清单 6。
清单 6. Spread 方法的应用1
2
3
4
5
6
7
8
9
10
11
| it("Spread function basic without Promise.all", function(){
let promise = new Promise(function(resolve, reject){
let returnBody = [1,2,3];
return returnBody?resolve(returnBody):reject(returnBody);
});
promise.spread(function(data1,data2,data3){
expect(data1).toEqual(1);
expect(data2).toEqual(2);
expect(data3).toEqual(3);
});
});
|
或者可以用解析方法把参数展开,如清单 7 代码段,这里的 spread 方法和 then 方法的功能相同。
清单 7. Spread 和 Then 的调用1
2
3
4
5
6
| promise.spread(function(...data){
expect(data).toEqual([1,2,3]);
});
promise.then(function(data){
expect(data).toEqual([1,2,3]);
});
|
Bind 函数在 Promise 对象调用的时候,存在一个上下文对象,即 this 对象。一般情况下,我们不能访问到 this 对象,但是 Bind 函数可以帮助我们 bind 一个 this。这在链式调用传递参数的时候非常有用。如清单 8。需要说明的是,this 对象和 then 函数中的返回值没有关系。
清单 8. Bind 方法的使用1
2
3
4
5
6
7
8
9
10
| it("Bind function", function(){
Promise.bind({a:1}).then(function(){
this.b = 2;
}).then(function(){
this.c = 3;
return 6;
}).then(function(data){
expect(this.a+this.b+this.c).toEqual(data);
});
});
|
Promise try 和 methodPromise try 和 Method 都是方便创建 Promise 的方法。我们可以观察到,在 then 的 return 方法中,可以返回一个数值,一个数组,甚至是一个函数,这些都可以被解析成 Promise 对象而被下一次链式调用。
Try 和 Method 同理,可以把含有 error 抛出的或有正常的返回值的函数,转化为 Promise 对象,方便之后调用。它俩的不同点是,try 返回的是 Promise 对象,method 返回的是一个函数,函数调用之后是 Promise 对象。如以下清单 9。
清单 9. Promise try 和 method 的区别1
2
3
4
5
6
7
8
9
10
11
12
| it("Error and method functions", function(){
Promise.try(function(){
throw new Error("this should be error");
}).then({}, function(err){
expect(err).toEqual(new Error("this should be error"));
});
Promise.method(function(){
return "message";
})().then(function(data){//注意这里的 method 函数返回之后要进行调用再执行 then 方法
expect(data).toBe("message");
})
});
|
链式调用中的 Promise 函数Promise 函数有两种:
- 一种是用来创建 Promise 链的头对象的,例如 resolve 和 reject 方法;
- 另外一种只能在链式调用过程中使用,例如 then,spread 方法。
但是有一种函数,例如 bind,它既能创建头对象,也能在链式过程中调用,既有 Promise.bind(),又有.bind()。Bluebird 提供了大量这样的函数,我们在后面的容器管理中,可以具体看到。 |
|
|
|
|
|