คำขอซิงโครนัสใน Node.js


99

หากฉันต้องการเรียกใช้ 3 http API ตามลำดับจะมีทางเลือกอื่นที่ดีกว่าสำหรับโค้ดต่อไปนี้:

http.get({ host: 'www.example.com', path: '/api_1.php' }, function(res) { 
  res.on('data', function(d) { 

    http.get({ host: 'www.example.com', path: '/api_2.php' }, function(res) { 
      res.on('data', function(d) { 

        http.get({ host: 'www.example.com', path: '/api_3.php' }, function(res) { 
          res.on('data', function(d) { 


          });
        });
        }
      });
    });
    }
  });
});
}

นอกจากการทำความสะอาดแล้วฉันไม่คิดว่าคุณจะทำได้ดีไปกว่านั้น
hvgotcodes

2
ทำไมต้องเรียงตามลำดับ?
Raynos

11
@Raynos คุณอาจต้องการข้อมูลบางอย่างจาก api_1 ก่อนที่คุณจะรู้ว่าจะส่งอะไรไปยัง api_2
andyortlieb

9
เป็นเรื่องที่ควรค่าแก่การกล่าวถึงว่า Futures นั้นเลิกใช้ไปแล้วลองใช้ไลบรารีที่ใหม่กว่าเช่น Bluebird หรือ Q.
Benjamin Gruenbaum

1
ชื่อและคำถามขัดแย้งกัน คุณไม่ได้อธิบายคำขอซิงโครนัสในคำถามของคุณ แต่เป็นลำดับของคำขอซึ่งโดยปกติแต่ละคำขอจะเกิดขึ้นแบบอะซิงโครนัส ความแตกต่างใหญ่ - บล็อกการโทรแบบซิงโครนัสและลำดับของการดำเนินการแบบอะซิงโครนัสไม่ได้บล็อก (บล็อก UI บล็อกเซิร์ฟเวอร์จากการจัดการคำขออื่น ๆ ) มีคำตอบด้านล่างกล่าวถึงsync-requestห้องสมุดซึ่งเป็นคำตอบที่ดีสำหรับชื่อคำถามนี้ แต่ไม่ใช่คำตอบสำหรับความหมายของรหัสของคำถาม คำตอบด้านล่างเกี่ยวกับสัญญาเป็นคำตอบที่ดีกว่าสำหรับสิ่งนั้น คุณหมายถึงอะไร
Jake

คำตอบ:


69

ใช้รอการตัดบัญชีเช่นFutures.

var sequence = Futures.sequence();

sequence
  .then(function(next) {
     http.get({}, next);
  })
  .then(function(next, res) {
     res.on("data", next);
  })
  .then(function(next, d) {
     http.get({}, next);
  })
  .then(function(next, res) {
    ...
  })

หากคุณต้องการที่จะผ่านขอบเขตไปให้ทำสิ่งนี้

  .then(function(next, d) {
    http.get({}, function(res) {
      next(res, d);
    });
  })
  .then(function(next, res, d) { })
    ...
  })

โปรดลองใช้ IcedCoffeScript ซึ่งให้การรอและเลื่อนสำหรับ nodejs
Thanigainathan

นี่คือการไม่ปิดกั้น? ฉันหมายความว่ามันกำลังปิดกั้นสำหรับฟังก์ชันถัดไปในบรรทัด แต่จะไม่บล็อกการทำงานของฟังก์ชัน async อื่น ๆ ใช่หรือไม่
Oktav

1
ใช่วิธีการรอการตัดบัญชีไม่ใช่การปิดกั้น / async
dvlsg

4
ES6 Promise API ควรแทนที่สิ่งนี้ได้อย่างมีประสิทธิภาพแม้จะเป็นไปตามผู้เขียน "Futures"
Alexander Mills

ฟิวเจอร์สเก่ามากและเลิกใช้งานแล้ว ดู q แทน
Jim Aho

53

ฉันชอบโซลูชันของ Raynos เช่นกัน แต่ฉันชอบไลบรารีควบคุมการไหลอื่น

https://github.com/caolan/async

ขึ้นอยู่กับว่าคุณต้องการผลลัพธ์ในแต่ละฟังก์ชันที่ตามมาฉันจะใช้อนุกรมขนานหรือน้ำตก

ชุดเมื่อต้องดำเนินการตามลำดับ แต่คุณไม่จำเป็นต้องได้ผลลัพธ์ในการเรียกใช้ฟังก์ชันที่ตามมาแต่ละครั้ง

ขนานหากสามารถเรียกใช้งานแบบขนานได้คุณไม่ต้องการผลลัพธ์จากแต่ละฟังก์ชันระหว่างฟังก์ชันขนานแต่ละฟังก์ชันและคุณต้องมีการเรียกกลับเมื่อทุกอย่างเสร็จสิ้น

น้ำตกหากคุณต้องการปรับเปลี่ยนผลลัพธ์ในแต่ละฟังก์ชันและส่งต่อไปยังฟังก์ชันถัดไป

endpoints = 
 [{ host: 'www.example.com', path: '/api_1.php' },
  { host: 'www.example.com', path: '/api_2.php' },
  { host: 'www.example.com', path: '/api_3.php' }];

async.mapSeries(endpoints, http.get, function(results){
    // Array of results
});

9
var http = ต้องใช้ ('http');
Elle Mundy

7
ฮะ. example.com เป็นโดเมนที่ออกแบบมาเพื่อสิ่งนี้จริงๆ ว้าว.
meawoppl

รหัส async.series ใช้ไม่ได้อย่างน้อยก็เท่ากับ async v0.2.10 series () ใช้เวลาไม่เกินสองอาร์กิวเมนต์และจะเรียกใช้องค์ประกอบของอาร์กิวเมนต์แรกเป็นฟังก์ชันดังนั้น async จึงแสดงข้อผิดพลาดที่พยายามเรียกใช้อ็อบเจ็กต์เป็นฟังก์ชัน
ฝา

1
คุณสามารถทำสิ่งที่คล้ายกับสิ่งที่ตั้งใจไว้กับรหัสนี้โดยใช้ forEachAsync ( github.com/FuturesJS/forEachAsync )
ฝา

นี่เป็นสิ่งที่ฉันต้องการ ขอบคุณ!
aProperFox

33

คุณสามารถทำได้โดยใช้ไลบรารี Common Nodeของฉัน:

function get(url) {
  return new (require('httpclient').HttpClient)({
    method: 'GET',
      url: url
    }).finish().body.read().decodeToString();
}

var a = get('www.example.com/api_1.php'), 
    b = get('www.example.com/api_2.php'),
    c = get('www.example.com/api_3.php');

3
อึฉันโหวตขึ้นคิดว่ามันจะใช้งานได้และมันก็ไม่ :(require(...).HttpClient is not a constructor
moeiscool

31

คำขอซิงค์

สิ่งที่ง่ายที่สุดที่ฉันพบและใช้คือsync-requestและรองรับทั้งโหนดและเบราว์เซอร์!

var request = require('sync-request');
var res = request('GET', 'http://google.com');
console.log(res.body.toString('utf-8'));

นั่นคือไม่มีการกำหนดค่าที่บ้าคลั่งไม่มีการติดตั้ง lib ที่ซับซ้อนแม้ว่าจะมีทางเลือกสำรอง lib ใช้งานได้จริง ฉันได้ลองใช้ตัวอย่างอื่น ๆ ที่นี่แล้วและก็นิ่งงันเมื่อมีการตั้งค่าเพิ่มเติมมากมายที่ต้องทำหรือการติดตั้งไม่ทำงาน!

หมายเหตุ:

ตัวอย่างที่ใช้sync-requestใช้ไม่ได้ผลเมื่อคุณใช้res.getBody()ทั้งหมด get body ทำคือยอมรับการเข้ารหัสและแปลงข้อมูลตอบกลับ เพียงแค่ทำres.body.toString(encoding)แทน


ฉันพบว่าคำขอการซิงค์นั้นช้ามาก .. ฉันลงเอยด้วยการใช้github.com/dhruvbird/http-syncอันอื่นซึ่งเร็วกว่า 10 เท่าในกรณีของฉัน
Filip Spiridonov

ฉันไม่เคยวิ่งช้าเลย สิ่งนี้ทำให้เกิดกระบวนการลูก ระบบของคุณใช้ซีพียูจำนวนเท่าใดและคุณใช้โหนดเวอร์ชันใด ฉันต้องการทราบเพื่อตรวจสอบว่าฉันจำเป็นต้องเปลี่ยนหรือไม่
jemiloii

ฉันเห็นด้วยกับ Filip นี้ช้า
Rambo7

สิ่งเดียวกับที่ฉันถาม flip แต่ไม่มีการตอบสนอง: ระบบของคุณใช้ cpus กี่ตัวและคุณใช้โหนดเวอร์ชันใด
jemiloii

สิ่งนี้ใช้ CPU จำนวนมากไม่แนะนำให้ใช้ในการผลิต
moeiscool

20

ฉันจะใช้ฟังก์ชันเรียกซ้ำกับรายการ apis

var APIs = [ '/api_1.php', '/api_2.php', '/api_3.php' ];
var host = 'www.example.com';

function callAPIs ( host, APIs ) {
  var API = APIs.shift();
  http.get({ host: host, path: API }, function(res) { 
    var body = '';
    res.on('data', function (d) {
      body += d; 
    });
    res.on('end', function () {
      if( APIs.length ) {
        callAPIs ( host, APIs );
      }
    });
  });
}

callAPIs( host, APIs );

แก้ไข: ขอเวอร์ชัน

var request = require('request');
var APIs = [ '/api_1.php', '/api_2.php', '/api_3.php' ];
var host = 'www.example.com';
var APIs = APIs.map(function (api) {
  return 'http://' + host + api;
});

function callAPIs ( host, APIs ) {
  var API = APIs.shift();
  request(API, function(err, res, body) { 
    if( APIs.length ) {
      callAPIs ( host, APIs );
    }
  });
}

callAPIs( host, APIs );

แก้ไข: เวอร์ชันคำขอ / async

var request = require('request');
var async = require('async');
var APIs = [ '/api_1.php', '/api_2.php', '/api_3.php' ];
var host = 'www.example.com';
var APIs = APIs.map(function (api) {
  return 'http://' + host + api;
});

async.eachSeries(function (API, cb) {
  request(API, function (err, res, body) {
    cb(err);
  });
}, function (err) {
  //called when all done, or error occurs
});

นี่เป็นวิธีที่ฉันใช้เนื่องจากฉันมีรายการคำขอที่เปลี่ยนแปลงได้ (600 รายการและเพิ่มขึ้นเรื่อย ๆ ) ที่กล่าวว่ามีปัญหากับรหัสของคุณ: เหตุการณ์ 'ข้อมูล' จะถูกปล่อยออกมาหลายครั้งต่อคำขอหากเอาต์พุต API มีขนาดใหญ่กว่าขนาดชิ้น คุณต้องการ "บัฟเฟอร์" ข้อมูลดังนี้: var body = ''; res.on ('data', function (data) {body + = data;}). on ('end', function () {callback (body); if (APIs.length) callAPI (host, APIs);} );
Ankit Aggarwal

อัปเดตแล้ว ฉันแค่อยากจะแสดงให้เห็นว่าปัญหาสามารถทำให้ง่ายขึ้น / ยืดหยุ่นมากขึ้นได้อย่างไรผ่านการเรียกซ้ำ โดยส่วนตัวแล้วฉันมักจะใช้โมดูลคำขอสำหรับสิ่งนี้เนื่องจากช่วยให้คุณข้ามการโทรกลับหลายครั้งได้อย่างง่ายดาย
generalhenry

@generalhenry ฉันจะทำสิ่งนี้ได้อย่างไรหากต้องการใช้โมดูลคำขอ คุณสามารถเสนอข้อมูลโค้ดที่บรรลุตามข้างต้นโดยใช้คำขอได้หรือไม่?
Scotty

ฉันเพิ่มเวอร์ชันคำขอและเวอร์ชันคำขอ / async
generalhenry

5

ดูเหมือนว่าวิธีแก้ปัญหานี้จะไม่มีวันจบสิ้นนี่คืออีกหนึ่งวิธี :)

// do it once.
sync(fs, 'readFile')

// now use it anywhere in both sync or async ways.
var data = fs.readFile(__filename, 'utf8')

http://alexeypetrushin.github.com/synchronize


แม้ว่าไลบรารีที่คุณเชื่อมโยง DOES จะเสนอวิธีแก้ปัญหาของ OP แต่ในตัวอย่างของคุณ fs.readFile จะซิงค์เสมอ
Eric

1
ไม่คุณสามารถให้การโทรกลับอย่างชัดเจนและใช้เป็นเวอร์ชันอะซิงโครนัสได้หากต้องการ
Alex Craft

1
ตัวอย่างนี้ใช้สำหรับคำร้องขอ http ไม่ใช่การสื่อสารระบบไฟล์
Seth

5

ความเป็นไปได้อีกประการหนึ่งคือการตั้งค่าการโทรกลับที่ติดตามงานที่เสร็จสมบูรณ์:

function onApiResults(requestId, response, results) {
    requestsCompleted |= requestId;

    switch(requestId) {
        case REQUEST_API1:
            ...
            [Call API2]
            break;
        case REQUEST_API2:
            ...
            [Call API3]
            break;
        case REQUEST_API3:
            ...
            break;
    }

    if(requestId == requestsNeeded)
        response.end();
}

จากนั้นเพียงกำหนด ID ให้กับแต่ละคนและคุณสามารถตั้งค่าความต้องการของคุณว่างานใดจะต้องทำให้เสร็จก่อนที่จะปิดการเชื่อมต่อ

const var REQUEST_API1 = 0x01;
const var REQUEST_API2 = 0x02;
const var REQUEST_API3 = 0x03;
const var requestsNeeded = REQUEST_API1 | REQUEST_API2 | REQUEST_API3;

โอเคมันไม่สวย เป็นเพียงอีกวิธีหนึ่งในการโทรตามลำดับ โชคไม่ดีที่ NodeJS ไม่มีการโทรแบบซิงโครนัสพื้นฐานที่สุด แต่ฉันเข้าใจว่าสิ่งล่อใจคืออะไร


4

ใช้ sequenty

sudo npm ติดตั้ง sequenty

หรือ

https://github.com/AndyShin/sequenty

ง่ายมาก.

var sequenty = require('sequenty'); 

function f1(cb) // cb: callback by sequenty
{
  console.log("I'm f1");
  cb(); // please call this after finshed
}

function f2(cb)
{
  console.log("I'm f2");
  cb();
}

sequenty.run([f1, f2]);

นอกจากนี้คุณสามารถใช้ลูปเช่นนี้:

var f = [];
var queries = [ "select .. blah blah", "update blah blah", ...];

for (var i = 0; i < queries.length; i++)
{
  f[i] = function(cb, funcIndex) // sequenty gives you cb and funcIndex
  {
    db.query(queries[funcIndex], function(err, info)
    {
       cb(); // must be called
    });
  }
}

sequenty.run(f); // fire!

4

ในปี 2018 และใช้โมดูล ES6 และสัญญาเราสามารถเขียนฟังก์ชันเช่นนี้ได้:

import { get } from 'http';

export const fetch = (url) => new Promise((resolve, reject) => {
  get(url, (res) => {
    let data = '';
    res.on('end', () => resolve(data));
    res.on('data', (buf) => data += buf.toString());
  })
    .on('error', e => reject(e));
});

จากนั้นในโมดูลอื่น

let data;
data = await fetch('http://www.example.com/api_1.php');
// do something with data...
data = await fetch('http://www.example.com/api_2.php');
// do something with data
data = await fetch('http://www.example.com/api_3.php');
// do something with data

รหัสจะต้องดำเนินการในบริบทแบบอะซิงโครนัส (โดยใช้asyncคำสำคัญ)


3

การใช้ไลบรารีการร้องขอสามารถช่วยลดส่วนที่เหลือให้น้อยที่สุด:

var request = require('request')

request({ uri: 'http://api.com/1' }, function(err, response, body){
    // use body
    request({ uri: 'http://api.com/2' }, function(err, response, body){
        // use body
        request({ uri: 'http://api.com/3' }, function(err, response, body){
            // use body
        })
    })
})

แต่เพื่อความยอดเยี่ยมสูงสุดคุณควรลองใช้ไลบรารีโฟลว์การควบคุมเช่น Step - มันจะช่วยให้คุณสามารถขนานคำขอโดยสมมติว่ายอมรับได้

var request = require('request')
var Step    = require('step')

// request returns body as 3rd argument
// we have to move it so it works with Step :(
request.getBody = function(o, cb){
    request(o, function(err, resp, body){
        cb(err, body)
    })
}

Step(
    function getData(){
        request.getBody({ uri: 'http://api.com/?method=1' }, this.parallel())
        request.getBody({ uri: 'http://api.com/?method=2' }, this.parallel())
        request.getBody({ uri: 'http://api.com/?method=3' }, this.parallel())
    },
    function doStuff(err, r1, r2, r3){
        console.log(r1,r2,r3)
    }
)

2

มีจำนวนมากของห้องสมุดมีการควบคุมการไหล - ฉันชอบconseq ( ... เพราะผมเขียนมัน.) นอกจากนี้ยังon('data')สามารถยิงหลายครั้งเพื่อใช้ REST เสื้อคลุมห้องสมุดเช่นrestler

Seq()
  .seq(function () {
    rest.get('http://www.example.com/api_1.php').on('complete', this.next);
  })
  .seq(function (d1) {
    this.d1 = d1;
    rest.get('http://www.example.com/api_2.php').on('complete', this.next);
  })
  .seq(function (d2) {
    this.d2 = d2;
    rest.get('http://www.example.com/api_3.php').on('complete', this.next);
  })
  .seq(function (d3) {
    // use this.d1, this.d2, d3
  })

2

สิ่งนี้ได้รับการตอบรับอย่างดีจาก Raynos ยังมีการเปลี่ยนแปลงในไลบรารีลำดับเนื่องจากมีการโพสต์คำตอบ

หากต้องการลำดับการทำงานให้ไปที่ลิงค์นี้: https://github.com/FuturesJS/sequence/tree/9daf0000289954b85c0925119821752fbfb3521e https://github.com/FuturesJS/sequence/tree/9daf0000289954b85c0925119821752fbfb3521e

นี่คือวิธีที่คุณจะทำให้มันใช้งานได้หลังจากnpm install sequence:

var seq = require('sequence').Sequence;
var sequence = seq.create();

seq.then(function call 1).then(function call 2);

1

นี่คือ @ andy-shin เวอร์ชันของฉันตามลำดับโดยมีอาร์กิวเมนต์ในอาร์เรย์แทนดัชนี:

function run(funcs, args) {
    var i = 0;
    var recursive = function() {
        funcs[i](function() {
            i++;
            if (i < funcs.length)
                recursive();
        }, args[i]);
    };
    recursive();
}

1

... 4 ปีต่อมา ...

นี่คือโซลูชันดั้งเดิมของเฟรมเวิร์กDanf (คุณไม่จำเป็นต้องใช้รหัสใด ๆ สำหรับสิ่งประเภทนี้มีเพียง config บางส่วนเท่านั้น)

// config/common/config/sequences.js

'use strict';

module.exports = {
    executeMySyncQueries: {
        operations: [
            {
                order: 0,
                service: 'danf:http.router',
                method: 'follow',
                arguments: [
                    'www.example.com/api_1.php',
                    'GET'
                ],
                scope: 'response1'
            },
            {
                order: 1,
                service: 'danf:http.router',
                method: 'follow',
                arguments: [
                    'www.example.com/api_2.php',
                    'GET'
                ],
                scope: 'response2'
            },
            {
                order: 2,
                service: 'danf:http.router',
                method: 'follow',
                arguments: [
                    'www.example.com/api_3.php',
                    'GET'
                ],
                scope: 'response3'
            }
        ]
    }
};

ใช้orderค่าเดียวกันสำหรับการดำเนินการที่คุณต้องการดำเนินการควบคู่กัน

หากคุณต้องการให้สั้นกว่านี้คุณสามารถใช้กระบวนการรวบรวม:

// config/common/config/sequences.js

'use strict';

module.exports = {
    executeMySyncQueries: {
        operations: [
            {
                service: 'danf:http.router',
                method: 'follow',
                // Process the operation on each item
                // of the following collection.
                collection: {
                    // Define the input collection.
                    input: [
                        'www.example.com/api_1.php',
                        'www.example.com/api_2.php',
                        'www.example.com/api_3.php'
                    ],
                    // Define the async method used.
                    // You can specify any collection method
                    // of the async lib.
                    // '--' is a shorcut for 'forEachOfSeries'
                    // which is an execution in series.
                    method: '--'
                },
                arguments: [
                    // Resolve reference '@@.@@' in the context
                    // of the input item.
                    '@@.@@',
                    'GET'
                ],
                // Set the responses in the property 'responses'
                // of the stream.
                scope: 'responses'
            }
        ]
    }
};

ดูภาพรวมของกรอบสำหรับข้อมูลเพิ่มเติม


1

ฉันมาที่นี่เพราะฉันต้องการ จำกัด อัตรา http.request (คำค้นหาการรวมประมาณ 10k เพื่อการค้นหาแบบยืดหยุ่นเพื่อสร้างรายงานการวิเคราะห์) ต่อไปนี้ทำให้เครื่องของฉันสำลัก

for (item in set) {
    http.request(... + item + ...);
}

URL ของฉันง่ายมากดังนั้นสิ่งนี้อาจไม่เกี่ยวข้องกับคำถามเดิม แต่ฉันคิดว่าทั้งสองอย่างอาจใช้ได้และคุ้มค่าที่จะเขียนที่นี่สำหรับผู้อ่านที่มาที่นี่พร้อมกับปัญหาที่คล้ายกับของฉันและผู้ที่ต้องการโซลูชัน JavaScript ที่ไม่มีไลบรารี

งานของฉันไม่ได้ขึ้นอยู่กับลำดับและวิธีแรกของฉันในการซ่อนสิ่งนี้คือการรวมไว้ในเชลล์สคริปต์เพื่อแยกชิ้นส่วน (เพราะฉันยังใหม่กับ JavaScript) นั่นใช้งานได้ แต่ไม่น่าพอใจ ความละเอียด JavaScript ของฉันในท้ายที่สุดคือทำสิ่งต่อไปนี้:

var stack=[];
stack.push('BOTTOM');

function get_top() {
  var top = stack.pop();
  if (top != 'BOTTOM')
    collect(top);
}

function collect(item) {
    http.request( ... + item + ...
    result.on('end', function() {
      ...
      get_top();
    });
    );
}

for (item in set) {
   stack.push(item);
}

get_top();

ดูเหมือนว่าการเรียกซ้ำกันระหว่างการเก็บรวบรวมและget_top ฉันไม่แน่ใจว่ามีผลเนื่องจากระบบเป็นแบบอะซิงโครนัสและฟังก์ชันรวบรวมเสร็จสมบูรณ์โดยมีการเรียกกลับที่ซ่อนไว้สำหรับเหตุการณ์เมื่อเปิด ('end''ปลาย'

ฉันคิดว่ามันเป็นเรื่องทั่วไปเพียงพอที่จะใช้กับคำถามเดิม ถ้าเช่นเดียวกับสถานการณ์ของฉันลำดับ / ชุดเป็นที่รู้จัก URL / คีย์ทั้งหมดสามารถผลักดันบนสแต็กได้ในขั้นตอนเดียว หากมีการคำนวณในขณะที่คุณไปฟังก์ชันon ('end'สามารถพุช url ถัดไปบนสแต็กก่อนget_top ()หากมีสิ่งใดผลลัพธ์จะมีการซ้อนน้อยลงและอาจจะง่ายต่อการ refactor เมื่อ API ที่คุณเรียกใช้ การเปลี่ยนแปลง

ฉันรู้ว่าสิ่งนี้มีประสิทธิภาพเทียบเท่ากับเวอร์ชันเรียกซ้ำง่าย ๆ ของ @ generalhenry ด้านบน (ดังนั้นฉันจึงโหวตให้!)


0

คำขอขั้นสูง

นี่คือโมดูลซิงโครนัสอื่นที่อิงตามคำขอและใช้คำสัญญา ใช้งานง่ายสุด ๆ ใช้ได้ดีกับการทดสอบมอคค่า

npm install super-request

request("http://domain.com")
    .post("/login")
    .form({username: "username", password: "password"})
    .expect(200)
    .expect({loggedIn: true})
    .end() //this request is done 
    //now start a new one in the same session 
    .get("/some/protected/route")
    .expect(200, {hello: "world"})
    .end(function(err){
        if(err){
            throw err;
        }
    });

0

รหัสนี้สามารถใช้เพื่อดำเนินการอาร์เรย์ของสัญญาแบบซิงโครนัส & ตามลำดับหลังจากนั้นคุณสามารถรันโค้ดสุดท้ายของคุณในการ.then()โทร

const allTasks = [() => promise1, () => promise2, () => promise3];

function executePromisesSync(tasks) {
  return tasks.reduce((task, nextTask) => task.then(nextTask), Promise.resolve());
}

executePromisesSync(allTasks).then(
  result => console.log(result),
  error => console.error(error)
);

0

ฉันได้สิ่งที่คุณ (และฉัน) ต้องการจริงๆโดยไม่ต้องใช้การรอคอยสัญญาหรือการรวมไลบรารีใด ๆ (ภายนอก) (ยกเว้นของเราเอง)

วิธีการทำมีดังนี้

เรากำลังจะสร้างโมดูล C ++ เพื่อใช้งานร่วมกับ node.js และฟังก์ชันโมดูล C ++ นั้นจะทำการร้องขอ HTTP และส่งคืนข้อมูลเป็นสตริงและคุณสามารถใช้สิ่งนั้นได้โดยตรงโดยทำ:

var myData = newModule.get(url);

คุณพร้อมที่จะเริ่มต้นแล้วหรือยัง?

ขั้นตอนที่ 1: สร้างโฟลเดอร์ใหม่ที่อื่นในคอมพิวเตอร์ของคุณเราใช้โฟลเดอร์นี้เพื่อสร้างไฟล์ module.node เท่านั้น (คอมไพล์จาก C ++) คุณสามารถย้ายได้ในภายหลัง

ในโฟลเดอร์ใหม่ (ฉันใส่ของฉันไว้ใน mynewFolder / src เพื่อจัดระเบียบ - เนส):

npm init

แล้ว

npm install node-gyp -g

ตอนนี้สร้างไฟล์ใหม่ 2 ไฟล์: 1 เรียกว่า something.cpp และสำหรับใส่รหัสนี้ (หรือแก้ไขถ้าคุณต้องการ):

#pragma comment(lib, "urlmon.lib")
#include <sstream>
#include <WTypes.h>  
#include <node.h>
#include <urlmon.h> 
#include <iostream>
using namespace std;
using namespace v8;

Local<Value> S(const char* inp, Isolate* is) {
    return String::NewFromUtf8(
        is,
        inp,
        NewStringType::kNormal
    ).ToLocalChecked();
}

Local<Value> N(double inp, Isolate* is) {
    return Number::New(
        is,
        inp
    );
}

const char* stdStr(Local<Value> str, Isolate* is) {
    String::Utf8Value val(is, str);
    return *val;
}

double num(Local<Value> inp) {
    return inp.As<Number>()->Value();
}

Local<Value> str(Local<Value> inp) {
    return inp.As<String>();
}

Local<Value> get(const char* url, Isolate* is) {
    IStream* stream;
    HRESULT res = URLOpenBlockingStream(0, url, &stream, 0, 0);

    char buffer[100];
    unsigned long bytesReadSoFar;
    stringstream ss;
    stream->Read(buffer, 100, &bytesReadSoFar);
    while(bytesReadSoFar > 0U) {
        ss.write(buffer, (long long) bytesReadSoFar);
        stream->Read(buffer, 100, &bytesReadSoFar);
    }
    stream->Release();
    const string tmp = ss.str();
    const char* cstr = tmp.c_str();
    return S(cstr, is);
}

void Hello(const FunctionCallbackInfo<Value>& arguments) {
    cout << "Yo there!!" << endl;

    Isolate* is = arguments.GetIsolate();
    Local<Context> ctx = is->GetCurrentContext();

    const char* url = stdStr(arguments[0], is);
    Local<Value> pg = get(url,is);

    Local<Object> obj = Object::New(is);
    obj->Set(ctx,
        S("result",is),
        pg
    );
    arguments.GetReturnValue().Set(
       obj
    );

}

void Init(Local<Object> exports) {
    NODE_SET_METHOD(exports, "get", Hello);
}

NODE_MODULE(cobypp, Init);

ตอนนี้สร้างไฟล์ใหม่ในไดเร็กทอรีเดียวกันที่เรียกว่าsomething.gypและใส่ (อะไรทำนองนี้) ไว้ในนั้น:

{
   "targets": [
       {
           "target_name": "cobypp",
           "sources": [ "src/cobypp.cpp" ]
       }
   ]
}

ตอนนี้ในไฟล์ package.json ให้เพิ่ม: "gypfile": true,

ตอนนี้: ในคอนโซล node-gyp rebuild

หากผ่านคำสั่งทั้งหมดและพูดว่า "ok" ในตอนท้ายโดยไม่มีข้อผิดพลาดแสดงว่าคุณ (เกือบ) ดีไปแล้วถ้าไม่ใช่ก็แสดงความคิดเห็น ..

แต่ถ้ามันใช้งานได้ให้ไปที่ build / Release / cobypp.node (หรืออะไรก็ได้ที่เรียกร้องให้คุณ) คัดลอกลงในโฟลเดอร์ node.js หลักจากนั้นใน node.js:

var myCPP = require("./cobypp")
var myData = myCPP.get("http://google.com").result;
console.log(myData);

..

response.end(myData);//or whatever
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.