我已经开发JavaScript好几年了,我完全不理解关于承诺的大惊小怪。

似乎我所做的就是改变:

api(function(result){
    api2(function(result2){
        api3(function(result3){
             // do work
        });
    });
});

无论如何,我可以使用像async这样的库,比如:

api().then(function(result){
     api2().then(function(result2){
          api3().then(function(result3){
               // do work
          });
     });
});

代码更多,可读性更差。我在这里没有获得任何东西,它也没有突然神奇地“变平”。更不用说把事情变成承诺了。

那么,承诺有什么好大惊小怪的呢?


当前回答

是的,promise是异步回调。它们不能做回调所不能做的任何事情,并且在异步中您面临与普通回调相同的问题。

然而,承诺不仅仅是回调。它们是一种非常强大的抽象,允许更清晰、更好的功能代码和更少容易出错的样板文件。

那么主要思想是什么呢?

承诺是表示单个(异步)计算结果的对象。他们只决心达到那个结果一次。这意味着几件事:

promise实现了一个观察者模式:

You don't need to know the callbacks that will use the value before the task completes. Instead of expecting callbacks as arguments to your functions, you can easily return a Promise object The promise will store the value, and you can transparently add a callback whenever you want. It will be called when the result is available. "Transparency" implies that when you have a promise and add a callback to it, it doesn't make a difference to your code whether the result has arrived yet - the API and contracts are the same, simplifying caching/memoisation a lot. You can add multiple callbacks easily

承诺是可链的(如果你愿意,可以是单链的):

If you need to transform the value that a promise represents, you map a transform function over the promise and get back a new promise that represents the transformed result. You cannot synchronously get the value to use it somehow, but you can easily lift the transformation in the promise context. No boilerplate callbacks. If you want to chain two asynchronous tasks, you can use the .then() method. It will take a callback to be called with the first result, and returns a promise for the result of the promise that the callback returns.

听起来复杂吗?接下来是代码示例。

var p1 = api1(); // returning a promise
var p3 = p1.then(function(api1Result) {
    var p2 = api2(); // returning a promise
    return p2; // The result of p2 …
}); // … becomes the result of p3

// So it does not make a difference whether you write
api1().then(function(api1Result) {
    return api2().then(console.log)
})
// or the flattened version
api1().then(function(api1Result) {
    return api2();
}).then(console.log)

扁平化不是神奇的,但你可以很容易地做到。对于嵌套较多的示例,(接近)等价的是

api1().then(api2).then(api3).then(/* do-work-callback */);

如果看到这些方法的代码有助于理解,下面是几个最基本的promise库。

承诺有什么好大惊小怪的?

Promise抽象允许更好的函数组合。例如,在链接之后,all函数为多个并行等待的承诺的组合结果创建一个承诺。

最后但并非最不重要的承诺带有集成的错误处理。计算的结果可能是承诺用一个值来实现,或者用一个理由来拒绝。所有的复合函数都自动处理这个问题,并在承诺链中传播错误,因此您不需要在任何地方显式地关心它——与普通回调实现相反。最后,您可以为所有发生的异常添加一个专用的错误回调。

更不用说把事情变成承诺了。

这是相当微不足道的,实际上与良好的承诺库,看看我如何转换现有的回调API承诺?

其他回答

除了其他答案,ES2015语法与承诺无缝融合,减少了更多的样板代码:

// Sequentially:
api1()
  .then(r1 => api2(r1))
  .then(r2 => api3(r2))
  .then(r3 => {
      // Done
  });

// Parallel:
Promise.all([
    api1(),
    api2(),
    api3()
]).then(([r1, r2, r3]) => {
    // Done
});

承诺概述:

在JS中,我们可以将异步操作(例如数据库调用,AJAX调用)包装在承诺中。通常,我们希望在检索到的数据上运行一些额外的逻辑。JS承诺有处理异步操作结果的处理程序函数。处理程序函数甚至可以在其中包含其他异步操作,这些操作可能依赖于前面的异步操作的值。

一个承诺总是有以下三种状态:

待定:每个承诺的起始状态,既未完成也未拒绝。 完成的:操作成功完成。 rejected:操作失败。

挂起的承诺可以用值来解决/实现或拒绝。然后调用以下将回调作为参数的处理程序方法:

promise .prototype.then():当promise被解析时,该函数的回调参数将被调用。 promise .prototype.catch():当promise被拒绝时,这个函数的回调参数将被调用。

虽然上面的方法技能获得回调参数,但它们远比使用要好 这里只有回调的例子可以说明很多问题:

例子

function createProm(resolveVal, rejectVal) { return new Promise((resolve, reject) => { setTimeout(() => { if (Math.random() > 0.5) { console.log("Resolved"); resolve(resolveVal); } else { console.log("Rejected"); reject(rejectVal); } }, 1000); }); } createProm(1, 2) .then((resVal) => { console.log(resVal); return resVal + 1; }) .then((resVal) => { console.log(resVal); return resVal + 2; }) .catch((rejectVal) => { console.log(rejectVal); return rejectVal + 1; }) .then((resVal) => { console.log(resVal); }) .finally(() => { console.log("Promise done"); });

The createProm function creates a promises which is resolved or rejected based on a random Nr after 1 second If the promise is resolved the first then method is called and the resolved value is passed in as an argument of the callback If the promise is rejected the first catch method is called and the rejected value is passed in as an argument The catch and then methods return promises that's why we can chain them. They wrap any returned value in Promise.resolve and any thrown value (using the throw keyword) in Promise.reject. So any value returned is transformed into a promise and on this promise we can again call a handler function. Promise chains give us more fine tuned control and better overview than nested callbacks. For example the catch method handles all the errors which have occurred before the catch handler.

没有承诺只是回调的包装

例子 你可以在node js中使用javascript原生承诺

my cloud 9 code link : https://ide.c9.io/adx2803/native-promises-in-node

/**
* Created by dixit-lab on 20/6/16.
*/

var express = require('express');
var request = require('request');   //Simplified HTTP request client.


var app = express();

function promisify(url) {
    return new Promise(function (resolve, reject) {
    request.get(url, function (error, response, body) {
    if (!error && response.statusCode == 200) {
        resolve(body);
    }
    else {
        reject(error);
    }
    })
    });
}

//get all the albums of a user who have posted post 100
app.get('/listAlbums', function (req, res) {
//get the post with post id 100
promisify('http://jsonplaceholder.typicode.com/posts/100').then(function (result) {
var obj = JSON.parse(result);
return promisify('http://jsonplaceholder.typicode.com/users/' + obj.userId + '/albums')
})
.catch(function (e) {
    console.log(e);
})
.then(function (result) {
    res.end(result);
}
)

})


var server = app.listen(8081, function () {

var host = server.address().address
var port = server.address().port

console.log("Example app listening at http://%s:%s", host, port)

})


//run webservice on browser : http://localhost:8081/listAlbums

Promises允许程序员编写比使用回调更简单、更易读的代码。


在一个程序中,有一系列的步骤需要执行。

function f() {
   step_a();
   step_b();
   step_c();
   ...
}

每一步之间通常都有信息传递。

function f() {
   const a = step_a(   );
   const b = step_b( a );
   const c = step_c( b );
   ...
}

其中一些步骤可能会花费(相对)较长的时间,所以有时您希望与其他事情并行执行这些步骤。一种方法是使用线程。另一个是异步编程。(这两种方法各有利弊,这里不再讨论。)这里,我们讨论的是异步编程。

在使用异步编程时,实现上述目标的简单方法是提供一个回调函数,在步骤完成时调用它。

// step_* calls the provided function with the returned value once complete.
function f() {
   step_a(
      function( a )
         step_b(
            function( b )
               step_c(
                  ...
               )
            },
         )
      },
   )
}

这很难读懂。承诺提供了一种简化代码的方法。

// step_* returns a promise.
function f() {
   step_a()
   .then( step_b )
   .then( step_c )
   ...
}

返回的对象被称为promise,因为它代表了函数的未来结果(即承诺的结果)(可以是值或异常)。

尽管承诺很有帮助,但使用承诺还是有点复杂。这就是async和await的用武之地。在声明为async的函数中,可以使用await来代替then。

// step_* returns a promise.
async function f()
   const a = await step_a(   );
   const b = await step_b( a );
   const c = await step_c( b );
   ...
}

不可否认,这比使用回调可读性强得多。

JavaScript的Promise实际上使用回调函数来确定Promise被解析或拒绝后要做什么,因此两者并没有本质上的不同。Promises背后的主要思想是采用回调——特别是在您想要执行某种操作的地方嵌套回调,但它将更易于阅读。