Làm cách nào để truy cập kết quả lời hứa trước đó trong chuỗi .then ()?

674
Bergi 2015-02-01 00:41.

Tôi đã cấu trúc lại mã của mình cho các lời hứa và xây dựng một chuỗi lời hứa dài phẳng tuyệt vời , bao gồm nhiều lệnh .then()gọi lại. Cuối cùng, tôi muốn trả về một số giá trị tổng hợp và cần truy cập nhiều kết quả hứa hẹn trung gian . Tuy nhiên, các giá trị độ phân giải từ giữa chuỗi không nằm trong phạm vi trong lần gọi lại cuối cùng, làm cách nào để truy cập chúng?

function getExample() {
    return promiseA(…).then(function(resultA) {
        // Some processing
        return promiseB(…);
    }).then(function(resultB) {
        // More processing
        return // How do I gain access to resultA here?
    });
}

16 answers

391
Bergi 2015-02-01 00:44.

Phá vỡ dây chuyền

Khi bạn cần truy cập các giá trị trung gian trong chuỗi của mình, bạn nên chia chuỗi của mình thành từng phần đơn lẻ mà bạn cần. Thay vì đính kèm một lệnh gọi lại và bằng cách nào đó cố gắng sử dụng tham số của nó nhiều lần, hãy đính kèm nhiều lệnh gọi lại vào cùng một lời hứa - bất cứ nơi nào bạn cần giá trị kết quả. Đừng quên, một lời hứa chỉ đại diện cho (proxy) một giá trị trong tương lai ! Tiếp theo để lấy một lời hứa từ lời hứa kia trong một chuỗi tuyến tính, hãy sử dụng các tổ hợp lời hứa mà thư viện của bạn cung cấp cho bạn để xây dựng giá trị kết quả.

Điều này sẽ dẫn đến một luồng điều khiển rất đơn giản, thành phần rõ ràng của các chức năng và do đó dễ dàng điều chỉnh.

function getExample() {
    var a = promiseA(…);
    var b = a.then(function(resultA) {
        // some processing
        return promiseB(…);
    });
    return Promise.all([a, b]).then(function([resultA, resultB]) {
        // more processing
        return // something using both resultA and resultB
    });
}

Thay vì cấu trúc tham số trong lệnh gọi lại sau Promise.allđó chỉ có sẵn với ES6, trong ES5, thenlệnh gọi sẽ được thay thế bằng một phương thức trợ giúp tiện lợi được cung cấp bởi nhiều thư viện hứa hẹn ( Q , Bluebird , when ,…) : .spread(function(resultA, resultB) { ….

Bluebird cũng có một joinchức năng chuyên dụng để thay thế tổ hợp Promise.all+ đó spreadbằng một cấu trúc đơn giản hơn (và hiệu quả hơn):

…
return Promise.join(a, b, function(resultA, resultB) { … });
247
Bergi 2015-02-01 00:43.

ECMAScript Harmony

Tất nhiên, vấn đề này cũng được các nhà thiết kế ngôn ngữ nhận ra. Họ đã làm rất nhiều việc và cuối cùng đề xuất chức năng không đồng bộ đã biến nó thành

ECMAScript 8

Bạn không cần một lệnh thengọi hoặc hàm gọi lại nào nữa, vì trong một hàm không đồng bộ (trả về một lời hứa khi được gọi), bạn có thể chỉ cần đợi các lời hứa giải quyết trực tiếp. Nó cũng có các cấu trúc điều khiển tùy ý như điều kiện, vòng lặp và mệnh đề try-catch, nhưng để thuận tiện, chúng tôi không cần chúng ở đây:

async function getExample() {
    var resultA = await promiseA(…);
    // some processing
    var resultB = await promiseB(…);
    // more processing
    return // something using both resultA and resultB
}

ECMAScript 6

Trong khi chờ đợi ES8, chúng tôi đã sử dụng một kiểu cú pháp rất giống nhau. ES6 đi kèm với các chức năng của trình tạo , cho phép chia nhỏ việc thực thi thành nhiều phần tại các yieldtừ khóa được đặt tùy ý . Các lát cắt đó có thể được chạy sau nhau, độc lập, thậm chí không đồng bộ - và đó chỉ là những gì chúng ta làm khi muốn đợi giải quyết lời hứa trước khi chạy bước tiếp theo.

Có các thư viện chuyên dụng (như co hoặc task.js ), nhưng cũng có nhiều thư viện hứa hẹn có các hàm trợ giúp ( Q , Bluebird , when ,…) thực hiện việc thực thi từng bước bất đồng bộ này cho bạn khi bạn cung cấp cho chúng một hàm trình tạo mang lại những lời hứa.

var getExample = Promise.coroutine(function* () {
//               ^^^^^^^^^^^^^^^^^ Bluebird syntax
    var resultA = yield promiseA(…);
    // some processing
    var resultB = yield promiseB(…);
    // more processing
    return // something using both resultA and resultB
});

Điều này đã hoạt động trong Node.js kể từ phiên bản 4.0, cũng có một số trình duyệt (hoặc phiên bản dành cho nhà phát triển của chúng) đã hỗ trợ cú pháp trình tạo tương đối sớm.

ECMAScript 5

Tuy nhiên, nếu bạn muốn / cần tương thích ngược, bạn không thể sử dụng chúng mà không có bộ chuyển tiếp. Cả hai hàm bộ tạo và hàm không đồng bộ đều được hỗ trợ bởi công cụ hiện tại, hãy xem ví dụ tài liệu của Babel về bộ tạohàm không đồng bộ .

Và sau đó, cũng có nhiều ngôn ngữ biên dịch sang JS khác được dành riêng để giảm bớt lập trình không đồng bộ. Chúng thường sử dụng cú pháp tương tự như await, (ví dụ: Iced CoffeeScript ), nhưng cũng có những cú pháp khác có chú thích-giống doHaskell (ví dụ như LatteJs , monadic , PureScript hoặc LispyScript ).

103
Esailija 2015-02-01 03:16.

Kiểm tra đồng bộ

Gán các giá trị cần-dùng-cho-sau này cho các biến và sau đó nhận giá trị của chúng thông qua kiểm tra đồng bộ. Ví dụ sử dụng .value()phương pháp của bluebird nhưng nhiều thư viện cung cấp phương pháp tương tự.

function getExample() {
    var a = promiseA(…);

    return a.then(function() {
        // some processing
        return promiseB(…);
    }).then(function(resultB) {
        // a is guaranteed to be fulfilled here so we can just retrieve its
        // value synchronously
        var aValue = a.value();
    });
}

Điều này có thể được sử dụng cho bao nhiêu giá trị tùy thích:

function getExample() {
    var a = promiseA(…);

    var b = a.then(function() {
        return promiseB(…)
    });

    var c = b.then(function() {
        return promiseC(…);
    });

    var d = c.then(function() {
        return promiseD(…);
    });

    return d.then(function() {
        return a.value() + b.value() + c.value() + d.value();
    });
}
58
Bergi 2015-02-01 00:42.

Đóng lồng (và)

Sử dụng các bao đóng để duy trì phạm vi của các biến (trong trường hợp của chúng tôi là tham số hàm gọi lại thành công) là giải pháp JavaScript tự nhiên. Với các lời hứa, chúng ta có thể lồng và làm phẳng các lệnh .then() gọi lại một cách tùy ý - chúng tương đương nhau về mặt ngữ nghĩa, ngoại trừ phạm vi của lệnh bên trong.

function getExample() {
    return promiseA(…).then(function(resultA) {
        // some processing
        return promiseB(…).then(function(resultB) {
            // more processing
            return // something using both resultA and resultB;
        });
    });
}

Tất nhiên, đây là việc xây dựng một kim tự tháp thụt vào. Nếu thụt lề ngày càng lớn, bạn vẫn có thể áp dụng các công cụ cũ để chống lại kim tự tháp của sự diệt vong : modularize, sử dụng các hàm bổ sung được đặt tên và làm phẳng chuỗi hứa hẹn ngay khi bạn không cần biến nữa.
Về lý thuyết, bạn luôn có thể tránh nhiều hơn hai cấp độ lồng nhau (bằng cách đặt tất cả các bao đóng là rõ ràng), trong thực tế, hãy sử dụng càng nhiều càng tốt.

function getExample() {
    // preprocessing
    return promiseA(…).then(makeAhandler(…));
}
function makeAhandler(…)
    return function(resultA) {
        // some processing
        return promiseB(…).then(makeBhandler(resultA, …));
    };
}
function makeBhandler(resultA, …) {
    return function(resultB) {
        // more processing
        return // anything that uses the variables in scope
    };
}

Bạn cũng có thể sử dụng các hàm trợ giúp cho loại ứng dụng từng phần này , chẳng hạn như _.partialtừ Underscore / lodash hoặc phương thức gốc.bind() , để giảm thụt lề hơn nữa:

function getExample() {
    // preprocessing
    return promiseA(…).then(handlerA);
}
function handlerA(resultA) {
    // some processing
    return promiseB(…).then(handlerB.bind(null, resultA));
}
function handlerB(resultA, resultB) {
    // more processing
    return // anything that uses resultA and resultB
}
50
Bergi 2015-02-01 00:42.

Chuyển qua rõ ràng

Tương tự như lồng các lệnh gọi lại, kỹ thuật này dựa vào các bao đóng. Tuy nhiên, chuỗi vẫn ổn định - thay vì chỉ truyền kết quả mới nhất, một số đối tượng trạng thái được chuyển cho mỗi bước. Các đối tượng trạng thái này tích lũy kết quả của các hành động trước đó, phân phối lại tất cả các giá trị sẽ cần sau đó cộng với kết quả của tác vụ hiện tại.

function getExample() {
    return promiseA(…).then(function(resultA) {
        // some processing
        return promiseB(…).then(b => [resultA, b]); // function(b) { return [resultA, b] }
    }).then(function([resultA, resultB]) {
        // more processing
        return // something using both resultA and resultB
    });
}

Ở đây, mũi tên nhỏ đó b => [resultA, b]là hàm đóng lại resultAvà chuyển một mảng của cả hai kết quả cho bước tiếp theo. Trong đó sử dụng cú pháp hủy cấu trúc tham số để chia nhỏ lại trong các biến đơn.

Trước khi cấu trúc hủy có sẵn với ES6, một phương pháp trợ giúp tiện lợi được gọi là .spread()đã được cung cấp bởi nhiều thư viện hứa hẹn ( Q , Bluebird , when ,…). Nó cần một hàm có nhiều tham số - một cho mỗi phần tử mảng - được sử dụng như .spread(function(resultA, resultB) { ….

Tất nhiên, việc đóng đó cần thiết ở đây có thể được đơn giản hóa hơn nữa bằng một số hàm trợ giúp, ví dụ:

function addTo(x) {
    // imagine complex `arguments` fiddling or anything that helps usability
    // but you get the idea with this simple one:
    return res => [x, res];
}

…
return promiseB(…).then(addTo(resultA));

Ngoài ra, bạn có thể sử dụng Promise.allđể tạo ra lời hứa cho mảng:

function getExample() {
    return promiseA(…).then(function(resultA) {
        // some processing
        return Promise.all([resultA, promiseB(…)]); // resultA will implicitly be wrapped
                                                    // as if passed to Promise.resolve()
    }).then(function([resultA, resultB]) {
        // more processing
        return // something using both resultA and resultB
    });
}

Và bạn có thể không chỉ sử dụng mảng mà còn sử dụng các đối tượng phức tạp tùy ý. Ví dụ: với _.extendhoặc Object.assigntrong một chức năng trợ giúp khác:

function augment(obj, name) {
    return function (res) { var r = Object.assign({}, obj); r[name] = res; return r; };
}

function getExample() {
    return promiseA(…).then(function(resultA) {
        // some processing
        return promiseB(…).then(augment({resultA}, "resultB"));
    }).then(function(obj) {
        // more processing
        return // something using both obj.resultA and obj.resultB
    });
}

Mặc dù mô hình này đảm bảo một chuỗi phẳng và các đối tượng trạng thái rõ ràng có thể cải thiện độ rõ ràng, nhưng nó sẽ trở nên tẻ nhạt đối với một chuỗi dài. Đặc biệt là khi bạn chỉ cần trạng thái lẻ tẻ, bạn vẫn phải vượt qua nó qua từng bước. Với giao diện cố định này, các lệnh gọi lại đơn lẻ trong chuỗi được kết hợp khá chặt chẽ và không linh hoạt để thay đổi. Nó làm cho việc bao gồm các bước đơn lẻ khó hơn và các lệnh gọi lại không thể được cung cấp trực tiếp từ các mô-đun khác - chúng luôn cần được bọc trong mã soạn sẵn quan tâm đến trạng thái. Các chức năng trợ giúp trừu tượng như trên có thể làm dịu cơn đau một chút, nhưng nó sẽ luôn hiện diện.

35
Bergi 2015-02-01 00:43.

Trạng thái theo ngữ cảnh có thể thay đổi

Giải pháp nhỏ (nhưng không phù hợp và đúng hơn là sai sót) là chỉ sử dụng các biến phạm vi cao hơn (mà tất cả các lệnh gọi lại trong chuỗi đều có quyền truy cập) và ghi giá trị kết quả vào chúng khi bạn nhận được chúng:

function getExample() {
    var resultA;
    return promiseA(…).then(function(_resultA) {
        resultA = _resultA;
        // some processing
        return promiseB(…);
    }).then(function(resultB) {
        // more processing
        return // something using both resultA and resultB
    });
}

Thay vì nhiều biến, người ta cũng có thể sử dụng một đối tượng (ban đầu trống), trên đó kết quả được lưu trữ dưới dạng các thuộc tính được tạo động.

Giải pháp này có một số nhược điểm:

  • Trạng thái có thể thay đổi là xấucác biến toàn cục là xấu .
  • Mẫu này không hoạt động trên các ranh giới hàm, việc sửa đổi các hàm khó hơn vì khai báo của chúng không được rời khỏi phạm vi được chia sẻ
  • Phạm vi của các biến không ngăn cản việc truy cập chúng trước khi chúng được khởi tạo. Điều này đặc biệt có khả năng xảy ra đối với các cấu trúc hứa hẹn phức tạp (vòng lặp, phân nhánh, loại trừ) trong đó điều kiện chủng tộc có thể xảy ra. Chuyển trạng thái một cách rõ ràng, một thiết kế khai báo hứa hẹn khuyến khích, buộc một phong cách mã hóa sạch hơn có thể ngăn chặn điều này.
  • Người ta phải chọn phạm vi cho các biến được chia sẻ một cách chính xác. Nó cần phải là cục bộ cho hàm được thực thi để ngăn chặn các điều kiện chạy đua giữa nhiều lệnh gọi song song, như sẽ xảy ra nếu, ví dụ, trạng thái được lưu trữ trên một phiên bản.

Thư viện Bluebird khuyến khích sử dụng một đối tượng được truyền cùng, sử dụng phương thức của họbind() để gán một đối tượng ngữ cảnh cho một chuỗi hứa hẹn. Nó sẽ có thể truy cập được từ mỗi chức năng gọi lại thông qua thistừ khóa không sử dụng được . Mặc dù các thuộc tính đối tượng dễ mắc lỗi chính tả không được phát hiện hơn là các biến, nhưng mẫu này khá thông minh:

function getExample() {
    return promiseA(…)
    .bind({}) // Bluebird only!
    .then(function(resultA) {
        this.resultA = resultA;
        // some processing
        return promiseB(…);
    }).then(function(resultB) {
        // more processing
        return // something using both this.resultA and resultB
    }).bind(); // don't forget to unbind the object if you don't want the
               // caller to access it
}

Cách tiếp cận này có thể được mô phỏng dễ dàng trong các thư viện hứa hẹn không hỗ trợ .bind (mặc dù theo cách hơi dài dòng hơn và không thể được sử dụng trong một biểu thức):

function getExample() {
    var ctx = {};
    return promiseA(…)
    .then(function(resultA) {
        this.resultA = resultA;
        // some processing
        return promiseB(…);
    }.bind(ctx)).then(function(resultB) {
        // more processing
        return // something using both this.resultA and resultB
    }.bind(ctx));
}
16
Jay 2017-03-25 10:08.

Một vòng quay ít khắc nghiệt hơn về "Trạng thái theo ngữ cảnh có thể thay đổi"

Sử dụng một đối tượng có phạm vi cục bộ để thu thập các kết quả trung gian trong chuỗi hứa hẹn là một cách tiếp cận hợp lý cho câu hỏi bạn đặt ra. Hãy xem xét đoạn mã sau:

function getExample(){
    //locally scoped
    const results = {};
    return promiseA(paramsA).then(function(resultA){
        results.a = resultA;
        return promiseB(paramsB);
    }).then(function(resultB){
        results.b = resultB;
        return promiseC(paramsC);
    }).then(function(resultC){
        //Resolve with composite of all promises
        return Promise.resolve(results.a + results.b + resultC);
    }).catch(function(error){
        return Promise.reject(error);
    });
}
  • Các biến toàn cục là không tốt, vì vậy giải pháp này sử dụng một biến phạm vi cục bộ mà không gây hại. Nó chỉ có thể truy cập trong chức năng.
  • Trạng thái có thể thay đổi là xấu, nhưng trạng thái này không biến đổi theo cách xấu. Trạng thái có thể thay đổi xấu xí theo truyền thống đề cập đến việc sửa đổi trạng thái của đối số hàm hoặc biến toàn cục, nhưng cách tiếp cận này chỉ đơn giản là sửa đổi trạng thái của một biến phạm vi cục bộ tồn tại với mục đích duy nhất là tổng hợp các kết quả hứa hẹn ... một biến sẽ chết một cách đơn giản một khi lời hứa được giải quyết.
  • Các lời hứa trung gian không bị ngăn truy cập vào trạng thái của đối tượng kết quả, nhưng điều này không giới thiệu một số kịch bản đáng sợ trong đó một trong các lời hứa trong chuỗi sẽ lừa đảo và phá hoại kết quả của bạn. Trách nhiệm thiết lập các giá trị trong mỗi bước của lời hứa được giới hạn trong chức năng này và kết quả tổng thể sẽ đúng hoặc không chính xác ... nó sẽ không có một số lỗi sẽ xuất hiện nhiều năm sau trong quá trình sản xuất (trừ khi bạn có ý định !)
  • Điều này không giới thiệu một kịch bản điều kiện chạy đua sẽ phát sinh từ lệnh gọi song song vì một phiên bản mới của biến kết quả được tạo cho mọi lệnh gọi của hàm getExample.
8
Anthony 2017-01-22 12:14.

Node 7.4 hiện hỗ trợ các cuộc gọi async / await với cờ hòa hợp.

Thử đi:

async function getExample(){

  let response = await returnPromise();

  let response2 = await returnPromise2();

  console.log(response, response2)

}

getExample()

và chạy tệp với:

node --harmony-async-await getExample.js

Đơn giản như có thể được!

8
yzfdjzwl 2017-07-25 20:34.

Dạo này mình cũng gặp một số câu hỏi như bạn. Cuối cùng, tôi tìm thấy một giải pháp tốt với câu hỏi, nó đơn giản và tốt để đọc. Tôi hy vọng điều này có thể giúp bạn.

Theo how-to-chain-javascript-promise

được rồi, hãy xem mã:

const firstPromise = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('first promise is completed');
            resolve({data: '123'});
        }, 2000);
    });
};

const secondPromise = (someStuff) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('second promise is completed');
            resolve({newData: `${someStuff.data} some more data`});
        }, 2000);
    });
};

const thirdPromise = (someStuff) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('third promise is completed');
            resolve({result: someStuff});
        }, 2000);
    });
};

firstPromise()
    .then(secondPromise)
    .then(thirdPromise)
    .then(data => {
        console.log(data);
    });
6
Anthony 2015-11-21 09:59.

Một câu trả lời khác, sử dụng babel-nodephiên bản <6

Sử dụng async - await

npm install -g [email protected]

example.js:

async function getExample(){

  let response = await returnPromise();

  let response2 = await returnPromise2();

  console.log(response, response2)

}

getExample()

Sau đó, chạy babel-node example.jsvà thì đấy!

2
Anthony 2015-08-12 08:35.

Tôi sẽ không sử dụng mẫu này trong mã của riêng mình vì tôi không phải là người thích sử dụng các biến toàn cục. Tuy nhiên, nó sẽ hoạt động.

Người dùng là một mô hình Mongoose được quảng bá.

var globalVar = '';

User.findAsync({}).then(function(users){
  globalVar = users;
}).then(function(){
  console.log(globalVar);
});
2
amaksr 2017-06-10 14:56.

Một câu trả lời khác, sử dụng trình thực thi tuần tự nsynjs :

function getExample(){

  var response1 = returnPromise1().data;

  // promise1 is resolved at this point, '.data' has the result from resolve(result)

  var response2 = returnPromise2().data;

  // promise2 is resolved at this point, '.data' has the result from resolve(result)

  console.log(response, response2);

}

nynjs.run(getExample,{},function(){
    console.log('all done');
})

Cập nhật: thêm ví dụ làm việc

function synchronousCode() {
     var urls=[
         "https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js",
         "https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js",
         "https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"
     ];
     for(var i=0; i<urls.length; i++) {
         var len=window.fetch(urls[i]).data.text().data.length;
         //             ^                   ^
         //             |                   +- 2-nd promise result
         //             |                      assigned to 'data'
         //             |
         //             +-- 1-st promise result assigned to 'data'
         //
         console.log('URL #'+i+' : '+urls[i]+", length: "+len);
     }
}

nsynjs.run(synchronousCode,{},function(){
    console.log('all done');
})
<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>

1
alphakevin 2016-06-12 20:33.

Khi sử dụng bluebird, bạn có thể sử dụng .bindphương thức để chia sẻ các biến trong chuỗi hứa hẹn:

somethingAsync().bind({})
.spread(function (aValue, bValue) {
    this.aValue = aValue;
    this.bValue = bValue;
    return somethingElseAsync(aValue, bValue);
})
.then(function (cValue) {
    return this.aValue + this.bValue + cValue;
});

vui lòng kiểm tra liên kết này để biết thêm thông tin:

http://bluebirdjs.com/docs/api/promise.bind.html

1
Minh Giang 2017-03-03 23:45.
function getExample() {
    var retA, retB;
    return promiseA(…).then(function(resultA) {
        retA = resultA;
        // Some processing
        return promiseB(…);
    }).then(function(resultB) {
        // More processing
        //retA is value of promiseA
        return // How do I gain access to resultA here?
    });
}

một cách dễ dàng: D

1
Vishu 2017-08-30 00:34.

Tôi nghĩ bạn có thể sử dụng hàm băm của RSVP.

Một cái gì đó như dưới đây:

    const mainPromise = () => {
        const promise1 = new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log('first promise is completed');
                resolve({data: '123'});
            }, 2000);
        });

        const promise2 = new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log('second promise is completed');
                resolve({data: '456'});
            }, 2000);
        });

        return new RSVP.hash({
              prom1: promise1,
              prom2: promise2
          });

    };


   mainPromise()
    .then(data => {
        console.log(data.prom1);
        console.log(data.prom2);
    });
0
David Spector 2019-08-28 10:17.

Giải pháp:

Bạn có thể đặt các giá trị trung gian trong phạm vi trong bất kỳ hàm 'then' nào sau này một cách rõ ràng, bằng cách sử dụng 'bind'. Đó là một giải pháp tốt mà không yêu cầu thay đổi cách Promises hoạt động và chỉ yêu cầu một hoặc hai dòng mã để truyền các giá trị giống như lỗi đã được truyền.

Đây là một ví dụ đầy đủ:

// Get info asynchronously from a server
function pGetServerInfo()
    {
    // then value: "server info"
    } // pGetServerInfo

// Write into a file asynchronously
function pWriteFile(path,string)
    {
    // no then value
    } // pWriteFile

// The heart of the solution: Write formatted info into a log file asynchronously,
// using the pGetServerInfo and pWriteFile operations
function pLogInfo(localInfo)
    {
    var scope={localInfo:localInfo}; // Create an explicit scope object
    var thenFunc=p2.bind(scope); // Create a temporary function with this scope
    return (pGetServerInfo().then(thenFunc)); // Do the next 'then' in the chain
    } // pLogInfo

// Scope of this 'then' function is {localInfo:localInfo}
function p2(serverInfo)
    {
    // Do the final 'then' in the chain: Writes "local info, server info"
    return pWriteFile('log',this.localInfo+','+serverInfo);
    } // p2

Giải pháp này có thể được gọi như sau:

pLogInfo("local info").then().catch(err);

(Lưu ý: một phiên bản phức tạp và đầy đủ hơn của giải pháp này đã được thử nghiệm, nhưng không phải phiên bản ví dụ này, vì vậy nó có thể có lỗi.)

Related questions

MORE COOL STUFF

Cate Blanchett chia tay chồng sau 3 ngày bên nhau và vẫn kết hôn với anh ấy 25 năm sau

Cate Blanchett chia tay chồng sau 3 ngày bên nhau và vẫn kết hôn với anh ấy 25 năm sau

Cate Blanchett đã bất chấp những lời khuyên hẹn hò điển hình khi cô gặp chồng mình.

Tại sao Michael Sheen là một diễn viên phi lợi nhuận

Tại sao Michael Sheen là một diễn viên phi lợi nhuận

Michael Sheen là một diễn viên phi lợi nhuận nhưng chính xác thì điều đó có nghĩa là gì?

Hallmark Star Colin Egglesfield Các món ăn gây xúc động mạnh đối với người hâm mộ tại RomaDrama Live! [Loại trừ]

Hallmark Star Colin Egglesfield Các món ăn gây xúc động mạnh đối với người hâm mộ tại RomaDrama Live! [Loại trừ]

Ngôi sao của Hallmark Colin Egglesfield chia sẻ về những cuộc gặp gỡ với người hâm mộ ly kỳ tại RomaDrama Live! cộng với chương trình INSPIRE của anh ấy tại đại hội.

Tại sao bạn không thể phát trực tuyến 'chương trình truyền hình phía Bắc'

Tại sao bạn không thể phát trực tuyến 'chương trình truyền hình phía Bắc'

Bạn sẽ phải phủi sạch đầu đĩa Blu-ray hoặc DVD để xem tại sao Northern Exposure trở thành một trong những chương trình nổi tiếng nhất của thập niên 90.

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

8 công dụng tuyệt vời của Baking Soda và Giấm

8 công dụng tuyệt vời của Baking Soda và Giấm

Bạn biết đấy, hai sản phẩm này là nguồn điện để làm sạch, riêng chúng. Nhưng cùng với nhau, chúng có một loạt công dụng hoàn toàn khác.

Hạn hán, biến đổi khí hậu đe dọa tương lai của thủy điện Hoa Kỳ

Hạn hán, biến đổi khí hậu đe dọa tương lai của thủy điện Hoa Kỳ

Thủy điện rất cần thiết cho lưới điện của Hoa Kỳ, nhưng nó chỉ tạo ra năng lượng khi có nước di chuyển. Bao nhiêu nhà máy thủy điện có thể gặp nguy hiểm khi các hồ và sông cạn kiệt?

Quyên góp tóc của bạn để giúp giữ nước sạch của chúng tôi

Quyên góp tóc của bạn để giúp giữ nước sạch của chúng tôi

Tóc tỉa từ các tiệm và các khoản quyên góp cá nhân có thể được tái sử dụng như những tấm thảm thấm dầu và giúp bảo vệ môi trường.

BoJack Horseman vẫn gan ruột và gan dạ hơn bao giờ hết trong phần 3

BoJack Horseman vẫn gan ruột và gan dạ hơn bao giờ hết trong phần 3

BoJack Horseman (Ảnh: Netflix) BoJack Horseman bắt đầu mùa thứ ba với sự xuất hiện của nhân vật tiêu biểu, người vẫn đang theo đuổi những lời khen ngợi từ giới phê bình trong một nỗ lực tuyệt vọng để tìm ra ý nghĩa trong cuộc sống của mình. Mùa thứ hai về số phận bi kịch của Raphael Bob-Waksberg dày đặc những trò đùa cũng như tuyệt vọng.

Xem cách tính năng lái tự động cập nhật của Tesla bùng phát như thế nào khi bạn bỏ qua các cảnh báo của nó

Xem cách tính năng lái tự động cập nhật của Tesla bùng phát như thế nào khi bạn bỏ qua các cảnh báo của nó

Bản nâng cấp Phiên bản 8.0 gần đây của Tesla đối với hệ thống Lái xe tự động được thiết kế để cải thiện hệ thống và mối quan hệ của nó với người lái.

UnREAL, Chương trình Truyền hình Về Truyền hình Thực tế Chúng tôi Không biết Chúng tôi Cần

UnREAL, Chương trình Truyền hình Về Truyền hình Thực tế Chúng tôi Không biết Chúng tôi Cần

Chắc chắn khi con cái chúng ta xem lại các chương trình chúng ta đã xem vào khoảng năm 2015, chúng sẽ thấy UnREAL, một chương trình truyền hình mới chiếu vào tối Thứ Hai của Lifetime — bạn sẽ thấy đúng lúc, sẽ phát sóng ngay sau khi The Bachelor kết thúc — như một chương trình không thể được thực hiện vào bất kỳ thời điểm nào khác trong lịch sử truyền hình, nhưng một chương trình cảm thấy cần thiết để xem bây giờ. UnREAL có sự tham gia của Shiri Appleby trong vai Rachel, một nhà sản xuất truyền hình cho một chương trình về cơ bản là The Bachelor, mặc dù trong thế giới này, nó được gọi là Vĩnh viễn.

Sau tất cả, Josh Trank sẽ không đạo diễn bộ phim tuyển tập Star Wars thứ hai

Sau tất cả, Josh Trank sẽ không đạo diễn bộ phim tuyển tập Star Wars thứ hai

Thông báo chính thức đến trực tiếp từ trang web của Star Wars: Fantastic Four, đạo diễn Josh Trank, một người bí ẩn không xuất hiện tại đại hội Star Wars Celebration gần đây, sẽ không chỉ đạo phần hai Star Wars Anthology. Đây là tuyên bố được đăng ngày hôm qua: Tất cả thực sự là ngôn ngữ rất lịch sự, nhưng một bài báo của Hollywood Reporter cho thấy tất cả đều không tốt ở hậu trường, gọi sự ra đi là "một vụ sa thải" một phần dựa trên "hành vi bất thường" của Trank trong khi quay Fantastic Four: The Bài báo trích dẫn các nguồn gọi là Trank “đôi khi thiếu quyết đoán và thiếu sáng tạo,” và lưu ý rằng Fantastic Four, khởi chiếu vào ngày 7 tháng 8, đã được khởi động lại vào cuối tháng 4.

Edwin McCain ra mắt Grand Ole Opry: Quay cảnh hậu trường với nhạc sĩ 'I'll Be'

Edwin McCain ra mắt Grand Ole Opry: Quay cảnh hậu trường với nhạc sĩ 'I'll Be'

McCain, người đang làm việc cho một album mới, lần đầu tiên bước vào vòng kết nối vào tối thứ Sáu ở Nashville

Nicky Hilton Forced to Borrow Paris' 'I Love Paris' Sweatshirt After 'Airline Loses All [My] Luggage'

Nicky Hilton Forced to Borrow Paris' 'I Love Paris' Sweatshirt After 'Airline Loses All [My] Luggage'

Nicky Hilton Rothschild's luggage got lost, but luckily she has an incredible closet to shop: Sister Paris Hilton's!

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa. Từ Hollywood đến New York và mọi nơi ở giữa, hãy xem các ngôi sao yêu thích của bạn đang làm gì!

17 tuổi bị đâm chết trong khi 4 người khác bị thương trong một cuộc tấn công bằng dao trên sông Wisconsin

17 tuổi bị đâm chết trong khi 4 người khác bị thương trong một cuộc tấn công bằng dao trên sông Wisconsin

Các nhà điều tra đang xem xét liệu nhóm và nghi phạm có biết nhau trước vụ tấn công hay không

Tôi viết như thế nào

Tôi viết như thế nào

Đối với tôi, mọi thứ là về dòng đầu tiên đó và nó sẽ đưa bạn đến đâu. Một số nhà văn bị điều khiển bởi cốt truyện, sự sắp xếp tinh tế của các quân cờ, trong khi những người khác bị lôi cuốn bởi một nhân vật và khả năng thực hiện một cuộc hành trình với một người bạn hư cấu mới.

Đường băng hạ cánh

Đường băng hạ cánh

Cuối hè đầu thu là mùa hoài niệm. Những chiếc đèn đường chiếu ánh sáng của chúng qua những con đường đẫm mưa, và những chiếc lá dưới chân - màu đỏ cam tắt trong bóng chạng vạng - là lời nhắc nhở về những ngày đã qua.

Hãy tưởng tượng tạo ra một chiến lược nội dung thực sự CHUYỂN ĐỔI. Nó có thể.

Hãy tưởng tượng tạo ra một chiến lược nội dung thực sự CHUYỂN ĐỔI. Nó có thể.

Vào năm 2021, tôi khuyến khích bạn suy nghĩ lại mọi thứ bạn biết về khách hàng mà bạn phục vụ và những câu chuyện bạn kể cho họ. Lùi lại.

Sự mất mát của voi ma mút đã mở ra trái tim tôi để yêu

Sự mất mát của voi ma mút đã mở ra trái tim tôi để yêu

Vào ngày sinh nhật thứ 9 của Felix The Cat, tôi nhớ về một trong những mất mát lớn nhất trong cuộc đời trưởng thành của tôi - Sophie của tôi vào năm 2013. Tôi đã viết bài luận này và chia sẻ nó trên nền tảng này một thời gian ngắn vào năm 2013.

Language