js之promise讲解

1 Promise概述

Promise对象是CommonJS工作组提出的一种规范,目的是为异步操作提供统一接口

那么,什么是Promises?

首先,它是一个对象,也就是说与其他JavaScript对象的用法,没有什么两样;其次,它起到代理作用(proxy),充当异步操作与回调函数之间的中介。它使得异步操作具备同步操作的接口,使得程序具备正常的同步运行的流程,回调函数不必再一层层嵌套。

简单说,它的思想是,每一个异步任务立刻返回一个Promise对象,由于是立刻返回,所以可以采用同步操作的流程。这个Promises对象有一个then方法,允许指定回调函数,在异步任务完成后调用。

比如,异步操作f1返回一个Promise对象,它的回调函数f2写法如下。

(new Promise(f1)).then(f2);

这种写法对于多层嵌套的回调函数尤其方便。

// 传统写法

step1(function (value1) {

step2(value1, function(value2){

step3(value2,function(value3) {

step4(value3,function(value4) {

// ...

});

});

});

});

// Promises的写法

(new Promise(step1))

.then(step2)

.then(step3)

.then(step4);

从上面代码可以看到,采用Promises接口以后,程序流程变得非常清楚,十分易读。

注意,为了便于理解,上面代码的Promise对象的生成格式,做了简化,真正的语法请参照下文。

总的来说,传统的回调函数写法使得代码混成一团,变得横向发展而不是向下发展。Promises规范就是为了解决这个问题而提出的,目标是使用正常的程序流程(同步),来处理异步操作。它先返回一个Promise对象,后面的操作以同步的方式,寄存在这个对象上面。等到异步操作有了结果,再执行前期寄放在它上面的其他操作。

Promises原本只是社区提出的一个构想,一些外部函数库率先实现了这个功能。ECMAScript 6将其写入语言标准,因此目前javascript语言原生支持Promise对象。

前面说过,Promise接口的基本思想是,异步任务返回一个Promise对象。

Promise对象只有三种状态。

?   异步操作“未完成”(pending)

?   异步操作“已完成”(resolved,又称fulfilled)

?   异步操作“失败”(rejected)

这三种的状态的变化途径只有两种。

?   异步操作从“未完成”到“已完成”

?   异步操作从“未完成”到“失败”。

这种变化只能发生一次,一旦当前状态变为“已完成”或“失败”,就意味着不会再有新的状态变化了。因此,Promise对象的最终结果只有两种。

?   异步操作成功,Promise对象传回一个值,状态变为resolved。

?   异步操作失败,Promise对象抛出一个错误,状态变为rejected。

Promise对象使用then方法添加回调函数。then方法可以接受两个回调函数,第一个是异步操作成功时(变为resolved状态)时的回调函数,第二个是异步操作失败(变为rejected)时的回调函数(可以省略)。一旦状态改变,就调用相应的回调函数。

// po是一个Promise对象

po.then(

console.log,

console.error

);

上面代码中,Promise对象po使用then方法绑定两个回调函数:操作成功时的回调函数console.log,操作失败时的回调函数console.error(可以省略)。这两个函数都接受异步操作传回的值作为参数。

then方法可以链式使用。

po

.then(step1)

.then(step2)

.then(step3)

.then(

console.log,

console.error

);

上面代码中,po的状态一旦变为resolved,就依次调用后面每一个then指定的回调函数,每一步都必须等到前一步完成,才会执行。最后一个then方法的回调函数console.log和console.error,用法上有一点重要的区别。console.log只显示回调函数step3的返回值,而console.error可以显示step1、step2、step3之中任意一个发生的错误。也就是说,假定step1操作失败,抛出一个错误,这时step2和step3都不会再执行了(因为它们是操作成功的回调函数,而不是操作失败的回调函数)。Promises对象开始寻找,接下来第一个操作失败时的回调函数,在上面代码中是console.error。这就是说,Promises对象的错误有传递性。

从同步的角度看,上面的代码大致等同于下面的形式。

try {

var v1 = step1(po);

var v2 = step2(v1);

var v3 = step3(v2);

console.log(v3);

} catch (error) {

console.error(error);

}

ES6提供了原生的Promise构造函数,用来生成Promise实例。

下面代码创造了一个Promise实例。

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

// 异步操作的代码

if (/* 异步操作成功 */){

resolve(value);

} else {

reject(error);

}

});

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由JavaScript引擎提供,不用自己部署。

resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从Pending变为Resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从Pending变为Rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise实例生成以后,可以用then方法分别指定Resolved状态和Reject状态的回调函数。

po.then(function(value) {

// success

}, function(value) {

// failure

});

Promise的用法,简单说就是一句话:使用then方法添加回调函数。但是,不同的写法有一些细微的差别,请看下面四种写法,它们的差别在哪里?

// 写法一

doSomething().then(function () {

return doSomethingElse();

});

// 写法二

doSomething().then(function () {

doSomethingElse();

});

// 写法三

doSomething().then(doSomethingElse());

// 写法四

doSomething().then(doSomethingElse);

为了便于讲解,下面这四种写法都再用then方法接一个回调函数finalHandler。写法一的finalHandler回调函数的参数,是doSomethingElse函数的运行结果。

doSomething().then(function () {

return doSomethingElse();

}).then(finalHandler);

写法二的finalHandler回调函数的参数是undefined。

doSomething().then(function () {

doSomethingElse();

return;

}).then(finalHandler);

写法三的finalHandler回调函数的参数,是doSomethingElse函数返回的回调函数的运行结果。

doSomething().then(doSomethingElse())

.then(finalHandler);

写法四与写法一只有一个差别,那就是doSomethingElse会接收到doSomething()返回的结果。

doSomething().then(doSomethingElse)

.then(finalHandler);

2 promise.then

所有的 promise 对象实例里都有一个then 方法,它是用来跟这个 promise 进行交互的。首先,then 方法会缺省调用 resolve() 函数:

new Promise(function(resolve, reject) {

// Amock async action using setTimeout

setTimeout(function(){ resolve(10); }, 3000);

})

.then(function(result) {

console.log(result);

});

// From the console:

// 10

then 回调动作的触发时机是 promise 被执行完。我们还可以串联 then 方法执行回调操作:

new Promise(function(resolve, reject) {

// Amock async action using setTimeout

setTimeout(function(){ resolve(10); }, 3000);

})

.then(function(num) { console.log(‘first then:‘, num); return num * 2; })

.then(function(num) { console.log(‘second then:‘, num); return num * 2; })

.then(function(num) { console.log(‘last then: ‘,num);});

// From the console:

// first then: 10

// second then: 20

// lastthen:  40

你会发现,每次 then 调用都会以之前的 then 调用的返回值为参数。

如果一个 promise 已经执行完成,单then 被再次调用时,回调动作将会被再次执行。而如果这个 promise 里执行的是reject 回调函数,这是再调用 then 方法,回调函数将不会被执行。

3 promise.all

在我们的异步调用时经常有这样一种场景:我们需要同时调用多个异步操作,但希望只有等所有的操作都完成后,我们才去执行响应操作——这就是Promise.all 的作用。 Promise.all 方法可以接收多个 promise 作为参数,以数组的形式,当这些 promise 都成功执行完成后才调用回调函数。

Promise.all([promise1, promise2]).then(function(results){

//Both promises resolved

})

.catch(function(error) {

//One or more promises was rejected

});

一个很好的能演示 Promise.all 用法的例子是,执行多个 AJAX 操作(通过 fetch) 调用:

var request1 = fetch(‘/users.json‘);

var request2 = fetch(‘/articles.json‘);

Promise.all([request1, request2]).then(function(results) {

// Both promises done!

});

一旦promise 里调用了reject函数,也就是执行被拒绝了,没有能够正常完成,情况会有些复杂。一旦promise 被拒绝,catch 方法会捕捉到首个被执行的reject函数:

var req1 = new Promise(function(resolve, reject){

// Amock async action using setTimeout

setTimeout(function(){ resolve(‘First!‘); }, 4000);

});

var req2 = new Promise(function(resolve, reject){

// Amock async action using setTimeout

setTimeout(function(){ reject(‘Second!‘); }, 3000);

});

Promise.all([req1, req2]).then(function(results){

console.log(‘Then:‘, one);

}).catch(function(err) {

console.log(‘Catch:‘, err);

});

// From the console:

// Catch:Second!

4 promise.catch

catch 当一个 promise 被拒绝(reject)时,catch 方法会被执行:

new Promise(function(resolve,reject) {

// A mock async action using setTimeout

setTimeout(function() { reject(‘Done!‘); },3000);

})

.then(function(e){ console.log(‘done‘, e); })

.catch(function(e){ console.log(‘catch: ‘, e); });

// From theconsole:

// ‘catch:Done!‘

通常我们在 reject 方法里处理执行失败的结果,而在catch 里执行异常结果:

reject(Error(‘Data could not be found‘));

5 promise.race

Promise.race 是一个有趣的函数——它不是等待所有的 promise 被resolve 或 reject,而是在所有的 promise 中只要有一个执行结束,它就会触发:

var req1 = newPromise(function(resolve, reject) {

// A mock async action using setTimeout

setTimeout(function() { resolve(‘First!‘); },8000);

});

var req2 = newPromise(function(resolve, reject) {

// A mock async action using setTimeout

setTimeout(function() { resolve(‘Second!‘);}, 3000);

});

Promise.race([req1,req2]).then(function(one) {

console.log(‘Then: ‘, one);

}).catch(function(one,two) {

console.log(‘Catch: ‘, one);

});

// From theconsole:

// Then:Second!

一个有用的场景是,从多个镜像服务器下载资源,一旦有一个返回,其它的返回也就不用处理了。

6 promise实际应用

一个 XMLHttpRequest 调用转换为基于 Promises 的任务:

// From JakeArchibald‘s Promises and Back:

//http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest

function get(url){

// Return a new promise.

return new Promise(function(resolve, reject) {

// Do the usual XHR stuff

var req = new XMLHttpRequest();

req.open(‘GET‘, url);

req.onload = function() {

// This is called even on 404 etc

// so check the status

if (req.status == 200) {

// Resolve the promise with theresponse text

resolve(req.response);

}

else {

// Otherwise reject with the statustext

// which will hopefully be a meaningfulerror

reject(Error(req.statusText));

}

};

// Handle network errors

req.onerror = function() {

reject(Error("Network Error"));

};

// Make the request

req.send();

});

}

// Use it!

get(‘story.json‘).then(function(response){

console.log("Success!", response);

}, function(error){

console.error("Failed!", error);

});

Promise.resolve() 和 Promise.reject() 可以直接被调用。有时候,当判断出 promise 并不需要真正执行时,我们并不需要 使用 new 创建 Promise 对象,而是可以直接调用 Promise.resolve() 和 Promise.reject()。比如:

var userCache = {};

function getUserDetail(username) {

// In both cases, cached or not, apromise will be returned

if (userCache[username]) {

//Return a promise without the "new" keyword

return Promise.resolve(userCache[username]);

}

// Use the fetch API to get theinformation

// fetch returns a promise

return fetch(‘users/‘ + username + ‘.json‘)

.then(function(result) {

userCache[username] = result;

return result;

})

.catch(function() {

throw new Error(‘Could not finduser: ‘ + username);

});

}

因为 promise 肯定会返回,所以,我们可以使用then 和 catch 方法处理返回值!

加载图片

var preloadImage = function (path) {

return new Promise(function(resolve, reject) {

var image = newImage();

image.onload  = resolve;

image.onerror= reject;

image.src = path;

});

};

时间: 2024-10-12 17:02:26

js之promise讲解的相关文章

HTML5游戏开发引擎Pixi.js新手入门讲解

在线演示 本地下载 ?这篇文章中,介绍HTML5游戏引擎pixi.js的基本使用. 相关代码如下: Javascript 导入类库:(使用极客的cdn服务:http://cdn.gbtags.com) <scripttype="text/javascript"src="http://cdn.gbtags.com/pixi.js/1.6.1/pixi.js"></script> 引擎使用: .... .... 阅读原文:HTML5游戏开发引擎P

大数据技术之_18_大数据离线平台_02_Nginx+Mysql+数据收集+Web 工程 JS/JAVA SDK 讲解+Flume 故障后-如何手动上传 Nginx 日志文件至 HDFS 上

十一.Nginx11.1.介绍11.2.常见其他 Web 服务器11.3.版本11.4.Nginx 安装11.5.目录结构11.6.操作命令十二.Mysql12.1.介绍12.2.关系型数据库(SQL)种类12.3.特征12.4.术语12.4.与非关系型数据库比较(Not Only SQL)12.4.1.种类12.4.2.特征12.4.3.总结十三.数据收集13.1.收集方式13.2.数据的事件类型13.2.1.Launch 事件13.2.2.PageView 事件13.3.Nginx 日志收集

Angular JS中 Promise用法

一.Promise形象讲解A promise不是angular首创的,作为一种编程模式,它出现在1976年,比js还要古老得多.promise全称是 Futures and promises. 而在javascript世界中,一个广泛流行的库叫做Q 地址是https://github.com/kriskowal/q 而angular中的$q就是从它引入的.promise解决的是异步编程的问题,对于生活在同步编程世界中的程序员来说,它可能比较难于理解,这也构成了angular入门门槛之一,以下将用

node.js的Promise库-bluebird示例

前两天公司一哥们写了一段node.js代码发给我,后面特意提了一句“写的不太优雅”.我知道,他意思是回调嵌套回调,因为当时比较急也就没有再纠结.然而内心中总记得要解决这个问题.解决node.js的回调金字塔问题有较多方法,在<深入浅出node.js>这本书中介绍了好几种,有事件发布/订阅模式.Promise模式.async库等.其中Promise模式被很多人推崇,实现的库有很多,本着从众的原则,闭着眼睛选个bluebird吧. 然而bluebird的文档并不咋滴,相当不咋滴!网上的例子基本上都

Node.js之Promise维护(同步)多个回调(异步)状态

金天:学习一个新东西,就要持有拥抱的心态,如果固守在自己先前的概念体系,就会有举步维艰的感觉..NET程序员初用node.js最需要适应的就是异步开发, 全是异步,常规逻辑下遍历列表都是异步,如何保证列表遍历执行完毕?Promise帮你搞定!金天微信:15998603918 欢迎找我聊聊天. Node.js编程,清一色的回调. 如果没有Promise, 一连串的业务逻辑,从第一步回调到最后一步,“单线程逻辑”还搞的定,遭遇“多线程逻辑”,便陷入回调地狱. 自从有了Promise, 一切都不在是问

JS面向对象基础讲解(工厂模式、构造函数模式、原型模式、混合模式、动态原型模)

什么是面向对象?面向对象是一种思想!(废话). 面向对象可以把程序中的关键模块都视为对象,而模块拥有属性及方法.这样我们如果把一些属性及方法封装起来,日后使用将非常方便,也可以避免繁琐重复的工作.接下来将为大家讲解在JS中面向对象的实现.   工厂模式 工厂模式是软件工程领域一种广为人知的设计模式,而由于在ECMAScript中无法创建类,因此用函数封装以特定接口创建对象.其实现方法非常简单,也就是在函数内创建一个对象,给对象赋予属性及方法再将对象返回即可. ? 1 2 3 4 5 6 7 8

在Node.js使用Promise的方式操作Mysql

最近在学习Node.js,虽然早就听说了回调地狱结果过了一周就遇到了.所以花时间学习了了一下Promise.虽然还有Async/await.co.生成器等选择,但是因为本人基础较差,以及时间问题所以决定先用好Promise. 你可以选择用原生的,当然最好还是用BlueBird,听说性能比官方的好很多,而且有额外的特性:promisifyAll.Promisify 官方案例: var fs = Promise.promisifyAll(require("fs")); fs.readFil

原生JS实现Promise

ES6中Promise可以说很大情况下改善了异步回调的嵌套问题,那么如果我们自己去写一个类似Promise的库应该怎么去写? 我们先看一下Promise的特点: 第一:Promise构造函数接受一个函数作为参数,函数里面有两个参数resolve和reject分别作为执行成功或者执行失败的函数 var promise=new Promsie(function(resolve,rejec){ if(/*异步执行成功*/){ resolve(value); }else{ reject(error);

callback vs async.js vs promise vs async / await

需求: A.依次读取 A|B|C 三个文件,如果有失败,则立即终止. B.同时读取 A|B|C 三个文件,如果有失败,则立即终止. 一.callback 需求A: let read = function (code) { if (code) { return true; } else { return false; } } let readFileA = function (callback) { if (read(1)) { return callback(null, "111");