JavaScript Promise

在 Promise 出现之前,如果大家需要实现异步操作,通用的做法是事件加上回调函数,如果我们有多个异步操作需要嵌套执行的话,那么代码将变得非常难于阅读。让我们来看一个具体的代码例子。在下面的例子中,我们首先通过 Http request 调用一个 web service,然后将从 web service 收到的数据写入一个本地文件。从任务的角度来看,这是一个非常简单的任务,但是当你第一次看到这个代码的时候,一定觉得头很晕,因为在这段代码中

  • 对 web service 进行调用的代码和写文件的代码混杂在一起(写文件的代码嵌套在 end 事件的回调函数中),造成代码模块不清晰,阅读和理解起来比较费劲。
  • 对错误的处理分散在代码的各个地方,而且错误处理的实现方式都不一样。对 web service 进行调用的代码,通过监听 error 事件来处理错误,并且将错误输出到控制套;而写文件的代码,是通过回调函数来处理错误,并将错误通过 throw 语句抛出。

var http = require("http");

var fs = require("fs");

var querystring = require("querystring");

var postData = querystring.stringify({

'msg' : 'Hello World!'

});

var options = {

hostname: '127.0.0.1',

port: 8080,

path: '/upload',

method: 'POST',

headers: {

'Content-Type': 'application/x-www-form-urlencoded',

'Content-Length': postData.length

}

};

var req = http.request(options, (res) => {

console.log(`STATUS: ${res.statusCode}`);

console.log(`HEADERS: ${JSON.stringify(res.headers)}`);

res.setEncoding('utf8');

var dataReceived = ""

res.on('data', (chunk) => {

dataReceived = dataReceived + chunk.toString();

console.log(`BODY: ${chunk}`);

});

res.on('end', () => {

console.log('No more data in response.')

//now we try to write the message to a file

fs.writeFile("temp.txt", dataReceived, function(err){

if (err){

throw err;

}

console.log("Write file temp.txt succ");

});

})

});

req.on('error', (e) => {

console.log(`problem with request: ${e.message}`);

});

// write data to request body

req.write(postData);

req.end();

接下来,让我们使用 Promise 重写上面的代码。在重写的代码中,我们可以看到:

  • 对 web service 进行调用的代码和写文件的代码完全分离开了,代码结构变得非常清晰。在阅读代码的过程中,不会再被不相关的代码所干扰。
  • Promise 对象对外提供了统一的回调函数接口(resolve 和 reject 回调函数 ),在重写的代码中,我们可以很容易的把对web service的请求分装到一个模块中,从而对外隐藏Http Request的所有细节。
  • 通过 Promise 对象的封装,对错误代码的处理被统一了,都是通过对 reject 函数调用来说明异步操作过程中有错误发生,而且错误被集中到 .catch 代码段进行了处理(错误都被输出到了控制台)。
  • 通过 Promise 的封装,异步操作的代码变得和同步操作代码很像,更方便其他人理解代码的处理逻辑。

'use strict';

var http = require("http");

var fs = require("fs");

var querystring = require("querystring");

var postData = querystring.stringify({

'msg' : 'Hello World!'

});

var options = {

hostname: '127.0.0.1',

port: 8080,

path: '/upload',

method: 'POST',

headers: {

'Content-Type': 'application/x-www-form-urlencoded',

'Content-Length': postData.length

}

};

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

let req = http.request(options, (res) => {

console.log(`STATUS: ${res.statusCode}`);

console.log(`HEADERS: ${JSON.stringify(res.headers)}`);

res.setEncoding('utf8');

let dataReceived = ""

res.on('data', (chunk) => {

dataReceived = dataReceived + chunk.toString();

});

res.on('end', () => {

resolve(dataReceived);

})

});

req.on('error', function(e){

reject(e);

});

req.write(postData);

req.end();

})

pHttpRequest.then(

//http request promise成功时候的处理

//在http request promise成功的时候,开始处理写文件操作

function(dataReceived){

return new Promise(function(resolve, reject){

fs.writeFile("temp.txt", dataReceived, function(e){

if (e) reject(e);

else resolve("Write file succ");

});

})

}

).then(

//writeFile promise成功时候的处理

function(msg){

console.log(msg);

}

).catch(

//全局错误处理

function(err){

console.log("some error happen(" + err + ")");

}

)

正式基于 Promise 对象有上面所说的这些优点,ES6 正式将 Promise 对象编程了系统的一个内置对象,大家再也不用通过第三方库开始用 Promise 对象了。

Promise 对象的特性

  • Promise 对象一旦创建,就开始执行了,你没有办法取消 Promise 对象的执行。
  • Promise 对象的状态只由异步操作的结果决定,没有任何其他的操作可以改变Promise对象的状态如果异步操作执行成功,Promise 对象将进入 Resolved 状态,同时resolve函数将被调用;如果异步操作执行失败,Promise 对象将进入 Rejected 状态,同时 reject 函数将被调用。
  • Promise对象一旦进入 Resolved 或者 Rejected 状态,状态将不可能再发生变化,在 Promise 对象被销毁之前,将一直保持 Resolved 或者 Rejected 状态。
  • 因为Promise对象的实现方法,在异步操作过程中出现的异常是不会被抛出的,因此需要在 Promise 对象内部进行处理(通过提供 .catch 代码段来实现)。

下图表示了一个 Promise 对象的整个生命周期。

JavaScript Promise

Promise 对象的使用

创建 Promise 对象

在 ES6 中,你可以通过 new Promise(function(resolve, reject){

}) 来创建 Promise 对象,下面是一个具体的代码。

'use strict';

var fs = require("fs");

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

//put some code to call

fs.readFile("temp.txt", (err, data) => {

if (err){

reject(err);

}else{

resolved(data);

}

});

});

关联 resolve function 和 reject function

在上面的例子中,如果你执行这个代码,你会发现没有任何的效果(没有输出),那是因为你没有给这个创建的 Promise 对象关联相应的 resolve 和 reject 函数。下面是一个进一步的例子,这个例子将给 Promise 对象绑定 resolve 和 reject 函数,你就可以看到效果了。在我的测试环境中,因为我们有名为 temp.txt 的文件存在,所以输出了 file read fail

'use strict';

var fs = require("fs");

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

//put some code to call

fs.readFile("temp.txt", (err, data) => {

if (err){

reject(err);

}else{

resolved(data);

}

});

});

promiseObj.then(function(data){

console.log("file read succ");

}, function(err){

console.log("file read fail");

})

在通常情况下,reject 状态的函数我们一般不在 then 中设置,而是在 catch 中设置,这样代码看起来更像是传统意义上的同步代码(和try…catch 比较)。因此上面的例子可以重新写成

'use strict';

var fs = require("fs");

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

//put some code to call

fs.readFile("temp.txt", (err, data) => {

if (err){

reject(err);

}else{

resolved(data);

}

});

});

promiseObj.then(function(data){

console.log("file read succ");

}).catch(function(err){

console.log("file read fail");

});

级联多个 Promise 对象

Promise 对象的 resolve 函数的参数可以是另外一个 Promise 对象,这样就可以将 2 个 Promise 对象级联起来。下面是一个简单的例子

'use strict';

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

setTimeout(() => reject(new Error("something test"), 3000));

});

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

setTimeout(() => resolve(p1), 1000);

});

p2.then(function(data){

console.log("p2 succ");

}).catch(function(err){

console.log("p2 fail");

});

Promise 的特殊函数

Promise.all()

将多个 Promise 包装成一个全新的 Promise Object,如果所有的 Promise 被 Resolved,那么新的 Promise 将被 Resolve;否则新的 Promise 将被 Reject。

Promise.race()

和 .all 一样,.race 将把多个 Promise 包装成一个新的 Promise Object,不同的地方是。这些 Promise 之中任何一个 Resolve 或者 Reject 了,新的 Promise 就被 Resolve 或者 Reject 了。

Promise.resolve()

将传入的对象封装成一个 Promise 对象返回。resolve 方法根据以下的规则返回 Promise 对象。

  • 如果输入的参数本身是一个 Promise 对象,那么 resolve 方法直接返回这个对象;
  • 如果输入的对象本身有 then 方法(必须是一个可以接受2个 function 的方法),那么 resolve 将这个对象转换成 Promise 对象,并理解调用 then 方法;下面是一个例子

//for this example, you will see following output

// then function in thenobject

// Promise object resolve function is called Then function is called

var thenobject = {

then: function(resolve, reject){

console.log("then function in thenobject");

resolve("Then function is called");

}

};

var pObj = Promise.resolve(thenobject);

pObj.then(function(data){

console.log("Promise object resolve function is called " + data);

});

  • 如果传入的参数就是一个普通对象,那么返回的 Promise 对象直接处于 resolved 状态,并且输入的参数将作为 resolved 状态下调用的函数的参数。下面是一个具体的例子。

//for this example, you will see following output

// Promise object resolve function is called Hello World!

var pObj = Promise.resolve("Hello World!");

pObj.then(function(data){

console.log("Promise object resolve function is called " + data);

});

如果没有输入参数,那么返回的Promise对象直接处于 resolved 状态,并且 resolved 状态下调用的函数没有输入参数。

done() method

通过在Promise的调用链最后使用这个方法,可以保证catch到任何的错误。

finally() method

如果你需要在 Promise 结束的时候(不管 resolve 还是 reject 结束),都有一个函数被调用,那么就需要使用这个方法。这个方法接受一个回调函数作为输入。当 Promise 结束的时候,这个回调函数将被调用。

以上是 JavaScript Promise 的全部内容, 来源链接: utcz.com/z/264514.html

回到顶部