- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章JavaScript Promise启示录由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
本篇,主要普及promise的用法.
一直以来,JavaScript处理异步都是以callback的方式,在前端开发领域callback机制几乎深入人心。在设计API的时候,不管是浏览器厂商还是SDK开发商亦或是各种类库的作者,基本上都已经遵循着callback的套路.
近几年随着JavaScript开发模式的逐渐成熟,CommonJS规范顺势而生,其中就包括提出了Promise规范,Promise完全改变了js异步编程的写法,让异步编程变得十分的易于理解.
在callback的模型里边,我们假设需要执行一个异步队列,代码看起来可能像这样:
1
2
3
4
5
6
7
|
loadImg(
'a.jpg'
,
function
() {
loadImg(
'b.jpg'
,
function
() {
loadImg(
'c.jpg'
,
function
() {
console.log(
'all done!'
);
});
});
});
|
这也就是我们常说的回调金字塔,当异步的任务很多的时候,维护大量的callback将是一场灾难。当今Node.js大热,好像很多团队都要用它来做点东西以沾沾“洋气”,曾经跟一个运维的同学聊天,他们也是打算使用Node.js做一些事情,可是一想到js的层层回调就望而却步.
好,扯淡完毕,下面进入正题.
Promise可能大家都不陌生,因为Promise规范已经出来好一段时间了,同时Promise也已经纳入了ES6,而且高版本的chrome、firefox浏览器都已经原生实现了Promise,只不过和现如今流行的类Promise类库相比少些API.
所谓Promise,字面上可以理解为“承诺”,就是说A调用B,B返回一个“承诺”给A,然后A就可以在写计划的时候这么写:当B返回结果给我的时候,A执行方案S1,反之如果B因为什么原因没有给到A想要的结果,那么A执行应急方案S2,这样一来,所有的潜在风险都在A的可控范围之内了.
上面这句话,翻译成代码类似:
1
2
3
4
5
|
var
resB = B();
var
runA =
function
() {
resB.then(execS1, execS2);
};
runA();
|
只看上面这行代码,好像看不出什么特别之处。但现实情况可能比这个复杂许多,A要完成一件事,可能要依赖不止B一个人的响应,可能需要同时向多个人询问,当收到所有的应答之后再执行下一步的方案。最终翻译成代码可能像这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
var
resB = B();
var
resC = C();
...
var
runA =
function
() {
reqB
.then(resC, execS2)
.then(resD, execS3)
.then(resE, execS4)
...
.then(execS1);
};
runA();
|
在这里,当每一个被询问者做出不符合预期的应答时都用了不同的处理机制。事实上,Promise规范没有要求这样做,你甚至可以不做任何的处理(即不传入then的第二个参数)或者统一处理.
好了,下面我们来认识下Promise/A+规范:
then
方法(可以说,then就是promise的核心),而且then必须返回一个promise,同一个promise的then可以调用多次,并且回调的执行顺序跟它们被定义时的顺序一致可以看到,Promise规范的内容并不算多,大家可以试着自己实现以下Promise.
以下是笔者自己在参考许多类Promise库之后简单实现的一个Promise,代码请移步promiseA.
简单分析下思路:
构造函数Promise接受一个函数resolver,可以理解为传入一个异步任务,resolver接受两个参数,一个是成功时的回调,一个是失败时的回调,这两参数和通过then传入的参数是对等的.
其次是then的实现,由于Promise要求then必须返回一个promise,所以在then调用的时候会新生成一个promise,挂在当前promise的_next上,同一个promise多次调用都只会返回之前生成的_next.
由于then方法接受的两个参数都是可选的,而且类型也没限制,可以是函数,也可以是一个具体的值,还可以是另一个promise。下面是then的具体实现:
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
|
Promise.prototype.then =
function
(resolve, reject) {
var
next =
this
._next || (
this
._next = Promise());
var
status =
this
.status;
var
x;
if
(
'pending'
=== status) {
isFn(resolve) &&
this
._resolves.push(resolve);
isFn(reject) &&
this
._rejects.push(reject);
return
next;
}
if
(
'resolved'
=== status) {
if
(!isFn(resolve)) {
next.resolve(resolve);
}
else
{
try
{
x = resolve(
this
.value);
resolveX(next, x);
}
catch
(e) {
this
.reject(e);
}
}
return
next;
}
if
(
'rejected'
=== status) {
if
(!isFn(reject)) {
next.reject(reject);
}
else
{
try
{
x = reject(
this
.reason);
resolveX(next, x);
}
catch
(e) {
this
.reject(e);
}
}
return
next;
}
};
|
。
这里,then做了简化,其他promise类库的实现比这个要复杂得多,同时功能也更多,比如还有第三个参数——notify,表示promise当前的进度,这在设计文件上传等时很有用。对then的各种参数的处理是最复杂的部分,有兴趣的同学可以参看其他类Promise库的实现.
在then的基础上,应该还需要至少两个方法,分别是完成promise的状态从pending到resolved或rejected的转换,同时执行相应的回调队列,即resolve()和reject()方法.
到此,一个简单的promise就设计完成了,下面简单实现下两个promise化的函数:
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
|
function
sleep(ms) {
return
function
(v) {
var
p = Promise();
setTimeout(
function
() {
p.resolve(v);
}, ms);
return
p;
};
};
function
getImg(url) {
var
p = Promise();
var
img =
new
Image();
img.onload =
function
() {
p.resolve(
this
);
};
img.onerror =
function
(err) {
p.reject(err);
};
img.url = url;
return
p;
};
|
由于Promise构造函数接受一个异步任务作为参数,所以getImg还可以这样调用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function
getImg(url) {
return
Promise(
function
(resolve, reject) {
var
img =
new
Image();
img.onload =
function
() {
resolve(
this
);
};
img.onerror =
function
(err) {
reject(err);
};
img.url = url;
});
};
|
接下来(见证奇迹的时刻),假设有一个BT的需求要这么实现:异步获取一个json配置,解析json数据拿到里边的图片,然后按顺序队列加载图片,没张图片加载时给个loading效果 。
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
|
function
addImg(img) {
$(
'#list'
).find(
'> li:last-child'
).html(
''
).append(img);
};
function
prepend() {
$(
'<li>'
)
.html(
'loading...'
)
.appendTo($(
'#list'
));
};
function
run() {
$(
'#done'
).hide();
getData(
'map.json'
)
.then(
function
(data) {
$(
'h4'
).html(data.name);
return
data.list.reduce(
function
(promise, item) {
return
promise
.then(prepend)
.then(sleep(1000))
.then(
function
() {
return
getImg(item.url);
})
.then(addImg);
}, Promise.resolve());
})
.then(sleep(300))
.then(
function
() {
$(
'#done'
).show();
});
};
$(
'#run'
).on(
'click'
, run);
|
这里的sleep只是为了看效果加的,可猛击查看demo!当然,Node.js的例子可查看这里.
在这里,Promise.resolve(v)静态方法只是简单返回一个以v为肯定结果的promise,v可不传入,也可以是一个函数或者是一个包含then方法的对象或函数(即thenable).
类似的静态方法还有Promise.cast(promise),生成一个以promise为肯定结果的promise; 。
Promise.reject(reason),生成一个以reason为否定结果的promise.
我们实际的使用场景可能很复杂,往往需要多个异步的任务穿插执行,并行或者串行同在。这时候,可以对Promise进行各种扩展,比如实现Promise.all(),接受promises队列并等待他们完成再继续,再比如Promise.any(),promises队列中有任何一个处于完成态时即触发下一步操作.
标准的Promise 。
可参考html5rocks的这篇文章JavaScript Promises,目前高级浏览器如chrome、firefox都已经内置了Promise对象,提供更多的操作接口,比如Promise.all(),支持传入一个promises数组,当所有promises都完成时执行then,还有就是更加友好强大的异常捕获,应对日常的异步编程,应该足够了.
第三方库的Promise 。
现今流行的各大js库,几乎都不同程度的实现了Promise,如dojo,jQuery、Zepto、when.js、Q等,只是暴露出来的大都是Deferred对象,以jQuery(Zepto类似)为例,实现上面的getImg():
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
function
getImg(url) {
var
def = $.Deferred();
var
img =
new
Image();
img.onload =
function
() {
def.resolve(
this
);
};
img.onerror =
function
(err) {
def.reject(err);
};
img.src = url;
return
def.promise();
};
|
当然,jQuery中,很多的操作都返回的是Deferred或promise,如animate、ajax:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// animate
$(
'.box'
)
.animate({
'opacity'
: 0}, 1000)
.promise()
.then(
function
() {
console.log(
'done'
);
});
// ajax
$.ajax(options).then(success, fail);
$.ajax(options).done(success).fail(fail);
// ajax queue
$.when($.ajax(options1), $.ajax(options2))
.then(
function
() {
console.log(
'all done.'
);
},
function
() {
console.error(
'There something wrong.'
);
});
|
jQuery还实现了done()和fail()方法,其实都是then方法的shortcut.
处理promises队列,jQuery实现的是$.when()方法,用法和Promise.all()类似.
其他类库,这里值得一提的是when.js,本身代码不多,完整实现Promise,同时支持browser和Node.js,而且提供更加丰富的API,是个不错的选择。这里限于篇幅,不再展开.
尾声 。
我们看到,不管Promise实现怎么复杂,但是它的用法却很简单,组织的代码很清晰,从此不用再受callback的折磨了.
最后,Promise是如此的优雅!但Promise也只是解决了回调的深层嵌套的问题,真正简化JavaScript异步编程的还是Generator,在Node.js端,建议考虑Generator.
下一篇,研究下Generator.
github原文: https://github.com/chemdemo/chemdemo.github.io/issues/6 。
最后此篇关于JavaScript Promise启示录的文章就讲到这里了,如果你想了解更多关于JavaScript Promise启示录的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我有一个 html 格式的表单: 我需要得到 JavaScript在value input 字段执行,但只能通过表单的 submit .原因是页面是一个模板所以我不控制它(不能有
我管理的论坛是托管软件,因此我无法访问源代码,我只能向页面添加 JavaScript 来实现我需要完成的任务。 我正在尝试用超链接替换所有页面上某些文本关键字的第一个实例。我还根据国家/地区代码对这些
我正在使用 JS 打开新页面并将 HTML 代码写入其中,但是当我尝试使用 document.write() 在新页面中编写 JS 时功能不起作用。显然,一旦看到 ,主 JS 就会关闭。用于即将打开的
提问不是为了解决问题,提问是为了更好地理解系统 专家!我知道每当你将 javascript 代码输入 javascript 引擎时,它会立即由 javascript 引擎执行。由于没有看过Engi
我在一个文件夹中有两个 javascript 文件。我想将一个变量的 javascript 文件传递到另一个。我应该使用什么程序? 最佳答案 window.postMessage用于跨文档消息。使
我有一个练习,我需要输入两个输入并检查它们是否都等于一个。 如果是 console.log 正则 console.log false 我试过这样的事情: function isPositive(fir
我正在做一个Web应用程序,计划允许其他网站(客户端)在其页面上嵌入以下javascript: 我的网络应用程序位于 http://example.org 。 我不能假设客户端网站的页面有 JQue
目前我正在使用三个外部 JS 文件。 我喜欢将所有三个 JS 文件合而为一。 尽一切可能。我创建 aio.js 并在 aio.js 中 src="https://code.jquery.com/
我有例如像这样的数组: var myArray = []; var item1 = { start: '08:00', end: '09:30' } var item2 = {
所以我正在制作一个 Chrome 扩展,它使用我制作的一些 TamperMonkey 脚本。我想要一个“主”javascript 文件,您可以在其中包含并执行其他脚本。我很擅长使用以下行将其他 jav
我有 A、B html 和 A、B javascript 文件。 并且,如何将 A JavaScript 中使用的全局变量直接移动到 B JavaScript 中? 示例 JavaScript) va
我需要将以下整个代码放入名为 activate.js 的 JavaScript 中。你能告诉我怎么做吗? var int = new int({ seconds: 30, mark
我已经为我的 .net Web 应用程序创建了母版页 EXAMPLE1.Master。他们的 I 将值存储在 JavaScript 变量中。我想在另一个 JS 文件中检索该变量。 示例1.大师:-
是否有任何库可以用来转换这样的代码: function () { var a = 1; } 像这样的代码: function () { var a = 1; } 在我的浏览器中。因为我在 Gi
我收到语法缺失 ) 错误 $(document).ready(function changeText() { var p = document.getElementById('bidp
我正在制作进度条。它有一个标签。我想调整某个脚本完成的标签。在找到可能的解决方案的一些答案后,我想出了以下脚本。第一个启动并按预期工作。然而,第二个却没有。它出什么问题了?代码如下: HTML:
这里有一个很简单的问题,我简单的头脑无法回答:为什么我在外部库中加载时,下面的匿名和onload函数没有运行?我错过了一些非常非常基本的东西。 Library.js 只有一行:console.log(
我知道 javascript 是一种客户端语言,但如果实际代码中嵌入的 javascript 代码以某种方式与在控制台上运行的代码不同,我会尝试找到答案。让我用一个例子来解释它: 我想创建一个像 Mi
我如何将这个内联 javascript 更改为 Unobtrusive JavaScript? 谢谢! 感谢您的回答,但它不起作用。我的代码是: PHP js文件 document.getElem
我正在寻找将简单的 JavaScript 对象“转储”到动态生成的 JavaScript 源代码中的最优雅的方法。 目的:假设我们有 node.js 服务器生成 HTML。我们在服务器端有一个对象x。
我是一名优秀的程序员,十分优秀!