JavaScript Promise

说明

我们都知道JavaScript是单线程的,这意味着任何两句代码都不能同时运行,它们得一个接一个来。在浏览器中,JavaScript 和其他任务共享一个线程,不同的浏览器略有差异,但大体上这些和 JavaScript共享线程的任务包括重绘、更新样式、用户交互等,所有这些任务操作都会阻塞其他任务。
作为人类,你是多线程的。你可以用多个手指同时敲键盘,也可以一边开车一遍电话。唯一的全局阻塞函数是打喷嚏,打喷嚏期间所有其他事务都会暂停。很烦人对么?尤其是当你开着车打着电话的时候。我们都不喜欢这样打喷嚏的代码。尤其是在浏览器环境中往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。为了解决这个问题,Javascript语言将任务的执行模式分成两种:

“同步模式”就是后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的;”异步模式”则完全不同,每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。

“异步模式”非常重要。在浏览器端,耗时很长的操作都应该异步执行,避免浏览器失去响应,最好的例子就是Ajax操作。在服务器端,”异步模式”甚至是唯一的模式,因为执行环境是单线程的,如果允许同步执行所有http请求,服务器性能会急剧下降,很快就会失去响应。

而在异步模式中Promise的出现无疑是一件让人欢呼雀跃的事,这是为什么呢?

什么是Promise

一个promise代表了异步操作的最终结果,promise交互的主要方法是通过.then方法。Promise对象其实是CommonJS工作组提出的一种规范,目的是为异步编程提供统一接口

Promise对象有以下几种状态:

  • pending: 初始状态, 既不是 fulfilled 也不是 rejected.
  • fulfilled: 成功的操作.
  • rejected: 失败的操作.

这里要稍微拓展一点小知识,不知道你有没有听过薛定谔的猫。大体的意思是说在一个盒子里有一只猫,以及少量放射性物质。之后,有50%的概率放射性物质将会衰变并释放出毒气杀死这只猫,同时有50%的概率放射性物质不会衰变而猫将活下来。你永远的不会知道猫是死还是活在盒子未被打开前。这一点和promise的机制很相似,也就是说在异步回调返回之前,程序是无法知道结果是成功还是失败,因此我们给出了相应的预处理。

pending状态的promise对象既可转换为带着一个成功值的fulfilled 状态,也可变为带着一个失败信息的 rejected 状态。当状态发生转换时,promise.then绑定的方法(函数句柄)就会被调用。(当绑定方法时,如果 promise对象已经处于 fulfilled 或 rejected 状态,那么相应的方法将会被立刻调用, 所以在异步操作的完成情况和它的绑定方法之间不存在竞争条件。)
因为Promise.prototype.thenPromise.prototype.catch方法返回 promises对象, 所以它们可以被链式调用—— 一种被称为 composition 的操作。

JavaScript-promise

Promise的常见用法

  • 与XMLHttpRequest结合使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
'use strict';

// A-> $http function is implemented in order to follow the standard Adapter pattern
function $http(url){

// A small example of object
var core = {

// Method that performs the ajax request
ajax : function (method, url, args) {

// Creating a promise
var promise = new Promise( function (resolve, reject) {

// Instantiates the XMLHttpRequest
var client = new XMLHttpRequest();
var uri = url;

if (args && (method === 'POST' || method === 'PUT')) {
uri += '?';
var argcount = 0;
for (var key in args) {
if (args.hasOwnProperty(key)) {
if (argcount++) {
uri += '&';
}
uri += encodeURIComponent(key) + '=' + encodeURIComponent(args[key]);
}
}
}

client.open(method, uri);
client.send();

client.onload = function () {
if (this.status >= 200 && this.status < 300) {
// Performs the function "resolve" when this.status is equal to 2xx
resolve(this.response);
} else {
// Performs the function "reject" when this.status is different than 2xx
reject(this.statusText);
}
};
client.onerror = function () {
reject(this.statusText);
};
});

// Return the promise
return promise;
}
};

// Adapter pattern
return {
'get' : function(args) {
return core.ajax('GET', url, args);
},
'post' : function(args) {
return core.ajax('POST', url, args);
},
'put' : function(args) {
return core.ajax('PUT', url, args);
},
'delete' : function(args) {
return core.ajax('DELETE', url, args);
}
};
};

// B-> Here you define its functions and its payload
var mdnAPI = 'https://developer.mozilla.org/en-US/search.json';
var payload = {
'topic' : 'js',
'q' : 'Promise'
};

var callback = {
success : function(data){
console.log(1, 'success', JSON.parse(data));
},
error : function(data){
console.log(2, 'error', JSON.parse(data));
}
};
// End B

// Executes the method call
$http(mdnAPI)
.get(payload)
.then(callback.success)
.catch(callback.error);

// Executes the method call but an alternative way (1) to handle Promise Reject case
$http(mdnAPI)
.get(payload)
.then(callback.success, callback.error);

// Executes the method call but an alternative way (2) to handle Promise Reject case
$http(mdnAPI)
.get(payload)
.then(callback.success)
.then(undefined, callback.error);

上面代码其实是javascript的一种设计模式,详细可阅读Learning JavaScript Design Patterns。适配器模式的作用是解决两个软件实体间的接口不兼容的问题。使用适配器模式之后,原本由于接口不兼容而不能工作的两个软件实体可以一起工作。适配器的别名是包装器(wrapper),这是一个相对简单的模式。通过适配器模式将传统的xhr请求封装成restful风格然后返回Promise进一步方便链式调用。

  • 用于图片加载

See the Pen promise-test by Gnipbao (@Gnipbao) on CodePen.

如果你想系统的学习promsie的话建议你去看promise迷你书写的相当不错。
最后推荐一个不错的promiseES6-Promise

参阅